[pkg-java] r2880 - in branches/mysql-connector-java: . upstream upstream/5.0.4 upstream/5.0.4/docs upstream/5.0.4/src upstream/5.0.4/src/com upstream/5.0.4/src/com/mysql upstream/5.0.4/src/com/mysql/jdbc upstream/5.0.4/src/com/mysql/jdbc/configs upstream/5.0.4/src/com/mysql/jdbc/exceptions upstream/5.0.4/src/com/mysql/jdbc/integration upstream/5.0.4/src/com/mysql/jdbc/integration/c3p0 upstream/5.0.4/src/com/mysql/jdbc/integration/jboss upstream/5.0.4/src/com/mysql/jdbc/jdbc2 upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional upstream/5.0.4/src/com/mysql/jdbc/log upstream/5.0.4/src/com/mysql/jdbc/profiler upstream/5.0.4/src/com/mysql/jdbc/trace upstream/5.0.4/src/com/mysql/jdbc/util upstream/5.0.4/src/doc upstream/5.0.4/src/doc/sources upstream/5.0.4/src/lib upstream/5.0.4/src/org upstream/5.0.4/src/org/gjt upstream/5.0.4/src/org/gjt/mm upstream/5.0.4/src/org/gjt/mm/mysql upstream/5.0.4/src/testsuite upstream/5.0.4/src/testsuite/perf upstream/5.0.4/src/testsuite/regression upstream/5.0.4/src/testsuite/simple

Marcus Better marcusb-guest at alioth.debian.org
Fri Dec 22 11:43:22 CET 2006


Author: marcusb-guest
Date: 2006-12-22 11:43:08 +0100 (Fri, 22 Dec 2006)
New Revision: 2880

Added:
   branches/mysql-connector-java/upstream/
   branches/mysql-connector-java/upstream/5.0.4/
   branches/mysql-connector-java/upstream/5.0.4/CHANGES
   branches/mysql-connector-java/upstream/5.0.4/COPYING
   branches/mysql-connector-java/upstream/5.0.4/EXCEPTIONS-CONNECTOR-J
   branches/mysql-connector-java/upstream/5.0.4/README
   branches/mysql-connector-java/upstream/5.0.4/README.txt
   branches/mysql-connector-java/upstream/5.0.4/build.xml
   branches/mysql-connector-java/upstream/5.0.4/debug/
   branches/mysql-connector-java/upstream/5.0.4/docs/
   branches/mysql-connector-java/upstream/5.0.4/docs/README
   branches/mysql-connector-java/upstream/5.0.4/docs/README.txt
   branches/mysql-connector-java/upstream/5.0.4/docs/connector-j.html
   branches/mysql-connector-java/upstream/5.0.4/docs/connector-j.pdf
   branches/mysql-connector-java/upstream/5.0.4/docs/release-test-output/
   branches/mysql-connector-java/upstream/5.0.4/src/
   branches/mysql-connector-java/upstream/5.0.4/src/com/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/AssertionFailedException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Blob.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/BlobFromLocator.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Buffer.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CallableStatement.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CharsetMapping.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Charsets.properties
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Clob.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CommunicationsException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CompressedInputStream.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Connection.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionProperties.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionPropertiesTransform.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Constants.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CursorRowProvider.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DatabaseMetaData.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DocsConnectionPropsHelper.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Driver.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeProcessor.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeProcessorResult.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeTokenizer.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ExportControlled.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Field.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/LicenseConfiguration.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/LocalizedErrorMessages.properties
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Messages.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MiniAdmin.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlDataTruncation.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlDefs.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlErrorNumbers.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlIO.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlParameterMetadata.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlSavepoint.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NamedPipeSocketFactory.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NonRegisteringDriver.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NotImplemented.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NotUpdatable.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/OperationNotSupportedException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/OutputStreamWatcher.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/PacketTooBigException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/PreparedStatement.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ReplicationConnection.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ReplicationDriver.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ResultSet.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ResultSetMetaData.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowData.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowDataDynamic.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowDataStatic.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SQLError.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Security.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ServerPreparedStatement.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SingleByteCharsetConverter.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SocketFactory.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/StandardSocketFactory.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Statement.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/StringUtils.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/TimeUtil.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/UpdatableResultSet.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Util.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WatchableOutputStream.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WatchableWriter.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WriterWatcher.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/3-0-Compat.properties
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/clusterBase.properties
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/fullDebug.properties
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/maxPerformance.properties
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLDataException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransientException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/c3p0/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/CommonsLogger.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Jdk14Logger.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Log.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Log4JLogger.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/LogFactory.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/LogUtils.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/NullLogger.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/StandardLogger.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/ProfileEventSink.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/ProfilerEvent.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/trace/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/trace/Tracer.aj
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/BaseBugReport.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/LRUCache.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/PropertiesDocGenerator.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ReadAheadInputStream.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ResultSetUtil.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ServerController.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/TimezoneDump.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java
   branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/webapp/
   branches/mysql-connector-java/upstream/5.0.4/src/doc/
   branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/
   branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/connPropsToDocbook.xsl
   branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/errorMapToDocbook.xsl
   branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/placeholder.txt
   branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/pom.xml
   branches/mysql-connector-java/upstream/5.0.4/src/lib/
   branches/mysql-connector-java/upstream/5.0.4/src/lib/Commons-Logging-LICENSE.txt
   branches/mysql-connector-java/upstream/5.0.4/src/lib/LICENSE-AspectJ.html
   branches/mysql-connector-java/upstream/5.0.4/src/lib/c3p0-LICENSE
   branches/mysql-connector-java/upstream/5.0.4/src/lib/jboss-lgpl.txt
   branches/mysql-connector-java/upstream/5.0.4/src/org/
   branches/mysql-connector-java/upstream/5.0.4/src/org/gjt/
   branches/mysql-connector-java/upstream/5.0.4/src/org/gjt/mm/
   branches/mysql-connector-java/upstream/5.0.4/src/org/gjt/mm/mysql/
   branches/mysql-connector-java/upstream/5.0.4/src/org/gjt/mm/mysql/Driver.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/BaseTestCase.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/BasePerfTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/LoadStorePerfTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/RetrievalPerfTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/AppletRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/BlobRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/CachedRowsetTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/CallableStatementRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/ConnectionRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/DataSourceRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/EscapeProcessorRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/MetaDataRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/MicroPerformanceRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/NumbersRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/PooledConnectionRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/ResultSetRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StatementRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StressRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StringRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/SubqueriesRegressionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/BlobTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/CallableStatementTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/CharsetTests.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/ConnectionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/DataSourceTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/DateTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/EscapeProcessingTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/MetadataTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/MiniAdminTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/NumbersTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/SSLTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/ServerControllerTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/SimpleTransformer.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/StatementsTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/TransactionTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/TraversalTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/UpdatabilityTest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/XATest.java
   branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/tb2-data.txt.gz
Log:
Import upstream sources.


Added: branches/mysql-connector-java/upstream/5.0.4/CHANGES
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/CHANGES	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/CHANGES	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,3001 @@
+# Changelog
+# $Id: CHANGES 5908 2006-10-19 15:47:48Z mmatthews $
+
+10-20-06 - Version 5.0.4
+
+    - 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 
+	  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 
+	  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 
+	  statement), addresses BUG#22297.
+	  
+	- 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 
+	  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 
+      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 
+      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 
+	  workaround for transaction managers that don't 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 
+	  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 
+      "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 
+      "{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 
+      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 
+      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), 
+      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. 
+    
+    - 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 
+      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. 
+      
+      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 
+      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
+
+	- Fixed BUG#23281 - Downed slave caused round-robin load balance to
+	  not cycle back to first host in list.
+	  
+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 
+	  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, 
+	  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 
+	  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 
+	  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 
+      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 
+      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 
+      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.  
+    
+    - Fixed BUG#16169 - ResultSet.getNativeShort() causes stack overflow error
+      via recurisve calls.
+      
+    - 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 
+      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() 
+      check).
+      
+    - 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, 
+      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 
+      "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 
+      "logger=com.mysql.jdbc.log.CommonsLogger".
+      
+    - Fixed BUG#15570 - ReplicationConnection incorrectly copies state,
+	  doesn't transfer connection context correctly when transitioning between 
+	  the same read-only states.
+	  	  
+	- 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 
+	  only). Enable by using "rewriteBatchedStatements=true" in your JDBC URL.
+	  
+	- 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() 
+	  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 
+	  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 
+	  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 
+	  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 
+	  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 
+	  number 13601).
+	  
+	- 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 
+      two-argument version of this method.
+      
+    - 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 
+      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 
+                
+        * 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 
+ 
+    - Fixed BUG#14815 - DatabaseMetaData.getColumns() doesn't
+      return TABLE_NAME correctly.
+      
+    - 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 
+      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 
+	  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 
+	  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 
+	  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 
+	  the JDBC URL configuration property "processEscapeCodesForPrepStmts"
+	  to "false".
+	  
+	- 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 
+      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 
+      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 
+      "Catalog can't be null" exception.
+      
+    - Fixed BUG#12218, properties shared between master and slave with 
+      replication connection.
+      
+    - 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 
+      statements.
+      
+    - 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 
+	  "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 
+	  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. 
+	  (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 
+	  getResultSetHoldability() to return HOLD_CURSORS_OVER_COMMIT.
+	  
+	- Fixed BUG#12541 - Handling of catalog argument in 
+	  DatabaseMetaData.getIndexInfo(), which also means changes to the following
+	  methods in DatabaseMetaData:
+	  
+	    - getBestRowIdentifier()
+	    - getColumns()
+	    - getCrossReference()
+	    - getExportedKeys()
+	    - getImportedKeys()
+	    - getIndexInfo()
+	    - 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 
+	  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 
+      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 
+      case-sensitive (on Windows systems).
+      
+    - 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(). 
+	  
+	- 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 
+      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 
+	  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 
+	  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 
+	  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 
+	  does unnecessary call to toCharArray().
+	  
+	- 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 
+	  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 
+	  client-side prepared statements.
+	  
+	- 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) 
+	  with ResultSet.getString(). The driver will now always treat YEAR types
+	  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 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", 
+	  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 
+	  ResultSet.getObject(..), and ResultSetMetaData.getColumnClassName()
+	  will return "java.lang.Boolean".
+	 
+	- 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 
+	  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 
+	  "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 
+	  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 
+	  classes.
+	  
+	- 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 
+	  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 
+      'sessionVariables'.
+
+	- Fixed regression in ping() for users using autoReconnect=true.
+	
+	- 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 
+      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' 
+	  by default, but will most likely default to 'false' in 3.2).
+	  
+	- 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 
+	  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 
+	  '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 
+	  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 
+      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 
+	  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 
+	  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 
+	  various scalar and aggregate functions that return strings.
+	  
+	- 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 "%"), 
+	  even though it isn't JDBC compliant, for legacy's sake. Disable by setting
+	  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
+
+
+    - Fixed BUG#7686, Timestamp key column data needed "_binary'"
+      stripped for UpdatableResultSet.refreshRow().
+
+    - 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 
+      integer) and adjust quoting method for strings appropriately.
+
+    - Added 'holdResultsOpenOverStatementClose' property (default is
+      false), that keeps result sets open over statement.close() or new
+      execution on same statement (suggested by Kevin Burton).
+
+    - Fixed BUG#7952 -- Infinite recursion when 'falling back' to master
+      in failover configuration.
+
+    - Disable multi-statements (if enabled) for MySQL-4.1 versions prior
+      to version 4.1.10 if the query cache is enabled, as the server
+      returns wrong results in this configuration.
+
+    - Fixed duplicated code in configureClientCharset() that prevented
+      useOldUTF8Behavior=true from working properly.
+
+    - Removed 'dontUnpackBinaryResults' functionality, the driver now
+      always stores results from server-side prepared statements as-is
+      from the server and unpacks them on demand.
+
+    - Fixed BUG#8096 where emulated locators corrupt binary data
+      when using server-side prepared statements.
+
+    - Fixed synchronization issue with
+      ServerPreparedStatement.serverPrepare() that could cause
+      deadlocks/crashes if connection was shared between threads.
+
+    - By default, the driver now scans SQL you are preparing via all
+      variants of Connection.prepareStatement() to determine if it is a
+      supported type of statement to prepare on the server side, and if
+      it is not supported by the server, it instead prepares it as a
+      client-side emulated prepared statement (BUG#4718). You can
+      disable this by passing 'emulateUnsupportedPstmts=false' in your
+      JDBC URL.
+
+    - Remove _binary introducer from parameters used as in/out
+      parameters in CallableStatement.
+
+    - Always return byte[]s for output parameters registered as *BINARY.
+
+    - Send correct value for 'boolean' "true" to server for
+      PreparedStatement.setObject(n, "true", Types.BIT).
+
+    - Fixed bug with Connection not caching statements from
+      prepareStatement() when the statement wasn't a server-side
+      prepared statement.
+
+    - Choose correct 'direction' to apply time adjustments when both
+      client and server are in GMT timezone when using
+      ResultSet.get(..., cal) and PreparedStatement.set(...., cal).
+
+    - Added 'dontTrackOpenResources' option (default is false, to be
+      JDBC compliant), which helps with memory use for non-well-behaved
+      apps (i.e applications which don't close Statements when they
+      should).
+
+    - Fixed BUG#8428 - ResultSet.getString() doesn't maintain format
+      stored on server, bug fix only enabled when 'noDatetimeStringSync'
+      property is set to 'true' (the default is 'false').
+
+    - Fixed NPE in ResultSet.realClose() when using usage advisor and
+      result set was already closed.
+
+    - Fixed BUG#8487 - PreparedStatements not creating streaming result
+      sets.
+
+    - Don't pass NULL to String.valueOf() in
+      ResultSet.getNativeConvertToString(), as it stringifies it (i.e.
+      returns "null"), which is not correct for the method in question.
+
+    - Fixed BUG#8484 - ResultSet.getBigDecimal() throws exception
+      when rounding would need to occur to set scale. The driver now
+      chooses a rounding mode of 'half up' if non-rounding
+      BigDecimal.setScale() fails.
+
+    - Added 'useLocalSessionState' configuration property, when set to
+      'true' the JDBC driver trusts that the application is well-behaved
+      and only sets autocommit and transaction isolation levels using
+      the methods provided on java.sql.Connection, and therefore can
+      manipulate these values in many cases without incurring
+      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) 
+      to disable the streaming results for that statement.
+
+    - Added support for BIT type in MySQL-5.0.3. The driver will treat
+      BIT(1-8) as the JDBC standard BIT type (which maps to
+      java.lang.Boolean), as the server does not currently send enough
+      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, 
+      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(), 
+	  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 
+      '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 
+      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 
+      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
+      'padding' zeros).
+
+    - 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 
+      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 
+      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 
+      socket.connect().
+      
+09-04-04 - Version 3.1.4-beta
+
+    - 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() 
+      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 
+      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 
+      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 
+	  DECIMAL type don't work.
+	  
+	- 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, 
+      you must set the 'allowUrlInLocalInfile' connection property to 'true'.
+      
+    - 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 
+      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 
+      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 
+      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 
+	  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' 
+	  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 
+	  '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 
+	  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 
+	  info).
+	  
+	- ServerPreparedStatements weren't actually de-allocating
+	  server-side resources when .close() was called.
+	  
+	- 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. 
+	
+	- Support placeholder for parameter metadata for server >= 4.1.2
+	
+	- Fix for BUG#3539 getProcedures() does not return any procedures in 
+	  result set
+	
+	- 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 
+	  '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 
+	  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 
+	  UpdatableResultSet.
+	
+	- Removed useFastDates connection property. 
+	
+	- Correctly initialize datasource properties from JNDI Refs, including 
+	  explicitly specified URLs.
+	  
+	- 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). 
+	  
+	- 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 
+      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 
+      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 
+      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 
+      server.
+      
+    - Merged prepared statement caching, and .getMetaData() support
+      from 3.0 branch.
+    
+    - 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 
+      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 
+      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 
+      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' 
+      when useTimeZone='true' and server timezone differs from client timezone.
+	
+	- 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 
+	  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 
+	  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 
+      '+', making number unparseable by MySQL server.
+
+    - 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 
+      '+', 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 
+      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 
+      inverted value.
+      
+    - 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.
+
+11-15-04 - Version 3.0.16-ga
+
+	- 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 
+	  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 
+	  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 
+	  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 
+	  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 
+	  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 
+	  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 
+	  MysqlConnectionPoolDataSource.
+	  
+	- 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 
+      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
+
+	- 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 
+	  http://developer.java.sun.com/developer/bugParade/bugs/4401235.html)
+	  
+	- 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() 
+	  (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 
+      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 
+      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 
+      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 
+      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 
+      Access to DataSource', as well as retrieval of message from server.
+      
+    - 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 
+      DBMD.extractForeignKeyFromCreateTable().
+      
+    - Fix for BUG#1914 -- Support escape sequence {fn convert ... }
+    
+    - 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 
+      (JDBC-compliance).
+      
+    - Removed static synchronization bottleneck from 
+      PreparedStatement.setTimestamp().
+      
+    - Removed static synchronization bottleneck from instance factory 
+      method of SingleByteCharsetConverter.
+      
+    - 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 
+      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 
+	  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, 
+	  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 default column values when moveToInsertRow() is called
+	  (BUG#688)
+	
+	- 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 
+	  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 
+	  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. 
+	
+	- 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'). 
+	  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 
+	  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' 
+	  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 
+      Driver.acceptsUrl().
+      
+    - 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 
+      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 
+      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 
+      DBMD.nullsAreSortedAtStart (4.0.2 to 4.0.10, true, otherwise,
+      no).
+      
+    - 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 
+      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 
+      Connection.setCatalog.
+      
+    - Added support for quoted identifiers in PreparedStatement
+      parser.
+      
+    - Streamlined character conversion and byte[] handling in 
+      PreparedStatements for setByte().
+      
+    - 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. 
+    
+    - 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 
+      per table.
+      
+    - Fixed ResultSetMetaData.getPrecision() returning incorrect values 
+      for some floating point types.
+      
+    - 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 
+      (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 
+      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 
+      (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' 
+      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 
+      working
+      
+    - Fixed ResultSet.setFetchDirection(FETCH_UNKNOWN)
+    
+    - 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 
+      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 
+      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 
+      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 
+      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 
+      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 
+      '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(), 
+      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 
+      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 
+      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 
+      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 
+      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. 
+      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. 
+    
+    - 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 
+      if not updated).
+      
+    - 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 
+      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 
+       MysqlIO (bug 507456).
+       
+     - 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 
+       (bug 487669).
+       
+     - 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 
+       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() 
+       (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 '||' 
+       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 
+       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 
+       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 
+       Dmitry Vereshchagin)
+       
+     - 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
+
+     - Fixed ResultSet.getBlob() ArrayIndex out-of-bounds
+
+     - Fixed ResultSetMetaData.getColumnTypeName for TEXT/BLOB
+
+     - Fixed ArrayIndexOutOfBounds when sending large BLOB queries 
+       (Max size packet was not being set)
+
+     - Added ISOLATION level support to Connection.setIsolationLevel()
+
+     - Fixed NPE on PreparedStatement.executeUpdate() when all columns
+       have not been set.
+
+     - Fixed data parsing of TIMESTAMPs with 2-digit years
+
+     - Added Byte to PreparedStatement.setObject()
+
+     - ResultSet.getBoolean() now recognizes '-1' as 'true'
+
+     - ResultSet has +/-Inf/inf support
+
+     - ResultSet.insertRow() works now, even if not all columns are
+       set (they will be set to "NULL")
+
+     - DataBaseMetaData.getCrossReference() no longer ArrayIndexOOB
+
+     - getObject() on ResultSet correctly does TINYINT->Byte and
+       SMALLINT->Short
+
+12-03-00 - Version 2.0.3
+
+     - Implemented getBigDecimal() without scale component
+       for JDBC2.
+
+     - Fixed composite key problem with updateable result sets.
+
+     - Added detection of -/+INF for doubles.
+
+     - Faster ASCII string operations.
+
+     - Fixed incorrect detection of MAX_ALLOWED_PACKET, so sending
+       large blobs should work now.
+
+     - Fixed off-by-one error in java.sql.Blob implementation code.
+
+     - 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. 
+       Thanks to Moritz Maass.
+
+     - Cleaned up exception handling when driver connects
+
+     - Columns that are of type TEXT now return as Strings
+       when you use getObject()
+
+     - DatabaseMetaData.getPrimaryKeys() now works correctly wrt
+       to key_seq. Thanks to Brian Slesinsky.
+
+     - No escape processing is done on PreparedStatements anymore
+       per JDBC spec.
+
+     - Fixed many JDBC-2.0 traversal, positioning bugs, especially
+       wrt to empty result sets. Thanks to Ron Smits, Nick Brook,
+       Cessar Garcia and Carlos Martinez.
+
+     - Fixed some issues with updatability support in ResultSet when
+       using multiple primary keys.
+
+02-21-00 - Version 2.0pre5
+
+     - Fixed Bad Handshake problem.
+
+01-10-00 - Version 2.0pre4
+
+     - Fixes to ResultSet for insertRow() - Thanks to
+       Cesar Garcia
+
+     - Fix to Driver to recognize JDBC-2.0 by loading a JDBC-2.0
+       class, instead of relying on JDK version numbers. Thanks
+       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 
+
+     - Statement/PreparedStatement.getMoreResults() bug fixed. 
+       Thanks to Noel J. Bergman.
+
+     - Added Short as a type to PreparedStatement.setObject().
+       Thanks to Jeff Crowder
+
+     - Driver now automagically configures maximum/preferred packet
+       sizes by querying server.
+
+     - Autoreconnect code uses fast ping command if server supports
+       it.
+
+     - Fixed various bugs wrt. to packet sizing when reading from
+       the server and when alloc'ing to write to the server.
+
+08-17-99 - Version 2.0pre
+
+     - Now compiles under JDK-1.2. The driver supports both JDK-1.1
+       and JDK-1.2 at the same time through a core set of classes.
+       The driver will load the appropriate interface classes at
+       runtime by figuring out which JVM version you are using.
+
+     - Fixes for result sets with all nulls in the first row.
+       (Pointed out by Tim Endres)
+
+     - Fixes to column numbers in SQLExceptions in ResultSet
+       (Thanks to Blas Rodriguez Somoza)
+
+     - The database no longer needs to specified to connect.
+       (Thanks to Christian Motschke)
+
+07-04-99 - Version 1.2b
+
+     - Better Documentation (in progress), in doc/mm.doc/book1.html
+
+     - 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. 
+       (contributed by Alan Wilken)
+
+     - EscapeProcessor now handles \{ \} and { or } inside quotes
+       correctly. (thanks to Alik for some ideas on how to fix it)
+
+     - Fixes to properties handling in Connection.
+       (contributed by Juho Tikkala)
+
+     - ResultSet.getObject() now returns null for NULL columns
+       in the table, rather than bombing out.
+       (thanks to Ben Grosman)
+
+     - ResultSet.getObject() now returns Strings for types
+       from MySQL that it doesn't know about. (Suggested by
+       Chris Perdue)
+
+     - Removed DataInput/Output streams, not needed, 1/2 number
+       of method calls per IO operation.
+
+     - Use default character encoding if one is not specified. This
+       is a work-around for broken JVMs, because according to spec,
+       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 
+       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)
+
+     - Changed MysqlIO to re-use packets where possible to reduce
+       memory usage.
+
+     - Fixed escape-processor bugs pertaining to {} inside
+       quotes.
+
+04-14-99 - Version 1.2a
+
+     - Fixed character-set support for non-Javasoft JVMs
+       (thanks to many people for pointing it out)
+
+     - Fixed ResultSet.getBoolean() to recognize 'y' & 'n'
+       as well as '1' & '0' as boolean flags.
+       (thanks to Tim Pizey)
+
+     - Fixed ResultSet.getTimestamp() to give better performance.
+       (thanks to Richard Swift)
+
+     - Fixed getByte() for numeric types.
+       (thanks to Ray Bellis)
+
+     - Fixed DatabaseMetaData.getTypeInfo() for DATE type.
+       (thanks to Paul Johnston)
+
+     - Fixed EscapeProcessor for "fn" calls.
+       (thanks to Piyush Shah at locomotive.org)
+
+     - Fixed EscapeProcessor to not do extraneous work if there
+       are no escape codes.
+       (thanks to Ryan Gustafson)
+
+     - Fixed Driver to parse URLs of the form "jdbc:mysql://host:port"
+       (thanks to Richard Lobb)
+
+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!)
+
+03-08-99 - Version 1.1h
+
+     - 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
+       Unicode to work, but your JVM must be able to convert
+       between the character sets. This should also make reading
+       data from the server be a bit quicker, because there is now
+       no conversion from StringBuffer to String.
+
+     - Changed PreparedStatement.streamToString() to be more
+       efficient (code from Uwe Schaefer).
+
+     - URL parsing is more robust (throws SQL exceptions on errors
+       rather than NullPointerExceptions)
+
+     - PreparedStatement now can convert Strings to Time/Date values
+       via setObject() (code from Robert Currey).
+
+     - IO no longer hangs in Buffer.readInt(), that bug was
+       introduced in 1.1d when changing to all byte-arrays for
+       result sets. (Pointed out by Samo Login)
+
+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 
+       (thanks to Barry Lagerweij)
+
+     - Beginning of code cleanup, and formatting. Getting ready
+       to branch this off to a parallel JDBC-2.0 source tree.
+
+     - Added "final" modifier to critical sections in MysqlIO and
+       Buffer to allow compiler to inline methods for speed.
+
+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 
+       serialized  representation of the object to the database for
+       objects of Types.OTHER and objects of unknown type.
+
+     - Util now has a static method readObject() which given a ResultSet
+       and a column index will re-instantiate an object serialized in
+       the above manner.
+
+9-02-98 - Vesion 1.1
+
+     - 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.
+
+     - Statement.setMaxRows() works, as well as setting
+       by property maxRows. Statement.setMaxRows() overrides
+       maxRows set via properties or url parameters.
+
+     - Automatic re-connection is available. Because it has
+       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 
+       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 
+
+     - 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 - 
+
+     - 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 
+       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(). 
+       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(), 
+       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
+       so often to save memory.
+
+7-06-98 - Version 0.8
+
+     - 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 
+       applets and all classloaders.
+
+       Thanks to Joe Ennis <jce at mail.boone.com> for pointing
+       out the problem and working on a fix with me.
+
+7-01-98 - Version 0.7
+
+     - Fixed DatabaseMetadata problems in getColumns() and
+       bug in switch statement in the Field constructor.
+
+       Thanks to Costin Manolache <costin at tdiinc.com> for 
+       pointing these out.
+
+5-21-98 - Version 0.6
+
+     - 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() 
+        * getTableTypes()
+        * getColumns
+        * getCatalogs()

Added: branches/mysql-connector-java/upstream/5.0.4/COPYING
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/COPYING	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/COPYING	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,343 @@
+GNU General Public License
+**************************
+
+     	    GNU GENERAL PUBLIC LICENSE
+     	       Version 2, June 1991
+     
+     Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     		  675 Mass Ave, Cambridge, MA 02139, USA
+     Everyone is permitted to copy and distribute verbatim copies
+     of this license document, but changing it is not allowed.
+     
+     		    Preamble
+     
+     The licenses for most software are designed to take away your
+     freedom to share and change it.  By contrast, the GNU General Public
+     License is intended to guarantee your freedom to share and change free
+     software--to make sure the software is free for all its users.  This
+     General Public License applies to most of the Free Software
+     Foundation's software and to any other program whose authors commit to
+     using it.  (Some other Free Software Foundation software is covered by
+     the GNU Library General Public License instead.)  You can apply it to
+     your programs, too.
+     
+     When we speak of free software, we are referring to freedom, not
+     price.  Our General Public Licenses are designed to make sure that you
+     have the freedom to distribute copies of free software (and charge for
+     this service if you wish), that you receive source code or can get it
+     if you want it, that you can change the software or use pieces of it
+     in new free programs; and that you know you can do these things.
+     
+     To protect your rights, we need to make restrictions that forbid
+     anyone to deny you these rights or to ask you to surrender the rights.
+     These restrictions translate to certain responsibilities for you if you
+     distribute copies of the software, or if you modify it.
+     
+     For example, if you distribute copies of such a program, whether
+     gratis or for a fee, you must give the recipients all the rights that
+     you have.  You must make sure that they, too, receive or can get the
+     source code.  And you must show them these terms so they know their
+     rights.
+     
+     We protect your rights with two steps: (1) copyright the software, and
+     (2) offer you this license which gives you legal permission to copy,
+     distribute and/or modify the software.
+     
+     Also, for each author's protection and ours, we want to make certain
+     that everyone understands that there is no warranty for this free
+     software.  If the software is modified by someone else and passed on, we
+     want its recipients to know that what they have is not the original, so
+     that any problems introduced by others will not reflect on the original
+     authors' reputations.
+     
+     Finally, any free program is threatened constantly by software
+     patents.  We wish to avoid the danger that redistributors of a free
+     program will individually obtain patent licenses, in effect making the
+     program proprietary.  To prevent this, we have made it clear that any
+     patent must be licensed for everyone's free use or not licensed at all.
+     
+     The precise terms and conditions for copying, distribution and
+     modification follow.
+     
+     	    GNU GENERAL PUBLIC LICENSE
+     TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+     
+     0. This License applies to any program or other work which contains
+     a notice placed by the copyright holder saying it may be distributed
+     under the terms of this General Public License.  The "Program", below,
+     refers to any such program or work, and a "work based on the Program"
+     means either the Program or any derivative work under copyright law:
+     that is to say, a work containing the Program or a portion of it,
+     either verbatim or with modifications and/or translated into another
+     language.  (Hereinafter, translation is included without limitation in
+     the term "modification".)  Each licensee is addressed as "you".
+     
+     Activities other than copying, distribution and modification are not
+     covered by this License; they are outside its scope.  The act of
+     running the Program is not restricted, and the output from the Program
+     is covered only if its contents constitute a work based on the
+     Program (independent of having been made by running the Program).
+     Whether that is true depends on what the Program does.
+     
+     1. You may copy and distribute verbatim copies of the Program's
+     source code as you receive it, in any medium, provided that you
+     conspicuously and appropriately publish on each copy an appropriate
+     copyright notice and disclaimer of warranty; keep intact all the
+     notices that refer to this License and to the absence of any warranty;
+     and give any other recipients of the Program a copy of this License
+     along with the Program.
+     
+     You may charge a fee for the physical act of transferring a copy, and
+     you may at your option offer warranty protection in exchange for a fee.
+     
+     2. You may modify your copy or copies of the Program or any portion
+     of it, thus forming a work based on the Program, and copy and
+     distribute such modifications or work under the terms of Section 1
+     above, provided that you also meet all of these conditions:
+     
+     a) You must cause the modified files to carry prominent notices
+     stating that you changed the files and the date of any change.
+     
+     b) You must cause any work that you distribute or publish, that in
+     whole or in part contains or is derived from the Program or any
+     part thereof, to be licensed as a whole at no charge to all third
+     parties under the terms of this License.
+     
+     c) If the modified program normally reads commands interactively
+     when run, you must cause it, when started running for such
+     interactive use in the most ordinary way, to print or display an
+     announcement including an appropriate copyright notice and a
+     notice that there is no warranty (or else, saying that you provide
+     a warranty) and that users may redistribute the program under
+     these conditions, and telling the user how to view a copy of this
+     License.  (Exception: if the Program itself is interactive but
+     does not normally print such an announcement, your work based on
+     the Program is not required to print an announcement.)
+     
+     These requirements apply to the modified work as a whole.  If
+     identifiable sections of that work are not derived from the Program,
+     and can be reasonably considered independent and separate works in
+     themselves, then this License, and its terms, do not apply to those
+     sections when you distribute them as separate works.  But when you
+     distribute the same sections as part of a whole which is a work based
+     on the Program, the distribution of the whole must be on the terms of
+     this License, whose permissions for other licensees extend to the
+     entire whole, and thus to each and every part regardless of who wrote it.
+     
+     Thus, it is not the intent of this section to claim rights or contest
+     your rights to work written entirely by you; rather, the intent is to
+     exercise the right to control the distribution of derivative or
+     collective works based on the Program.
+     
+     In addition, mere aggregation of another work not based on the Program
+     with the Program (or with a work based on the Program) on a volume of
+     a storage or distribution medium does not bring the other work under
+     the scope of this License.
+     
+     3. You may copy and distribute the Program (or a work based on it,
+     under Section 2) in object code or executable form under the terms of
+     Sections 1 and 2 above provided that you also do one of the following:
+     
+     a) Accompany it with the complete corresponding machine-readable
+     source code, which must be distributed under the terms of Sections
+     1 and 2 above on a medium customarily used for software interchange; or,
+     
+     b) Accompany it with a written offer, valid for at least three
+     years, to give any third party, for a charge no more than your
+     cost of physically performing source distribution, a complete
+     machine-readable copy of the corresponding source code, to be
+     distributed under the terms of Sections 1 and 2 above on a medium
+     customarily used for software interchange; or,
+     
+     c) Accompany it with the information you received as to the offer
+     to distribute corresponding source code.  (This alternative is
+     allowed only for noncommercial distribution and only if you
+     received the program in object code or executable form with such
+     an offer, in accord with Subsection b above.)
+     
+     The source code for a work means the preferred form of the work for
+     making modifications to it.  For an executable work, complete source
+     code means all the source code for all modules it contains, plus any
+     associated interface definition files, plus the scripts used to
+     control compilation and installation of the executable.  However, as a
+     special exception, the source code distributed need not include
+     anything that is normally distributed (in either source or binary
+     form) with the major components (compiler, kernel, and so on) of the
+     operating system on which the executable runs, unless that component
+     itself accompanies the executable.
+     
+     If distribution of executable or object code is made by offering
+     access to copy from a designated place, then offering equivalent
+     access to copy the source code from the same place counts as
+     distribution of the source code, even though third parties are not
+     compelled to copy the source along with the object code.
+     
+     4. You may not copy, modify, sublicense, or distribute the Program
+     except as expressly provided under this License.  Any attempt
+     otherwise to copy, modify, sublicense or distribute the Program is
+     void, and will automatically terminate your rights under this License.
+     However, parties who have received copies, or rights, from you under
+     this License will not have their licenses terminated so long as such
+     parties remain in full compliance.
+     
+     5. You are not required to accept this License, since you have not
+     signed it.  However, nothing else grants you permission to modify or
+     distribute the Program or its derivative works.  These actions are
+     prohibited by law if you do not accept this License.  Therefore, by
+     modifying or distributing the Program (or any work based on the
+     Program), you indicate your acceptance of this License to do so, and
+     all its terms and conditions for copying, distributing or modifying
+     the Program or works based on it.
+     
+     6. Each time you redistribute the Program (or any work based on the
+     Program), the recipient automatically receives a license from the
+     original licensor to copy, distribute or modify the Program subject to
+     these terms and conditions.  You may not impose any further
+     restrictions on the recipients' exercise of the rights granted herein.
+     You are not responsible for enforcing compliance by third parties to
+     this License.
+     
+     7. If, as a consequence of a court judgment or allegation of patent
+     infringement or for any other reason (not limited to patent issues),
+     conditions are imposed on you (whether by court order, agreement or
+     otherwise) that contradict the conditions of this License, they do not
+     excuse you from the conditions of this License.  If you cannot
+     distribute so as to satisfy simultaneously your obligations under this
+     License and any other pertinent obligations, then as a consequence you
+     may not distribute the Program at all.  For example, if a patent
+     license would not permit royalty-free redistribution of the Program by
+     all those who receive copies directly or indirectly through you, then
+     the only way you could satisfy both it and this License would be to
+     refrain entirely from distribution of the Program.
+     
+     If any portion of this section is held invalid or unenforceable under
+     any particular circumstance, the balance of the section is intended to
+     apply and the section as a whole is intended to apply in other
+     circumstances.
+     
+     It is not the purpose of this section to induce you to infringe any
+     patents or other property right claims or to contest validity of any
+     such claims; this section has the sole purpose of protecting the
+     integrity of the free software distribution system, which is
+     implemented by public license practices.  Many people have made
+     generous contributions to the wide range of software distributed
+     through that system in reliance on consistent application of that
+     system; it is up to the author/donor to decide if he or she is willing
+     to distribute software through any other system and a licensee cannot
+     impose that choice.
+     
+     This section is intended to make thoroughly clear what is believed to
+     be a consequence of the rest of this License.
+     
+     8. If the distribution and/or use of the Program is restricted in
+     certain countries either by patents or by copyrighted interfaces, the
+     original copyright holder who places the Program under this License
+     may add an explicit geographical distribution limitation excluding
+     those countries, so that distribution is permitted only in or among
+     countries not thus excluded.  In such case, this License incorporates
+     the limitation as if written in the body of this License.
+     
+     9. The Free Software Foundation may publish revised and/or new versions
+     of the General Public License from time to time.  Such new versions will
+     be similar in spirit to the present version, but may differ in detail to
+     address new problems or concerns.
+     
+     Each version is given a distinguishing version number.  If the Program
+     specifies a version number of this License which applies to it and "any
+     later version", you have the option of following the terms and conditions
+     either of that version or of any later version published by the Free
+     Software Foundation.  If the Program does not specify a version number of
+     this License, you may choose any version ever published by the Free Software
+     Foundation.
+     
+     10. If you wish to incorporate parts of the Program into other free
+     programs whose distribution conditions are different, write to the author
+     to ask for permission.  For software which is copyrighted by the Free
+     Software Foundation, write to the Free Software Foundation; we sometimes
+     make exceptions for this.  Our decision will be guided by the two goals
+     of preserving the free status of all derivatives of our free software and
+     of promoting the sharing and reuse of software generally.
+     
+     		    NO WARRANTY
+     
+     11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+     FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+     OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+     PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+     OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+     MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+     TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+     PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+     REPAIR OR CORRECTION.
+     
+     12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+     WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+     REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+     INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+     OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+     TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+     YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+     PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+     POSSIBILITY OF SUCH DAMAGES.
+     
+     	     END OF TERMS AND CONDITIONS
+     
+     Appendix: How to Apply These Terms to Your New Programs
+     
+     If you develop a new program, and you want it to be of the greatest
+     possible use to the public, the best way to achieve this is to make it
+     free software which everyone can redistribute and change under these terms.
+     
+     To do so, attach the following notices to the program.  It is safest
+     to attach them to the start of each source file to most effectively
+     convey the exclusion of warranty; and each file should have at least
+     the "copyright" line and a pointer to where the full notice is found.
+     
+     <one line to give the program's name and a brief idea of what it does.>
+     Copyright (C) 19yy  <name of author>
+     
+     This program is free software; you can redistribute it and/or modify
+     it under the terms of the GNU General Public License as published by
+     the Free Software Foundation; either version 2 of the License, or
+     (at your option) any later version.
+     
+     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., 675 Mass Ave, Cambridge, MA 02139, USA.
+     
+     Also add information on how to contact you by electronic and paper mail.
+     
+     If the program is interactive, make it output a short notice like this
+     when it starts in an interactive mode:
+     
+     Gnomovision version 69, Copyright (C) 19yy name of author
+     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+     This is free software, and you are welcome to redistribute it
+     under certain conditions; type `show c' for details.
+     
+     The hypothetical commands `show w' and `show c' should show the appropriate
+     parts of the General Public License.  Of course, the commands you use may
+     be called something other than `show w' and `show c'; they could even be
+     mouse-clicks or menu items--whatever suits your program.
+     
+     You should also get your employer (if you work as a programmer) or your
+     school, if any, to sign a "copyright disclaimer" for the program, if
+     necessary.  Here is a sample; alter the names:
+     
+     Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+     `Gnomovision' (which makes passes at compilers) written by James Hacker.
+     
+     <signature of Ty Coon>, 1 April 1989
+     Ty Coon, President of Vice
+     
+     This General Public License does not permit incorporating your program into
+     proprietary programs.  If your program is a subroutine library, you may
+     consider it more useful to permit linking proprietary applications with the
+     library.  If this is what you want to do, use the GNU Library General
+     Public License instead of this License.
+

Added: branches/mysql-connector-java/upstream/5.0.4/EXCEPTIONS-CONNECTOR-J
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/EXCEPTIONS-CONNECTOR-J	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/EXCEPTIONS-CONNECTOR-J	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,99 @@
+MySQL FLOSS License Exception
+
+The MySQL AB Exception for Free/Libre and Open Source Software-only
+Applications Using MySQL Client Libraries (the "FLOSS Exception").
+
+
+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"). 
+
+
+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:
+
+    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.
+
+1. FLOSS License List
+
+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
+
+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).
+
+2. Definitions
+
+   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 $

Added: branches/mysql-connector-java/upstream/5.0.4/README
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/README	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/README	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,45 @@
+MySQL Connector/J @MYSQL_CJ_VERSION@ (formerly MM.MySQL)
+MySQL AB's JDBC Driver for MySQL
+Copyright (c) 2003 MySQL AB
+
+CONTENTS
+
+* License
+* Documentation Location
+
+
+LICENSE
+
+MySQL Connector/J is licensed under the GPL or a commercial license
+from MySQL AB. 
+
+If you have licensed this product under the GPL, please see the COPYING
+file for more information. 
+
+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.
+
+If you have licensed this product under a commercial license from
+MySQL AB, please see the file "MySQLEULA.txt" that comes with this 
+distribution for the terms of the license.
+
+If you need non-GPL licenses for commercial distribution please contact 
+me <mark at mysql.com> or <sales at mysql.com>.
+
+
+DOCUMENTATION LOCATION
+ 
+The documentation formerly contained in this file has moved into the 
+'doc' directory, where it is available in HTML, PDF and plaintext
+forms.
+
+You may also find the latest copy of the documentation on the MySQL
+website at http://dev.mysql.com/doc/refman/5.0/en/connector-j.html
+
+--
+This software is OSI Certified Open Source Software.
+OSI Certified is a certification mark of the Open Source Initiative.
+
+

Added: branches/mysql-connector-java/upstream/5.0.4/README.txt
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/README.txt	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/README.txt	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,45 @@
+MySQL Connector/J @MYSQL_CJ_VERSION@ (formerly MM.MySQL)
+MySQL AB's JDBC Driver for MySQL
+Copyright (c) 2003 MySQL AB
+
+CONTENTS
+
+* License
+* Documentation Location
+
+
+LICENSE
+
+MySQL Connector/J is licensed under the GPL or a commercial license
+from MySQL AB. 
+
+If you have licensed this product under the GPL, please see the COPYING
+file for more information. 
+
+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.
+
+If you have licensed this product under a commercial license from
+MySQL AB, please see the file "MySQLEULA.txt" that comes with this 
+distribution for the terms of the license.
+
+If you need non-GPL licenses for commercial distribution please contact 
+me <mark at mysql.com> or <sales at mysql.com>.
+
+
+DOCUMENTATION LOCATION
+ 
+The documentation formerly contained in this file has moved into the 
+'doc' directory, where it is available in HTML, PDF and plaintext
+forms.
+
+You may also find the latest copy of the documentation on the MySQL
+website at http://dev.mysql.com/doc/refman/5.0/en/connector-j.html
+
+--
+This software is OSI Certified Open Source Software.
+OSI Certified is a certification mark of the Open Source Initiative.
+
+

Added: branches/mysql-connector-java/upstream/5.0.4/build.xml
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/build.xml	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/build.xml	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1044 @@
+<?xml version='1.0'?>
+<!--
+   Copyright (C) 2002-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
+-->
+
+<!-- version $Id: build.xml 5899 2006-10-18 20:47:34Z 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="version_status" value=""/>
+
+	<property name="version" value="${major_version}.${minor_version}.${subminor_version}${version_status}"/>
+	<property name="prodName" value="mysql-connector-java"/>
+	<property name="extra.version" value=""/>
+	<property name="full.version" value="${version}${extra.version}"/>
+	<property name="fullProdName" value="${prodName}-${version}${extra.version}"/>
+	<property name="sourceDir" value="./src"/>
+	<property name="buildDir" value="./build"/>
+	<property name="distDir" value="./dist"/>
+	<property name="junit.results" value="${buildDir}/junit"/>
+	<property name="debug.enable" value="on" />
+
+	<!-- 
+	
+	     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. 
+	-->
+
+	<property name="com.mysql.jdbc.docs.sourceDir" value="/home/mmatthew/work/docs/prebuilt"/>
+
+	<taskdef resource="net/sf/antcontrib/antlib.xml">
+		<classpath>
+			<pathelement location="${sourceDir}/lib/ant-contrib.jar"/>
+		</classpath>
+	</taskdef>
+
+	<path id="built.driver.only.classpath">
+		<pathelement location="${buildDir}/${fullProdName}" />
+	</path>
+
+	<path id="project.build.classpath">
+		<fileset dir="${buildDir}/${fullProdName}/lib-nodist">
+			<include name="**/*.jar"/>
+		</fileset>
+
+		<fileset dir="${buildDir}/${fullProdName}/lib">
+			<include name="**/*.jar"/>
+		</fileset>
+
+		<pathelement location="${buildDir}/${fullProdName}" />
+	</path>
+
+	<target name="init" depends="-init-copy, -init-no-crypto">
+		<!-- Check that files needed to generate documentation are in place -->
+
+		<!-- 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 -->
+
+		<mkdir dir="${buildDir}/${fullProdName}/lib-nodist" />
+		
+		<available file="${com.mysql.jdbc.docs.sourceDir}" property="com.mysql.jdbc.docs.sourcesPresent" />
+
+		<available classname="com.mchange.v2.c3p0.QueryConnectionTester" 
+			classpathref="project.build.classpath" 
+			property="com.mysql.jdbc.c3p0Present" />
+
+		<available classname="org.apache.log4j.Logger" 
+			classpathref="project.build.classpath" 
+			property="com.mysql.jdbc.log4jPresent" />
+
+		<available classname="org.jboss.resource.adapter.jdbc.ValidConnectionChecker" 
+			classpathref="project.build.classpath" 
+			property="com.mysql.jdbc.jbossPresent" />
+	</target>
+
+	<target name="-init-copy" depends="clean">
+		<tstamp/>
+
+		<filter token="VERSION" value="${version}"/>
+
+		<copy todir="${buildDir}/${fullProdName}" filtering="true">
+			<fileset dir="${sourceDir}/" excludes="**/CVS">
+				<patternset id="classjar" >
+					<exclude name="**/*.class*"/>
+					<exclude name="**/*.jar"/>
+				</patternset>
+			</fileset>
+
+			<filterset>
+				<filter token="MYSQL_CJ_MAJOR_VERSION" value="${major_version}"/>
+				<filter token="MYSQL_CJ_MINOR_VERSION" value="${minor_version}"/>
+				<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_FULL_PROD_NAME" value="${fullProdName}"/>
+			</filterset>
+		</copy>
+
+		<copy todir="${buildDir}/${fullProdName}" filtering="false">
+			<fileset dir="${sourceDir}" excludes="**/CVS">
+				<patternset id="dojar" >
+					<include name="**/*.jar*"/>
+				</patternset>
+			</fileset>
+		</copy>
+	</target>
+
+	<target name="-init-no-crypto" depends="-init-copy" if="com.mysql.jdbc.noCryptoBuild">
+		<copy file="${sourceDir}//lib-nodist/ExportControlledNoCrypto.notjava"
+			toFile="${buildDir}/${fullProdName}/com/mysql/jdbc/ExportControlled.java"
+			overwrite="true"/>
+	</target>
+
+	<!-- 
+		This is the target we use to make MySQL AB GPL-licensed and binaries 
+		as well as commercially-licensed binaries that include source files.
+	-->
+
+	<target name="full-package" description="Builds driver, binary .jar file, docs and packages (.zip, .tar.gz) suitable for distribution that _do_ contain sources" 
+		depends="full-dist, -make-packages, -create-archives"/>
+
+	<!-- This is the target we use to make MySQL AB Commercially-licensed binaries -->
+
+	<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"/>
+
+	<target name="full-dist" description="Builds driver, binary .jar file and docs, basically a distribution 'image'"
+		depends="-dist-trace, dist, -bundle-docs"/>
+
+	<target name="package" depends="dist, -make-packages, -create-archives"/>
+
+	<target name="-remove-sources">
+		<echo>Removing sources from ${distDir}/toArchive</echo>
+		<delete>
+			<fileset dir="${distDir}/toArchive">
+				<include name="**/*.java"/>
+			</fileset>
+		</delete>
+		
+		<delete dir="${distDir}/toArchive/${fullProdName}/src"/>
+	</target>
+
+	<target name="-create-archives" depends="-make-packages">
+		<tar destfile="${distDir}/${fullProdName}.tar.gz"
+			compression="gzip" longfile="gnu">
+			<tarfileset dir="${toPackage}">
+				<patternset refid="non.test.sources" />
+			</tarfileset>
+		</tar>
+
+		<checksum file="${distDir}/${fullProdName}.tar.gz" forceOverwrite="yes" fileext=".md5"/>
+
+		<delete file="${distDir}/${fullProdName}.zip"/>
+
+		<zip zipfile="${distDir}/${fullProdName}.zip">
+			<fileset dir="${toPackage}">
+				<patternset refid="non.test.sources" />
+			</fileset>
+		</zip>
+
+		<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"
+					toDir="${mavenUploadDir}" />
+		
+		<jar jarfile="${distDir}/${fullProdName}.bundle.jar" 
+					basedir="${mavenUploadDir}" />
+	</target>
+	
+	<target name="-make-packages" depends="dist">
+		<property name="toPackage" value="${distDir}/toArchive"/>
+		<property name="packageDest" value = "${toPackage}/${fullProdName}"/>
+
+		<delete dir="${toPackage}" />
+		<mkdir dir="${toPackage}"/>
+		<mkdir dir="${packageDest}"/>
+
+		<delete file="${distDir}/${fullProdName}.tar.gz"/>
+
+		<patternset id="non.test.sources" >
+			<exclude name="**/*.nb*"/>
+			<exclude name="**/*.bak"/>
+			<exclude name="**/*.*~"/>
+			<exclude name="**/lib-nodist/*"/>
+			<exclude name="**/lib-coverage/*"/>
+			<exclude name="**/clover/*"/>
+			<exclude name="**/checkstyle/*"/>
+			<exclude name="**/.*"/>
+		</patternset>
+
+		<copy todir="${packageDest}">
+			<fileset dir="${buildDir}/${fullProdName}"
+				includes="debug/*, docs/**, *.jar" excludes="docs/sources">
+				<patternset refid="non.test.sources"/>
+			</fileset>
+
+			<fileset dir="." includes="src/com/**, src/doc/**, src/lib/*, src/org/**, src/testsuite/**, build.xml, CHANGES, COPYING, EXCEPTIONS-CONNECTOR-J, README">
+				<patternset refid="non.test.sources"/>
+			</fileset>
+		</copy>
+		
+		<!-- Fix CRLF for various platforms -->
+		<copy file="${packageDest}/README" tofile="${packageDest}/README.txt"/>
+		<copy file="${packageDest}/docs/README.txt" tofile="${packageDest}/docs/README"/>
+
+		<!-- For Windows-y platforms -->
+
+		<fixcrlf srcdir="${packageDest}"
+			tab="remove" tablength="8"
+			eol="crlf" includes="**/README.txt"/>
+
+		<!-- For Unix-y platforms -->
+
+		<fixcrlf srcdir="${packageDest}"
+					tab="remove" tablength="8"
+					eol="lf" includes="**/README"/>
+		
+		<if>
+			<isset property="com.mysql.jdbc.commercialBuild"/>
+			<then>
+				<!-- These are the GPL and FLOSS exceptions. They don't get shipped with
+									 commercial builds -->
+
+				<delete file="${packageDest}/COPYING"/>
+				<delete file="${packageDest}/EXCEPTIONS-CONNECTOR-J"/>
+
+				<!-- Pull in the commercial license itself -->
+
+				<copy file="${sourceDir}/lib-nodist/MySQLEULA.txt" toDir="${packageDest}"/>
+
+				<loadfile property="commercialLicenseText"
+					srcFile="${sourceDir}/lib-nodist/commercialLicense.txt"/>
+				<replaceregexp 
+					match="Copyright.*1307.[^replaceregexp]*USA"
+					replace="${commercialLicenseText}"
+					flags="s">
+				<fileset dir="${packageDest}" includes="**/*"/>
+				</replaceregexp>
+			</then>
+		</if>	
+	</target>
+
+	<target name="dist" depends="init, compile">
+
+		<delete file="${buildDir}/${fullProdName}-bin.jar" />
+		<delete file="${distDir}/${fullProdName}.jar" />
+
+		<mkdir dir="${distDir}" />
+		<mkdir dir="${buildDir}/META-INF" />
+		
+		<!-- JDBC-4.0 support of service provider mechanism -->
+		
+		<mkdir dir="${buildDir}/META-INF/services/" />
+		<echo file="${buildDir}/META-INF/services/java.sql.Driver"
+			message="com.mysql.jdbc.Driver" />
+
+		<manifest file="${buildDir}/META-INF/MANIFEST.MF">
+			<attribute name="Built-By" value="${user.name}" />
+
+			<section name="common">
+				<attribute name="Specification-Title" value="JDBC" />
+				<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" value="MySQL AB" />
+			</section>
+		</manifest>
+
+		<jar jarfile="${buildDir}/${fullProdName}/${fullProdName}-bin.jar" 
+			basedir="${buildDir}/${fullProdName}" 
+			includes="**/*.class,**/*.properties*,COPYING,README" 
+			excludes="testsuite/**" 
+			index="true" 
+			manifest="${buildDir}/META-INF/MANIFEST.MF"/>
+	</target>
+
+	<target name="-dist-trace" depends="init, compile-driver-trace">
+
+		<delete file="${buildDir}/${fullProdName}-bin-g.jar"/>
+
+		<mkdir dir="${distDir}" />
+		<mkdir dir="${buildDir}/${fullProdName}/debug"/>
+
+		<manifest file="${buildDir}/MANIFEST.MF">
+			<attribute name="Built-By" value="${user.name}" />
+
+			<section name="common">
+				<attribute name="Specification-Title" value="JDBC" />
+				<attribute name="Specification-Version" value="3.0" />
+				<attribute name="Specification-Vendor" value="Sun Microsystems Inc." />
+				<attribute name="Implementation-Title" value="MySQL Connector/J (trace/debug enabled)" />
+				<attribute name="Implementation-Version" value="${full-version} (trace/debug-enabled)" />
+				<attribute name="Implementation-Vendor" value="MySQL AB" />
+			</section>
+		</manifest>
+
+		<jar jarfile="${buildDir}/${fullProdName}/debug/${fullProdName}-bin-g.jar"
+				basedir="${buildDir}/${fullProdName}"
+				includes="**/*.class,**/*.properties*,COPYING,README"
+				excludes="testsuite/**"
+				index="true"
+				manifest="${buildDir}/MANIFEST.MF"
+			/>
+	</target>
+
+	<target name="-bundle-docs" depends="init">
+		<copy file="${com.mysql.jdbc.docs.sourceDir}/en/html/connector-j.html"
+		  	todir="${buildDir}/${fullProdName}/docs"
+		  	failonerror="true"/>
+		<copy file="${com.mysql.jdbc.docs.sourceDir}/en/pdf/connector-j.pdf"
+			todir="${buildDir}/${fullProdName}/docs"
+			failonerror="true"/>
+		<copy file="${com.mysql.jdbc.docs.sourceDir}/en/txt/connector-j.txt"
+			tofile="${buildDir}/${fullProdName}/docs/README.txt"
+			failonerror="true"/>
+		<copy todir="${buildDir}/${fullProdName}/docs/release-test-output" failonerror="false">
+			<fileset dir="${com.mysql.jdbc.docs.sourceDir}/release-test-output" excludes="**/CVS">
+			</fileset>
+		</copy>
+	</target>
+
+	<target name="test-all-multi" depends="compile">
+		<delete dir="${buildDir}/junit" />
+		<antcall target="test-multijvm" />
+		<antcall target="test-compliance-multijvm" />
+	</target>
+
+	<!-- Runs compliance testsuite against multiple JVMs and server configs -->
+
+	<target name="test-multijvm" depends="compile">
+		<for list="1,2,3,4,5,6,7,8" param="jvm.number">
+			<sequential>
+				<if>
+					<isset property="com.mysql.jdbc.testsuite.jvm.@{jvm.number}"/>
+					<then>
+						<for list="1,2,3,4,5,6,7,8" param="url.number">
+							<sequential>
+								<if>
+									<isset property="com.mysql.jdbc.testsuite.url.@{url.number}"/>
+									<then>
+										<make-output-hier 
+										jdbcUrl="${com.mysql.jdbc.testsuite.url.@{url.number}}" 
+										junitOutput="${junit.results}" 
+										jvm="${com.mysql.jdbc.testsuite.jvm.@{jvm.number}}" 
+										unitOrCompliance="unit" />
+
+										<property name="test.multi.junitOut.@{url.number}" value="eraseMe" />
+										<var name="test.multi.junitOut.@{url.number}" unset="true" />
+
+										<loadfile srcfile="${junit.results}/hier" 
+										property="test.multi.junitOut.@{url.number}" />
+
+										<antcall target="test">
+											<param name="com.mysql.jdbc.testsuite.url" value="${com.mysql.jdbc.testsuite.url.@{url.number}}" />
+											<param name="test.result.prefix" value="/${test.multi.junitOut.@{url.number}}" />
+											<param name="com.mysql.jdbc.testsuite.jvm" value="${com.mysql.jdbc.testsuite.jvm.@{jvm.number}}"/>
+										</antcall>
+									</then>
+								</if>
+							</sequential>
+						</for>
+					</then>
+				</if>
+			</sequential>
+		</for>
+	</target>
+
+	<target name="-set-test-result-prefix" unless="${test.result.prefix}">
+		<property name="test.result.prefix" value=""/>
+	</target>
+
+	<target name="test" depends="compile, -set-test-result-prefix">
+		<property name="com.mysql.jdbc.testsuite.jvm" value="java"/>
+		<mkdir dir="${junit.results}"/>
+		<echo message="Running unit tests against ${com.mysql.jdbc.testsuite.url} with jvm ${com.mysql.jdbc.testsuite.jvm}"/>
+
+		<property name="junit.unitregress.results" value="${junit.results}/${test.result.prefix}/unitregress" />
+
+		<mkdir dir="${junit.results}/${test.result.prefix}"/>
+		<mkdir dir="${junit.unitregress.results}"/>
+
+		<mkdir dir="${junit.unitregress.results}/report"/>
+
+		<junit printSummary="yes" 
+			fork="on" 
+			jvm="${com.mysql.jdbc.testsuite.jvm}"
+			errorProperty="tests.failed"
+			failureProperty="tests.failed">
+			<jvmarg value="-Xmx256m" />
+
+			<!-- For java.sql.SavePoint on old JVMs -->
+
+			<jvmarg value="-Xverify:none" />
+
+			<sysproperty key="com.mysql.jdbc.testsuite.url" value="${com.mysql.jdbc.testsuite.url}"/>
+
+			<classpath>
+				<fileset dir="${sourceDir}/lib-nodist">
+					<include name="**/*.jar"/>
+				</fileset>
+				<fileset dir="${buildDir}/${fullProdName}/lib">
+					<include name="**/*.jar"/>
+				</fileset>
+				<pathelement location="${buildDir}/${fullProdName}" />
+				<pathelement path="${java.class.path}" />
+			</classpath>
+
+			<formatter type="xml"/>
+
+			<batchtest fork="yes" todir="${junit.unitregress.results}" >
+				<fileset dir="${buildDir}/${fullProdName}">
+					<include name="**/*Test.java" />
+					<exclude name="**/perf/*.java"/>
+				</fileset>
+			</batchtest>
+		</junit>
+
+		<junitreport todir="${junit.unitregress.results}/report">
+			<fileset dir="${junit.unitregress.results}">
+				<include name="**/TEST-*.xml"/>
+			</fileset>
+			<report format="frames" todir="${junit.unitregress.results}/report"/>
+		</junitreport>
+		
+		<!-- Don't fail the build if we're doing multi-tests -->
+		<if>
+		 <equals arg1="${test.result.prefix}" arg2="" />
+		 <then>
+		 	<fail message="Tests failed. Check logs and/or reports in ${junit.results}." if="tests.failed"/>
+		 </then>
+		 <else>
+		 	<echo message="Not checking test failures because we're doing a multi-test..." />
+		 </else>
+		</if>
+		
+	</target>
+
+	<!-- Runs compliance testsuite against multiple JVMs and server configs -->
+
+	<target name="test-compliance-multijvm" depends="compile">
+		<for list="1,2,3,4,5,6,7,8" param="jvm.number">
+			<sequential>
+				<if>
+					<isset property="com.mysql.jdbc.testsuite.jvm.@{jvm.number}"/>
+					<then>
+						<for list="1,2,3,4,5,6,7,8" param="url.number">
+							<sequential>
+								<if>
+									<isset property="com.mysql.jdbc.compliance.url.@{url.number}"/>
+									<then>
+										<make-output-hier 
+											jdbcUrl="${com.mysql.jdbc.compliance.url.@{url.number}}" 
+											junitOutput="${junit.results}"
+											jvm="${com.mysql.jdbc.testsuite.jvm.@{jvm.number}}" 
+											unitOrCompliance="compliance"/>
+
+										<property name="test.compliance.multi.junitOut.@{url.number}" value="eraseMe" />
+										<var name="test.compliance.multi.junitOut.@{url.number}" unset="true" />
+
+										<loadfile srcfile="${junit.results}/hier" property="test.compliance.multi.junitOut.@{url.number}" />
+
+										<antcall target="test-compliance">
+											<param name="com.mysql.jdbc.compliance.url" value="${com.mysql.jdbc.compliance.url.@{url.number}}"/>
+											<param name="com.mysql.jdbc.testsuite.jvm" value="${com.mysql.jdbc.testsuite.jvm.@{jvm.number}}"/>
+											<param name="test.result.prefix" value="/${test.compliance.multi.junitOut.@{url.number}}"/>
+										</antcall>
+									</then>
+								</if>
+							</sequential>
+						</for>
+					</then>
+				</if>
+			</sequential>
+		</for>
+	</target>
+
+	<!-- 
+	
+		Tests for JDBC compliance. The JDBC compliance testsuite
+	    is not shipped with MySQL Connector/J as it is not licensible
+	    except from Sun.
+	     
+	    The properties com.mysql.jdbc.compliance.url and
+	    jdbc-compliance-location must be set
+	    prior to running this test. 
+	     
+	-->
+
+	<target name="test-compliance" depends="compile">
+		<property name="com.mysql.jdbc.test.jvm" value="java"/>
+		<mkdir dir="${junit.results}"/>
+		<echo message="Running compliance tests against ${com.mysql.jdbc.compliance.url} with jvm ${com.mysql.jdbc.testsuite.jvm}"/>
+
+		<property name="junit.compliance.results" 
+			value="${junit.results}/${test.result.prefix}/compliance"/>
+
+		<mkdir dir="${junit.results}/${test.result.prefix}"/>
+		<mkdir dir="${junit.compliance.results}"/>
+		<mkdir dir="${junit.compliance.results}/report"/>
+
+		<junit printsummary="yes" jvm="${com.mysql.jdbc.testsuite.jvm}"
+			errorProperty="tests.compliance.failed"
+			failureProperty="tests.compliance.failed">
+			<jvmarg value="-Xmx256m"/>
+			
+			<!-- For java.sql.SavePoint on old JVMs -->
+				
+			<jvmarg value="-Xverify:none"/>
+			
+			<sysproperty key="com.mysql.jdbc.compliance.url" value="${com.mysql.jdbc.compliance.url}"/>
+			
+			<classpath>
+				<pathelement location="${buildDir}/${fullProdName}"/>
+				<fileset dir="${sourceDir}/lib-nodist">
+					<include name="**/*.jar"/>
+				</fileset>
+				<fileset dir="${buildDir}/${fullProdName}/lib">
+					<include name="**/*.jar"/>
+				</fileset>
+				<pathelement path="${java.class.path}" />
+				<pathelement location="${jdbc-compliance-location}"/>
+			</classpath>
+
+			<formatter type="xml"/>
+
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.BatchUpdateTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.ConnectionTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.DatabaseMetaDataTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.EscapeSyntaxTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.PreparedStatementTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.ResultSetMetadataTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.ResultSetTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.StatementTest"/>
+		</junit>
+
+		<junitreport todir="${junit.compliance.results}/report">
+			<fileset dir="${junit.compliance.results}">
+				<include name="**/TEST-*.xml"/>
+			</fileset>
+			<report format="frames" todir="${junit.compliance.results}/report"/>
+		</junitreport>
+		
+		<!-- Don't fail the build if we're doing multi-tests -->
+		<if>
+			<equals arg1="${test.result.prefix}" arg2="" />
+			<then>
+				<fail message="Tests failed. Check logs and/or reports in ${junit.compliance.results}." if="tests.compliance.failed"/>
+			</then>
+			<else>
+				<echo message="Not checking test failures because we're doing a multi-test..." />
+			</else>
+		</if>
+		
+	</target>
+
+	<target name="compile" depends="init, compile-driver, compile-testsuite, compile.integration">
+	</target>
+
+	<target name="compile-trace" depends="init, compile-driver-trace">
+	</target>
+
+	<!-- Compiles the driver itself -->
+
+	<target name="compile-driver" depends="init, -clean-output">
+		<javac srcdir="${buildDir}/${fullProdName}" 
+			destdir="${buildDir}/${fullProdName}" 
+			deprecation="off" 
+			debug="${debug.enable}" 
+			excludes="testsuite/**, 
+			   com/mysql/jdbc/integration/**,
+			   com/mysql/jdbc/log/Log4JLogger.java">
+			<classpath refid="project.build.classpath" />
+		</javac>
+	</target>
+
+	<!-- Compiles a version of the driver with AspectJ tracing -->
+
+	<target name="compile-driver-trace" depends="init">
+		<taskdef 
+			      resource="org/aspectj/tools/ant/taskdefs/aspectjTaskdefs.properties">
+			<classpath>
+				<pathelement location="${sourceDir}/lib/aspectjtools.jar"/>
+			</classpath>
+		</taskdef>
+
+
+		<iajc destDir="${buildDir}/${fullProdName}">
+			<sourceroots>
+				<pathelement location="${buildDir}/${fullProdName}/org"/>
+				<pathelement location="${buildDir}/${fullProdName}/com"/>
+			</sourceroots>
+			<classpath refid="project.build.classpath"/>
+			<classpath>
+				<pathelement location="${sourceDir}/lib/aspectjtools.jar"/>
+			</classpath>
+		</iajc>
+	</target>
+
+	<!-- Compiles integration 'helpers' for third-party software -->
+	<target name="compile.integration" 
+		depends="compile-driver, 
+				 compile-integration-c3p0, 
+		         compile-integration-jboss,
+				 compile-integration-log4j"/>
+
+	<target name="compile-integration-c3p0" 
+		depends="compile-driver"
+		if="com.mysql.jdbc.c3p0Present">
+		<javac srcdir="${buildDir}/${fullProdName}" 
+				destdir="${buildDir}/${fullProdName}" 
+				deprecation="off" 
+				debug="${debug.enable}" 
+				includes="com/mysql/jdbc/integration/c3p0/**">
+			<classpath refid="project.build.classpath" />
+		</javac>
+	</target>
+
+	<target name="compile-integration-jboss" 
+			depends="compile-driver"
+			if="com.mysql.jdbc.jbossPresent">
+		<javac srcdir="${buildDir}/${fullProdName}" 
+					destdir="${buildDir}/${fullProdName}" 
+					deprecation="off" 
+					debug="${debug.enable}" 
+					includes="com/mysql/jdbc/integration/jboss/**">
+			<classpath refid="project.build.classpath" />
+		</javac>
+	</target>
+
+	<target name="compile-integration-log4j" 
+			depends="compile-driver"
+			if="com.mysql.jdbc.log4jPresent">
+		<javac srcdir="${buildDir}/${fullProdName}" 
+				destdir="${buildDir}/${fullProdName}" 
+				deprecation="off" 
+				debug="${debug.enable}" 
+				includes="com/mysql/jdbc/log/Log4JLogger.java">
+			<classpath refid="project.build.classpath" />
+		</javac>
+	</target>
+
+	<!-- Compiles the JUnit testsuite -->
+
+	<target name="compile-testsuite" depends="init, compile-driver">
+		<javac 
+			srcdir="${buildDir}/${fullProdName}" 
+			destdir="${buildDir}/${fullProdName}" 
+			deprecation="off" 
+			debug="${debug.enable}"
+			includes="testsuite/**"
+			excludes="testsuite/requiresNonRedists/**">
+			<classpath refid="project.build.classpath"/>
+		</javac>
+	</target>
+
+	<target name="real-clean">
+		<delete dir="${buildDir}"/>
+		<delete>
+			<fileset dir="${distDir}" 
+				includes="${fullProdName}.zip,${fullProdName}.tar.gz"/>
+		</delete>
+	</target>
+
+	<target name="clean" unless="com.mysql.jdbc.noCleanBetweenCompiles">
+		<delete dir="${buildDir}"/>
+	</target>
+
+	<target name="-clean-output" unless="com.mysql.jdbc.noCleanBetweenCompiles">
+		<delete>
+			<fileset dir="${buildDir}" 
+						includes="**/*.class"/>
+		</delete>
+	</target>
+
+	<target name="docs-generate-properties-table" depends="compile-driver">
+		<tempfile property="properties-xml" suffix=".xml" />
+
+		<java classname="com.mysql.jdbc.util.PropertiesDocGenerator"
+			output="${properties-xml}" classpath="${buildDir}/${fullProdName}"/>
+		<tempfile property="docsPropXslTempfile" suffix="xsl"/>
+		<copy file="${buildDir}/${fullProdName}/doc/sources/connPropsToDocbook.xsl" tofile="${docsPropXslTempfile}"/>
+
+		<style in="${properties-xml}" style="${docsPropXslTempfile}"
+		       out="${buildDir}/${fullProdName}/docs/sources/connPropsDocBook.xml"/>
+		<delete file="${properties-xml}"/>
+		<delete file="${docsPropXslTempfile}"/>
+	</target>
+
+	<target name="docs-generate-error-mapping-table" depends="compile-driver">
+		<tempfile property="errorsMapping-xml" suffix=".xml" />
+
+		<java classname="com.mysql.jdbc.util.ErrorMappingsDocGenerator"
+				output="${errorsMapping-xml}" classpath="${buildDir}/${fullProdName}"/>
+		<tempfile property="docsErrorXslTempfile" suffix="xsl"/>
+		<copy file="${buildDir}/${fullProdName}/doc/sources/errorMapToDocbook.xsl" tofile="${docsErrorXslTempfile}"/>
+
+		<style in="${errorsMapping-xml}" style="${docsErrorXslTempfile}"
+			       out="${buildDir}/${fullProdName}/docs/sources/errorMappingDocBook.xml"/>
+		<delete file="${docsErrorXslTempfile}"/>
+	</target>
+
+	<!-- 
+	     Targets below this point are for code coverage and depend on
+	     the 'Emma' project which you can download from 
+	     http://emma.sourceforge.net/
+	-->
+
+	<target name="emma" description="turns on EMMA instrumentation/reporting" >
+		<!-- directory that contains emma.jar and emma_ant.jar: -->
+		<property name="emma.dir" value="${sourceDir}/lib-coverage" />
+
+		<path id="emma.lib" >
+			<pathelement location="${emma.dir}/emma.jar" />
+			<pathelement location="${emma.dir}/emma_ant.jar" />
+		</path>
+
+		<taskdef resource="emma_ant.properties" classpathref="emma.lib" />
+
+		<property name="emma.enabled" value="true" />
+		<property name="emma.coverage.dir" value="${buildDir}/${fullProdName}-coverage" />
+	</target>
+
+	<target name="instrument" depends="init, compile" 
+		description="runs the examples" >
+
+		<emma enabled="${emma.enabled}" >
+			<instr instrpathref="built.driver.only.classpath"
+	             destdir="${buildDir}/${fullProdName}"	
+	             metadatafile="${emma.coverage.dir}/metadata.emma"
+	             merge="true"
+	      		 mode="overwrite">
+				<filter excludes="*Test*" />
+				<filter excludes="org.junit.*" />
+			</instr>
+		</emma>
+
+	</target>
+
+	<target name="test-emma-report" depends="test-coverage, test-coverage-compliance">
+		<emma enabled="${emma.enabled}" >
+			<report sourcepath="${buildDir}/${fullProdName}" >
+				<fileset dir="${emma.coverage.dir}" >
+					<include name="*.emma" />
+				</fileset>
+
+				<txt outfile="${emma.coverage.dir}/coverage.txt" />
+				<html outfile="${emma.coverage.dir}/coverage.html" />
+			</report>
+		</emma>
+	</target>
+
+	<target name="test-coverage-all-report" depends="test-coverage-multijvm, test-coverage-compliance-multijvm">
+		<emma enabled="${emma.enabled}" >
+			<report sourcepath="${buildDir}/${fullProdName}" >
+				<fileset dir="${emma.coverage.dir}" >
+					<include name="*.emma" />
+				</fileset>
+
+				<txt outfile="${emma.coverage.dir}/coverage.txt" />
+				<html outfile="${emma.coverage.dir}/coverage.html" />
+			</report>
+		</emma>
+	</target>
+
+
+	<target name="test-coverage" depends="instrument">
+		<property name="com.mysql.jdbc.testsuite.jvm" value="java"/>
+		<mkdir dir="${junit.results}"/>
+		<echo message="Running unit tests against ${com.mysql.jdbc.testsuite.url} with jvm ${com.mysql.jdbc.testsuite.jvm}"/>
+
+		<property name="junit.unitregress.results" value="${junit.results}/${test.result.prefix}/unitregress" />
+
+		<mkdir dir="${junit.results}/${test.result.prefix}"/>
+		<mkdir dir="${junit.unitregress.results}"/>
+
+		<mkdir dir="${junit.unitregress.results}/report"/>
+
+		<junit printSummary="yes" fork="on" jvm="${com.mysql.jdbc.testsuite.jvm}">
+			<jvmarg value="-Xmx256m" />
+			<jvmarg value="-Xverify:none" />
+			<!-- For java.sql.SavePoint on old JVMs -->
+			<jvmarg value="-Demma.coverage.out.file=${emma.coverage.dir}/coverage.emma" />
+			<jvmarg value="-Demma.coverage.out.merge=true" />
+			<sysproperty key="com.mysql.jdbc.testsuite.url" value="${com.mysql.jdbc.testsuite.url}"/>
+			<classpath>
+				<fileset dir="${sourceDir}/lib-nodist">
+					<include name="**/*.jar"/>
+				</fileset>
+				<fileset dir="${buildDir}/${fullProdName}/lib">
+					<include name="**/*.jar"/>
+				</fileset>
+				<pathelement location="${buildDir}/${fullProdName}" />
+				<pathelement path="${java.class.path}" />
+				<pathelement location="${emma.dir}/emma.jar" />
+				<pathelement location="${emma.dir}/emma_ant.jar" />
+			</classpath>
+
+			<formatter type="xml"/>
+
+			<batchtest fork="yes" todir="${junit.unitregress.results}">
+				<fileset dir="${buildDir}/${fullProdName}">
+					<include name="**/*Test.java" />
+					<exclude name="**/perf/*.java"/>
+				</fileset>
+			</batchtest>
+		</junit>
+
+
+		<junitreport todir="${junit.unitregress.results}/report">
+			<fileset dir="${junit.unitregress.results}">
+				<include name="**/TEST-*.xml"/>
+			</fileset>
+			<report format="frames" todir="${junit.unitregress.results}/report"/>
+		</junitreport>
+	</target>
+
+	<target name="test-coverage-compliance" depends="instrument">
+		<property name="com.mysql.jdbc.test.jvm" value="java"/>
+		<mkdir dir="${junit.results}"/>
+		<echo message="Running compliance tests against ${com.mysql.jdbc.compliance.url} with jvm ${com.mysql.jdbc.testsuite.jvm}"/>
+
+		<property name="junit.compliance.results" 
+			value="${junit.results}/${test.result.prefix}/compliance"/>
+
+		<mkdir dir="${junit.results}/${test.result.prefix}"/>
+		<mkdir dir="${junit.compliance.results}"/>
+		<mkdir dir="${junit.compliance.results}/report"/>
+
+		<junit printsummary="yes" jvm="${com.mysql.jdbc.testsuite.jvm}">
+			<jvmarg value="-Xmx256m"/>
+			<jvmarg value="-Xverify:none"/>
+			<!-- For java.sql.SavePoint on old JVMs -->
+			<jvmarg value="-Demma.coverage.out.file=${emma.coverage.dir}/coverage.emma" />
+			<jvmarg value="-Demma.coverage.out.merge=true" />
+			<sysproperty key="com.mysql.jdbc.compliance.url" value="${com.mysql.jdbc.compliance.url}"/>
+			<classpath>
+				<pathelement location="${buildDir}/${fullProdName}"/>
+				<fileset dir="${sourceDir}/lib-nodist">
+					<include name="**/*.jar"/>
+				</fileset>
+				<fileset dir="${buildDir}/${fullProdName}/lib">
+					<include name="**/*.jar"/>
+				</fileset>
+				<pathelement path="${java.class.path}" />
+				<pathelement location="${jdbc-compliance-location}"/>
+				<pathelement location="${emma.dir}/emma.jar" />
+				<pathelement location="${emma.dir}/emma_ant.jar" />
+			</classpath>
+
+			<formatter type="xml"/>
+
+
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.BatchUpdateTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.ConnectionTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.DatabaseMetaDataTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.EscapeSyntaxTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.PreparedStatementTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.ResultSetMetadataTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.ResultSetTest"/>
+			<test fork="yes" todir="${junit.compliance.results}" 
+		    		name="com.mysql.jdbc.compliance.tests.StatementTest"/>
+		</junit>
+
+		<junitreport todir="${junit.compliance.results}/report">
+			<fileset dir="${junit.compliance.results}">
+				<include name="**/TEST-*.xml"/>
+			</fileset>
+			<report format="frames" todir="${junit.compliance.results}/report"/>
+		</junitreport>
+	</target>
+
+	<!-- Makes output directory hierarchy based on MySQL server version,
+	     os information and jvm version -->
+
+	<macrodef name="make-output-hier">
+		<attribute name="jvm" />
+		<attribute name="jdbcUrl" />
+		<attribute name="unitOrCompliance" />
+		<attribute name="junitOutput" />
+		<sequential>
+			<java fork="true" jvm="@{jvm}" classname="com.mysql.jdbc.util.VersionFSHierarchyMaker">
+
+				<jvmarg value="-Xmx256m" />
+				<jvmarg value="-Xverify:none" />
+				<!-- For java.sql.SavePoint on old JVMs -->
+				<jvmarg value="-Demma.coverage.out.file=${emma.coverage.dir}/coverage.emma" />
+				<jvmarg value="-Demma.coverage.out.merge=true" />
+
+				<sysproperty key="com.mysql.jdbc.testsuite.url" value="@{jdbcUrl}" />
+
+				<classpath>
+					<fileset dir="${sourceDir}/lib-nodist">
+						<include name="**/*.jar" />
+					</fileset>
+					<fileset dir="${buildDir}/${fullProdName}/lib">
+						<include name="**/*.jar" />
+					</fileset>
+					<pathelement location="${buildDir}/${fullProdName}" />
+					<pathelement path="${java.class.path}" />
+					<pathelement location="${emma.dir}/emma.jar" />
+					<pathelement location="${emma.dir}/emma_ant.jar" />
+				</classpath>
+				<arg line="@{unitOrCompliance} @{junitOutput} @{junitOutput}/hier" />
+			</java>
+		</sequential>
+	</macrodef>
+
+	<!-- Runs compliance testsuite against multiple JVMs and 
+	     server configs, and produces coverage reports -->
+
+	<target name="test-coverage-multijvm" depends="instrument">
+		<for list="1,2,3,4,5,6,7,8" param="jvm.number">
+			<sequential>
+				<if>
+					<isset property="com.mysql.jdbc.testsuite.jvm.@{jvm.number}"/>
+					<then>
+						<for list="1,2,3,4,5,6,7,8" param="url.number">
+							<sequential>
+								<if>
+									<isset property="com.mysql.jdbc.testsuite.url.@{url.number}"/>
+									<then>
+										<make-output-hier 
+										jdbcUrl="${com.mysql.jdbc.testsuite.url.@{url.number}}" 
+										junitOutput="${junit.results}" 
+										jvm="${com.mysql.jdbc.testsuite.jvm.@{jvm.number}}" 
+										unitOrCompliance="unit" />
+
+										<property name="test.coverage.multi.junitOut.@{url.number}" value="eraseMe" />
+										<var name="test.coverage.multi.junitOut.@{url.number}" unset="true" />
+
+										<loadfile srcfile="${junit.results}/hier" 
+										property="test.coverage.multi.junitOut.@{url.number}" />
+
+										<antcall target="test-coverage">
+											<param name="com.mysql.jdbc.testsuite.url" value="${com.mysql.jdbc.testsuite.url.@{url.number}}" />
+											<param name="test.result.prefix" value="/${test.coverage.multi.junitOut.@{url.number}}" />
+											<param name="com.mysql.jdbc.testsuite.jvm" value="${com.mysql.jdbc.testsuite.jvm.@{jvm.number}}"/>
+										</antcall>
+									</then>
+								</if>
+							</sequential>
+						</for>
+					</then>
+				</if>
+			</sequential>
+		</for>
+	</target>
+
+	<!-- Runs compliance testsuite against multiple JVMs and server configs, collecting
+	       coverage data -->
+
+	<target name="test-coverage-compliance-multijvm" depends="instrument">
+		<for list="1,2,3,4,5,6,7,8" param="jvm.number"  xmlns:ac="antlib:net.sf.antcontrib">
+			<sequential>
+				<if>
+					<isset property="com.mysql.jdbc.testsuite.jvm.@{jvm.number}"/>
+					<then>
+						<for list="1,2,3,4,5,6,7,8" param="url.number">
+							<sequential>
+								<if>
+									<isset property="com.mysql.jdbc.compliance.url.@{url.number}"/>
+									<then>
+										<make-output-hier 
+											jdbcUrl="${com.mysql.jdbc.compliance.url.@{url.number}}" 
+											junitOutput="${junit.results}"
+											jvm="${com.mysql.jdbc.testsuite.jvm.@{jvm.number}}" 
+											unitOrCompliance="compliance"/>
+
+										<property name="test.coverage.compliance.multi.junitOut.@{url.number}" value="eraseMe" />
+										<var name="test.coverage.compliance.multi.junitOut.@{url.number}" unset="true" />
+
+										<loadfile srcfile="${junit.results}/hier" property="test.coverage.compliance.multi.junitOut.@{url.number}" />
+
+										<antcall target="test-coverage-compliance">
+											<param name="com.mysql.jdbc.compliance.url" value="${com.mysql.jdbc.compliance.url.@{url.number}}"/>
+											<param name="com.mysql.jdbc.testsuite.jvm" value="${com.mysql.jdbc.testsuite.jvm.@{jvm.number}}"/>
+											<param name="test.result.prefix" value="/${test.coverage.compliance.multi.junitOut.@{url.number}}"/>
+										</antcall>
+									</then>
+								</if>
+							</sequential>
+						</for>
+					</then>
+				</if>
+			</sequential>
+		</for>
+	</target>
+</project>

Added: branches/mysql-connector-java/upstream/5.0.4/docs/README
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/docs/README	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/docs/README	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,2933 @@
+1. MySQL Connector/J
+     ________________________________________________________
+
+   MySQL provides connectivity for client applications developed
+   in the Java programming language via a JDBC driver, which is
+   called MySQL Connector/J.
+
+   MySQL Connector/J is a JDBC-3.0 Type 4 driver, which means
+   that is pure Java, implements version 3.0 of the JDBC
+   specification, and communicates directly with the MySQL
+   server using the MySQL protocol.
+
+   Although JDBC is useful by itself, we would hope that if you
+   are not familiar with JDBC that after reading the first few
+   sections of this manual, that you would avoid using naked
+   JDBC for all but the most trivial problems and consider using
+   one of the popular persistence frameworks such as Hibernate
+   (http://www.hibernate.org/), Spring's JDBC templates
+   (http://www.springframework.org/) or Ibatis SQL Maps
+   (http://ibatis.apache.org/) to do the majority of repetitive
+   work and heavier lifting that is sometimes required with
+   JDBC.
+
+   This section is not designed to be a complete JDBC tutorial.
+   If you need more information about using JDBC you might be
+   interested in the following online tutorials that are more
+   in-depth than the information presented here:
+     * JDBC Basics
+       (http://java.sun.com/docs/books/tutorial/jdbc/basics/inde
+       x.html) --- A tutorial from Sun covering beginner topics
+       in JDBC
+     * JDBC Short Course
+       (http://java.sun.com/developer/onlineTraining/Database/JD
+       BCShortCourse/index.html) --- A more in-depth tutorial
+       from Sun and JGuru
+
+1.1. Connector/J Versions
+
+   There are currently three version of MySQL Connector/J
+   available:
+     * 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.
+     * 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
+   notes given where a setting applies to a specific option.
+
+1.1.1. Java Versions Supported
+
+   MySQL Connector/J supports Java-2 JVMs, including:
+     * JDK 1.2.x (only for Connector/J 3.1.x or earlier)
+     * JDK 1.3.x
+     * JDK 1.4.x
+     * JDK 1.5.x
+
+   If you are building Connector/J from source using the source
+   distribution (see Section A.2.4, "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
+
+   Because of the implementation of java.sql.Savepoint,
+   Connector/J 3.1.0 and newer will not run on JDKs older than
+   1.4 unless the class verifier is turned off (by setting the
+   -Xverify:none option to the Java runtime). This is because
+   the class verifier will try to load the class definition for
+   java.sql.Savepoint even though it is not accessed by the
+   driver unless you actually use savepoint functionality.
+
+   Caching functionality provided by Connector/J 3.1.0 or newer
+   is also not available on JVMs older than 1.4.x, as it relies
+   on java.util.LinkedHashMap which was first available in
+   JDK-1.4.0.
+
+1.2. Installing Connector/J
+
+   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
+
+1.2.1. Installing Connector/J from a Binary Distribution
+
+   The easiest method of installation is to use the binary
+   distribution of the Connector/J package. The binary
+   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
+   the Driver and Configuring the CLASSPATH").
+
+   MySQL Connector/J is distributed as a .zip or .tar.gz archive
+   containing the sources, the class files, and the JAR archive
+   named mysql-connector-java-[version]-bin.jar, and starting
+   with Connector/J 3.1.8 a debug build of the driver in a file
+   named mysql-connector-java-[version]-bin-g.jar.
+
+   Starting with Connector/J 3.1.9, the .class files that
+   constitute the JAR files are only included as part of the
+   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
+   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
+   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
+   need to use GNU tar (or an application that understands the
+   GNU tar archive format) to unpack the .tar.gz variant of the
+   distribution.
+
+1.2.2. Installing the Driver and Configuring the CLASSPATH
+
+   Once you have extracted the distribution archive, you can
+   install the driver by placing
+   mysql-connector-java-[version]-bin.jar in your classpath,
+   either by adding the full path to it to your CLASSPATH
+   environment variable, or by directly specifying it with the
+   command line switch -cp when starting your JVM.
+
+   If you are going to use the driver with the JDBC
+   DriverManager, you would use com.mysql.jdbc.Driver as the
+   class that implements java.sql.Driver.
+
+   You can set the CLASSPATH environment variableunder 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
+
+   Or with a Bourne-compatible shell (sh, ksh, bash):
+export set CLASSPATH=/path/to/mysql-connector-java-[version]-bin.jar:$
+CLASSPATH
+
+   Within Windows 2000, Windows XP and Windows Server 2003, you
+   must set the environment variable through the System control
+   panel.
+
+   If you want to use MySQL Connector/J with an application
+   server such as Tomcat or JBoss, you will have to read your
+   vendor's documentation for more information on how to
+   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
+   Frameworks." However, the authoritative source for JDBC
+   connection pool configuration information for your particular
+   application server is the documentation for that application
+   server.
+
+   If you are developing servlets or JSPs, and your application
+   server is J2EE-compliant, you can put the driver's .jar file
+   in the WEB-INF/lib subdirectory of your webapp, as this is a
+   standard location for third party class libraries in J2EE web
+   applications.
+
+   You can also use the MysqlDataSource or
+   MysqlConnectionPoolDataSource classes in the
+   com.mysql.jdbc.jdbc2.optional package, if your J2EE
+   application server supports or requires them. Starting with
+   Connector/J 5.0.0, the javax.sql.XADataSource interface is
+   implemented via the
+   com.mysql.jdbc.jdbc2.optional.MysqlXADataSource class, which
+   supports XA distributed transactions when used in combination
+   with MySQL server version 5.0.
+
+   The various MysqlDataSource classes support the following
+   parameters (through standard set mutators):
+     * user
+     * password
+     * serverName (see the previous section about fail-over
+       hosts)
+     * databaseName
+     * port
+
+1.2.3. Upgrading from an Older Version
+
+   MySQL AB tries to keep the upgrade process as easy as
+   possible, however as is the case with any software, sometimes
+   changes need to be made in new versions to support new
+   features, improve existing functionality, or comply with new
+   standards.
+
+   This section has information about what users who are
+   upgrading from one version of Connector/J to another (or to a
+   new version of the MySQL server, with respect to JDBC
+   functionality) should be aware of.
+
+1.2.3.1. Upgrading from MySQL Connector/J 3.0 to 3.1
+
+   Connector/J 3.1 is designed to be backward-compatible with
+   Connector/J 3.0 as much as possible. Major changes are
+   isolated to new functionality exposed in MySQL-4.1 and newer,
+   which includes Unicode character sets, server-side prepared
+   statements, SQLState codes returned in error messages by the
+   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
+       something misconfigured, it will usually show up as an
+       error with a message similar to Illegal mix of
+       collations.
+     * Server-side Prepared Statements --- Connector/J 3.1 will
+       automatically detect and use server-side prepared
+       statements when they are available (MySQL server version
+       4.1.0 and newer).
+       Starting with version 3.1.7, the driver scans SQL you are
+       preparing via all variants of
+       Connection.prepareStatement() to determine if it is a
+       supported type of statement to prepare on the server
+       side, and if it is not supported by the server, it
+       instead prepares it as a client-side emulated prepared
+       statement. You can disable this feature by passing
+       emulateUnsupportedPstmts=false in your JDBC URL.
+       If your application encounters issues with server-side
+       prepared statements, you can revert to the older
+       client-side emulated prepared statement code that is
+       still presently used for MySQL servers older than 4.1.0
+       with the connection property useServerPrepStmts=false
+     * Datetimes with all-zero components (0000-00-00 ...) ---
+       These values can not be represented reliably in Java.
+       Connector/J 3.0.x always converted them to NULL when
+       being read from a ResultSet.
+       Connector/J 3.1 throws an exception by default when these
+       values are encountered as this is the most correct
+       behavior according to the JDBC and SQL standards. This
+       behavior can be modified using the zeroDateTimeBehavior
+       configuration property. The allowable values are:
+          + exception (the default), which throws an
+            SQLException with an SQLState of S1009.
+          + convertToNull, which returns NULL instead of the
+            date.
+          + round, which rounds the date to the nearest closest
+            value which is 0001-01-01.
+       Starting with Connector/J 3.1.7, ResultSet.getString()
+       can be decoupled from this behavior via
+       noDatetimeStringSync=true (the default value is false) so
+       that you can get retrieve the unaltered all-zero value as
+       a String. It should be noted that this also precludes
+       using any time zone conversions, therefore the driver
+       will not allow you to enable noDatetimeStringSync and
+       useTimezone at the same time.
+     * New SQLState Codes --- Connector/J 3.1 uses SQL:1999
+       SQLState codes returned by the MySQL server (if
+       supported), which are different from the legacy X/Open
+       state codes that Connector/J 3.0 uses. If connected to a
+       MySQL server older than MySQL-4.1.0 (the oldest version
+       to return SQLStates as part of the error code), the
+       driver will use a built-in mapping. You can revert to the
+       old mapping by using the configuration property
+       useSqlStateCodes=false.
+     * ResultSet.getString() --- Calling ResultSet.getString()
+       on a BLOB column will now return the address of the
+       byte[] array that represents it, instead of a String
+       representation of the BLOB. BLOBs have no character set,
+       so they can't be converted to java.lang.Strings without
+       data loss or corruption.
+       To store strings in MySQL with LOB behavior, use one of
+       the TEXT types, which the driver will treat as a
+       java.sql.Clob.
+     * Debug builds --- Starting with Connector/J 3.1.8 a debug
+       build of the driver in a file named
+       mysql-connector-java-[version]-bin-g.jar is shipped
+       alongside the normal binary jar file that is named
+       mysql-connector-java-[version]-bin.jar.
+       Starting with Connector/J 3.1.9, we don't ship the .class
+       files unbundled, they are only available in the JAR
+       archives that ship with the driver.
+       You should not use the debug build of the driver unless
+       instructed to do so when reporting a problem or 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.
+
+1.2.3.2. JDBC-Specific Issues When Upgrading to MySQL Server 4.1 or
+Newer
+
+     * Using the UTF-8 Character Encoding - Prior to MySQL
+       server version 4.1, the UTF-8 character encoding was not
+       supported by the server, however the JDBC driver could
+       use it, allowing storage of multiple character sets in
+       latin1 tables on the server.
+       Starting with MySQL-4.1, this functionality is
+       deprecated. If you have applications that rely on this
+       functionality, and can not upgrade them to use the
+       official Unicode character support in MySQL server
+       version 4.1 or newer, you should add the following
+       property to your connection URL:
+       useOldUTF8Behavior=true
+     * Server-side Prepared Statements - Connector/J 3.1 will
+       automatically detect and use server-side prepared
+       statements when they are available (MySQL server version
+       4.1.0 and newer). If your application encounters issues
+       with server-side prepared statements, you can revert to
+       the older client-side emulated prepared statement code
+       that is still presently used for MySQL servers older than
+       4.1.0 with the following connection property:
+       useServerPrepStmts=false
+
+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.
+
+   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
+       (available from http://subversion.tigris.org/).
+     * Apache Ant version 1.6 or newer (available from
+       http://ant.apache.org/).
+     * JDK-1.4.2 or later. Although MySQL Connector/J can be
+       installed on older JDKs, to compile it from source you
+       must have at least JDK-1.4.2.
+
+   The Subversion source code repository for MySQL Connector/J
+   is located at http://svn.mysql.com/svnpublic/connector-j. In
+   general, you should not check out the entire repository
+   because it contains every branch and tag for MySQL
+   Connector/J and is quite large.
+
+   To check out and compile a specific branch of MySQL
+   Connector/J, follow these steps:
+    1. At the time of this writing, there are three active
+       branches of Connector/J: branch_3_0, branch_3_1 and
+       branch_5_0. Check out the latest code from the branch
+       that you want with the following command (replacing
+       [major] and [minor] with appropriate version numbers):
+shell> svn co �
+http://svn.mysql.com/svnpublic/connector-j/branches/branch_[major]_[mi
+nor]/connector-j
+       This creates a connector-j subdirectory in the current
+       directory that contains the latest sources for the
+       requested branch.
+    2. Change location to the connector-j directory to make it
+       your current working directory:
+shell> cd connector-j
+    3. Issue the following command to compile the driver and
+       create a .jar file suitable for installation:
+shell> ant dist
+       This creates a build directory in the current directory,
+       where all build output will go. A directory is created in
+       the build directory that includes the version number of
+       the sources you are building from. This directory
+       contains the sources, compiled .class files, and a .jar
+       file suitable for deployment. For other possible targets,
+       including ones that will create a fully packaged
+       distribution, issue the following command:
+shell> ant --projecthelp
+    4. A newly created .jar file containing the JDBC driver will
+       be placed in the directory
+       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."
+
+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
+       DriverManager"
+     * Section A.5.1.2, "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
+       parameters"
+     * Section A.5.1.3, "Retrieving results and output parameter
+       values"
+     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+       using Statement.getGeneratedKeys()"
+     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+       using SELECT LAST_INSERT_ID()"
+     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+       in Updatable ResultSets"
+     * Section A.5.2.1.1, "Using a connection pool with a J2EE
+       application server"
+     * Section A.5.3, "Example of transaction with retry logic"
+
+1.4. Connector/J (JDBC) Reference
+
+   This section of the manual contains reference material for
+   MySQL Connector/J, some of which is automatically generated
+   during the Connector/J build process.
+
+1.4.1. Driver/Datasource Class Names, URL Syntax and Configuration
+Properties for Connector/J
+
+   The name of the class that implements java.sql.Driver in
+   MySQL Connector/J is com.mysql.jdbc.Driver. The
+   org.gjt.mm.mysql.Driver class name is also usable to remain
+   backward-compatible with MM.MySQL. You should use this class
+   name when registering the driver, or when otherwise
+   configuring software to use MySQL Connector/J.
+
+   The JDBC URL format for MySQL Connector/J is as follows, with
+   items in square brackets ([, ]) being optional:
+jdbc:mysql://[host][,failoverhost...][:port]/[database] �
+[?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
+
+   If the hostname is not specified, it defaults to 127.0.0.1.
+   If the port is not specified, it defaults to 3306, the
+   default port number for MySQL servers.
+jdbc:mysql://[host:port],[host:port].../[database] �
+[?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
+
+   If the database is not specified, the connection will be made
+   with no default database. In this case, you will need to
+   either call the setCatalog() method on the Connection
+   instance or fully-specify table names using the database name
+   (i.e. SELECT dbname.tablename.colname FROM
+   dbname.tablename...) in your SQL. Not specifying the database
+   to use upon connection is generally only useful when building
+   tools that work with multiple databases, such as GUI database
+   managers.
+
+   MySQL Connector/J has fail-over support. This allows the
+   driver to fail-over to any number of slave hosts and still
+   perform read-only queries. Fail-over only happens when the
+   connection is in an autoCommit(true) state, because fail-over
+   can not happen reliably when a transaction is in progress.
+   Most application servers and connection pools set autoCommit
+   to true at the end of every transaction/connection use.
+
+   The fail-over functionality has the following behavior:
+     * If the URL property autoReconnect is false: Failover only
+       happens at connection initialization, and failback occurs
+       when the driver determines that the first host has become
+       available again.
+     * If the URL property autoReconnect is true: Failover
+       happens when the driver determines that the connection
+       has failed (before every query), and falls back to the
+       first host when it determines that the host has become
+       available again (after queriesBeforeRetryMaster queries
+       have been issued).
+
+   In either case, whenever you are connected to a "failed-over"
+   server, the connection will be set to read-only state, so
+   queries that would modify data will have exceptions thrown
+   (the query will never be processed by the MySQL server).
+
+   Configuration properties define how Connector/J will make a
+   connection to a MySQL server. Unless otherwise noted,
+   properties can be set for a DataSource object or for a
+   Connection object.
+
+   Configuration Properties can be set in one of the following
+   ways:
+     * Using the set*() methods on MySQL implementations of
+       java.sql.DataSource (which is the preferred method when
+       using implementations of java.sql.DataSource):
+          + com.mysql.jdbc.jdbc2.optional.MysqlDataSource
+          + com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDat
+            aSource
+     * As a key/value pair in the java.util.Properties instance
+       passed to DriverManager.getConnection() or
+       Driver.connect()
+     * As a JDBC URL parameter in the URL given to
+       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.
+
+   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
+   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.
+   com.mysql.jdbc.StandardSocketFactory 3.0.3
+   connectTimeout Timeout for socket connect (in milliseconds),
+   with 0 being no timeout. Only works on JDK-1.4 or newer.
+   Defaults to '0'. 0 3.0.1
+   socketTimeout Timeout on network socket operations (0, the
+   default means no timeout). 0 3.0.1
+   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
+   interactiveClient Set the CLIENT_INTERACTIVE flag, which
+   tells MySQL to timeout connections based on
+   INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT false 3.1.0
+   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
+   useCompression Use zlib compression when communicating with
+   the server (true/false)? Defaults to 'false'. false 3.0.17
+
+   High Availability and Clustering.
+   Property Name Definition Default Value Since Version
+   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'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. 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
+   reconnectAtTxEnd If autoReconnect is set to true, should the
+   driver attempt reconnectionsat the end of every transaction?
+   false 3.0.10
+   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
+   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. 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
+   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
+
+   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
+   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
+   allowUrlInLocalInfile Should the driver allow URLs in 'LOAD
+   DATA LOCAL INFILE' statements? false 3.1.4
+   paranoid Take measures to prevent exposure sensitive
+   information in error messages and clear data structures
+   holding sensitive data when possible? (defaults to 'false')
+   false 3.0.1
+
+   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
+   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
+   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
+   blobSendChunkSize Chunk to use when sending BLOB/CLOBs via
+   ServerPreparedStatements 1048576 3.1.9
+   cacheCallableStmts Should the driver cache the parsing stage
+   of CallableStatements false 3.1.2
+   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? false 3.0.10
+   cacheResultSetMetadata Should the driver cache
+   ResultSetMetaData for Statements and PreparedStatements?
+   (Req. JDK-1.4+, true/false, default 'false') false 3.1.1
+   cacheServerConfiguration Should the driver cache the results
+   of 'SHOW VARIABLES' and 'SHOW COLLATION' on a per-URL basis?
+   false 3.1.5
+   defaultFetchSize The driver will call setFetchSize(n) with
+   this value on all newly-created Statements 0 3.1.9
+   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. false 3.1.7
+   dynamicCalendars Should the driver retrieve the default
+   calendar when required, or cache it per connection/session?
+   false 3.1.5
+   elideSetAutoCommits 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)? false 3.1.3
+   holdResultsOpenOverStatementClose Should the driver close
+   result sets on Statement.close() as required by the JDBC
+   specification? false 3.1.7
+   locatorFetchBufferSize If 'emulateLocators' is configured to
+   'true', what size buffer should be used when fetching BLOB
+   data for getBinaryInputStream? 1048576 3.2.1
+   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 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. false 3.1.13
+   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
+   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
+   useReadAheadInput Use newer, optimized non-blocking, buffered
+   input stream when reading from the server? true 3.1.5
+
+   Debuging/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
+   logs to STDERR) com.mysql.jdbc.log.StandardLogger 3.1.1
+   profileSQL Trace queries and their execution/fetch times to
+   the configured logger (true/false) defaults to 'false' false
+   3.1.0
+   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
+   that will get logged when profiling or tracing 2048 3.1.3
+   packetDebugBufferSize The maximum number of packets to retain
+   when 'enablePacketDebug' is true 20 3.1.3
+   slowQueryThresholdMillis If 'logSlowQueries' is enabled, how
+   long should a query (in ms) before it is logged as 'slow'?
+   2000 3.1.2
+   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')?
+   false 3.1.1
+   autoGenerateTestcaseScript Should the driver dump the SQL it
+   is executing, including server-side prepared statements to
+   STDERR? false 3.1.9
+   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
+   dumpQueriesOnException Should the driver dump the contents of
+   the query sent to the server in the message for
+   SQLExceptions? false 3.1.3
+   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 false
+   3.1.3
+   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? false
+   3.1.2
+   logSlowQueries Should queries that take longer than
+   'slowQueryThresholdMillis' be logged? false 3.1.2
+   traceProtocol Should trace-level network protocol be logged?
+   false 3.1.2
+
+   Miscellaneous.
+   Property Name Definition Default Value Since Version
+   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' 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
+   characterSetResults Character set to tell the server to
+   return results as. 3.0.13
+   connectionCollation If set, tells the server to use this
+   collation via 'set collation_connection' 3.0.13
+   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
+   allowNanAndInf Should the driver allow NaN or +/- INF values
+   in PreparedStatement.setDouble()? false 3.1.5
+   autoClosePStmtStreams Should the driver automatically call
+   .close() on streams/readers passed as arguments via set*()
+   methods? false 3.1.12
+   autoDeserialize Should the driver automatically detect and
+   de-serialize objects stored in BLOB fields? false 3.1.5
+   capitalizeTypeNames Capitalize type names in
+   DatabaseMetaData? (usually only useful when using WebObjects,
+   true/false, defaults to 'false') false 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
+   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. false 3.0.9
+   continueBatchOnError Should the driver continue processing
+   batch commands if one statement fails. The JDBC spec allows
+   either way (defaults to 'true'). true 3.0.3
+   createDatabaseIfNotExist Creates the database given in the
+   URL if it doesn't yet exist. Assumes the configured user has
+   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
+   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
+   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
+   maxRows The maximum number of rows to return (0, the default
+   means return all rows). -1 all versions
+   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
+   noDatetimeStringSync Don't ensure that
+   ResultSet.getDatetimeType().toString().equals(ResultSet.getSt
+   ring()) false 3.1.7
+   noTimezoneConversionForTimeType Don't convert TIME values
+   using the server timezone if 'useTimezone'='true' false 5.0.0
+   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) true
+   3.1.8
+   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) true
+   3.1.8
+   overrideSupportsIntegrityEnhancementFacility 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)? false 3.1.12
+   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
+   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
+   to does not support transactions, still allow calls to
+   commit(), rollback() and setAutoCommit() (true/false,
+   defaults to 'false')? false 2.0.13
+   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. false 3.1.11
+   rollbackOnPooledClose Should the driver issue a rollback()
+   when the logical connection in a pool is closed? true 3.0.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
+   strictFloatingPoint Used only in older versions of compliance
+   test false 3.0.0
+   strictUpdates Should the driver do strict checking (all
+   primary keys selected) of updatable result sets (true, false,
+   defaults to 'true')? true 3.0.4
+   tinyInt1isBit Should the driver treat the datatype TINYINT(1)
+   as the BIT type (because the server silently converts BIT ->
+   TINYINT(1) when creating tables)? true 3.0.16
+   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? false 3.1.9
+   ultraDevHack Create PreparedStatements for prepareCall() when
+   required, because UltraDev is broken and issues a
+   prepareCall() for _all_ statements? (true/false, defaults to
+   'false') false 2.0.3
+   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. false 3.1.12
+   useHostsInPrivileges Add '@hostname' to users in
+   DatabaseMetaData.getColumn/TablePrivileges() (true/false),
+   defaults to 'true'. true 3.0.2
+   useInformationSchema When connected to MySQL-5.0.7 or newer,
+   should the driver use the INFORMATION_SCHEMA to derive
+   information used by DatabaseMetaData? false 5.0.0
+   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.)
+   false 5.0.0
+   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? true 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
+   useServerPrepStmts Use server-side prepared statements if the
+   server supports them? (defaults to 'true'). true 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
+   useStreamLengthsInPrepStmts Honor stream length parameter in
+   PreparedStatement/ResultSet.setXXXStream() method calls
+   (true/false, defaults to 'true')? true 3.0.2
+   useTimezone Convert time/date types between client and server
+   timezones (true/false, defaults to 'false')? false 3.0.2
+   useUnbufferedInput Don't use BufferedInputStream for reading
+   data from the server true 3.0.11
+   yearIsDateType Should the JDBC driver treat the MySQL type
+   "YEAR" as a java.sql.Date, or as a SHORT? true 3.1.9
+   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'.
+   exception 3.1.4
+
+   Connector/J also supports access to MySQL via named pipes on
+   Windows NT/2000/XP using the NamedPipeSocketFactory as a
+   plugin-socket factory via the socketFactory property. If you
+   don't use a namedPipePath property, the default of
+   '\\.\pipe\MySQL' will be used. If you use the
+   NamedPipeSocketFactory, the hostname and port number values
+   in the JDBC url will be ignored. You can enable this feature
+   using:
+socketFactory=com.mysql.jdbc.NamedPipeSocketFactory
+
+   Named pipes only work when connecting to a MySQL server on
+   the same physical machine as the one the JDBC driver is being
+   used on. In simple performance tests, it appears that named
+   pipe access is between 30%-50% faster than the standard
+   TCP/IP access.
+
+   You can create your own socket factories by following the
+   example code in com.mysql.jdbc.NamedPipeSocketFactory, or
+   com.mysql.jdbc.StandardSocketFactory.
+
+1.4.2. JDBC API Implementation Notes
+
+   MySQL Connector/J passes all of the tests in the
+   publicly-available version of Sun's JDBC compliance test
+   suite. However, in many places the JDBC specification is
+   vague about how certain functionality should be implemented,
+   or the specification allows leeway in implementation.
+
+   This section gives details on a interface-by-interface level
+   about how certain implementation decisions may affect how you
+   use MySQL Connector/J.
+     * Blob
+       The Blob implementation does not allow in-place
+       modification (they are copies, as reported by the
+       DatabaseMetaData.locatorsUpdateCopies() method). Because
+       of this, you should use the corresponding
+       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.
+     * CallableStatement
+       Starting with Connector/J 3.1.1, stored procedures are
+       supported when connecting to MySQL version 5.0 or newer
+       via the CallableStatement interface. Currently, the
+       getParameterMetaData() method of CallableStatement is not
+       supported.
+     * Clob
+       The Clob implementation does not allow in-place
+       modification (they are copies, as reported by the
+       DatabaseMetaData.locatorsUpdateCopies() method). Because
+       of this, you should use the PreparedStatement.setClob()
+       method to save changes back to the database. The JDBC API
+       does not have a ResultSet.updateClob() method.
+     * Connection
+       Unlike older versions of MM.MySQL the isClosed() method
+       does not ping the server to determine if it is alive. In
+       accordance with the JDBC specification, it only returns
+       true if closed() has been called on the connection. If
+       you need to determine if the connection is still valid,
+       you should issue a simple query, such as SELECT 1. The
+       driver will throw an exception if the connection is no
+       longer valid.
+     * DatabaseMetaData
+       Foreign Key information
+       (getImportedKeys()/getExportedKeys() and
+       getCrossReference()) is only available from InnoDB
+       tables. However, the driver uses SHOW CREATE TABLE to
+       retrieve this information, so when other storage engines
+       support foreign keys, the driver will transparently
+       support them as well.
+     * PreparedStatement
+       PreparedStatements are implemented by the driver, as
+       MySQL does not have a prepared statement feature. Because
+       of this, the driver does not implement
+       getParameterMetaData() or getMetaData() as it would
+       require the driver to have a complete SQL parser in the
+       client.
+       Starting with version 3.1.0 MySQL Connector/J,
+       server-side prepared statements and binary-encoded result
+       sets are used when the server supports them.
+       Take care when using a server-side prepared statement
+       with large parameters that are set via setBinaryStream(),
+       setAsciiStream(), setUnicodeStream(), setBlob(), or
+       setClob(). If you want to re-execute the statement with
+       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).
+          + 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.
+          + If a parameter changes from large to non-large, the
+            driver must reset the server-side state of the
+            prepared statement to allow the parameter that is
+            being changed to take the place of the prior large
+            value. This removes all of the large data that has
+            already been sent to the server, thus requiring the
+            data to be re-sent, via the setBinaryStream(),
+            setAsciiStream(), setUnicodeStream(), setBlob() or
+            setClob() methods.
+       Consequently, if you want to change the type of a
+       parameter to a non-large one, you must call
+       clearParameters() and set all parameters of the prepared
+       statement again before it can be re-executed.
+     * ResultSet
+       By default, ResultSets are completely retrieved and
+       stored in memory. In most cases this is the most
+       efficient way to operate, and due to the design of the
+       MySQL network protocol is easier to implement. If you are
+       working with ResultSets that have a large number of rows
+       or large values, and can not allocate heap space in your
+       JVM for the memory required, you can tell the driver to
+       stream the results back one row at a time.
+       To enable this functionality, you need to create a
+       Statement instance in the following manner:
+stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+              java.sql.ResultSet.CONCUR_READ_ONLY);
+stmt.setFetchSize(Integer.MIN_VALUE);
+       The combination of a forward-only, read-only result set,
+       with a fetch size of Integer.MIN_VALUE serves as a signal
+       to the driver to stream result sets row-by-row. After
+       this any result sets created with the statement will be
+       retrieved row-by-row.
+       There are some caveats with this approach. You will have
+       to read all of the rows in the result set (or close it)
+       before you can issue any other queries on the connection,
+       or an exception will be thrown.
+       The earliest the locks these statements hold can be
+       released (whether they be MyISAM table-level locks or
+       row-level locks in some other storage engine such as
+       InnoDB) is when the statement completes.
+       If the statement is within scope of a transaction, then
+       locks are released when the transaction completes (which
+       implies that the statement needs to complete first). As
+       with most other databases, statements are not complete
+       until all the results pending on the statement are read
+       or the active result set for the statement is closed.
+       Therefore, if using streaming results, you should process
+       them as quickly as possible if you want to maintain
+       concurrent access to the tables referenced by the
+       statement producing the result set.
+     * ResultSetMetaData
+       The isAutoIncrement() method only works when using MySQL
+       servers 4.0 and newer.
+     * 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
+       than to toggle result set streaming as described above.
+       MySQL does not support SQL cursors, and the JDBC driver
+       doesn't emulate them, so "setCursorName()" has no effect.
+
+1.4.3. Java, JDBC and MySQL Types
+
+   MySQL Connector/J is flexible in the way it handles
+   conversions between MySQL data types and Java data types.
+
+   In general, any MySQL data type can be converted to a
+   java.lang.String, and any numerical type can be converted to
+   any of the Java numerical types, although round-off,
+   overflow, or loss of precision may occur.
+
+   Starting with Connector/J 3.1.0, the JDBC driver will issue
+   warnings or throw DataTruncation exceptions as is required by
+   the JDBC specification unless the connection was configured
+   not to do so by using the property jdbcCompliantTruncation
+   and setting it to false.
+
+   The conversions that are always guaranteed to work are listed
+   in the following table:
+
+   Connection Properties - Miscellaneous.
+   These MySQL Data Types Can always be converted to these Java
+   types
+   CHAR, VARCHAR, BLOB, TEXT, ENUM, and SET java.lang.String,
+   java.io.InputStream, java.io.Reader, java.sql.Blob,
+   java.sql.Clob
+   FLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT,
+   SMALLINT, MEDIUMINT, INTEGER, BIGINT java.lang.String,
+   java.lang.Short, java.lang.Integer, java.lang.Long,
+   java.lang.Double, java.math.BigDecimal
+   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.
+
+   The ResultSet.getObject() method uses the type conversions
+   between MySQL and Java types, following the JDBC
+   specification where appropriate. The value returned by
+   ResultSetMetaData.GetColumnClassName() is also shown below.
+   For more information on the java.sql.Types classes see Java 2
+   Platform Types
+   (http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Types.html)
+   .
+
+   MySQL Types to Java Types for ResultSet.getObject().
+   MySQL Type Name Return value of GetColumnClassName Returned
+   as Java Class
+   BIT(1) (new in MySQL-5.0) BIT java.lang.Boolean
+   BIT( > 1) (new in MySQL-5.0) BIT byte[]
+   TINYINT TINYINT java.lang.Boolean if the configuration
+   property tinyInt1isBit is set to true (the default) and the
+   storage size is 1, or java.lang.Integer if not.
+   BOOL, BOOLEAN TINYINT See TINYINT, above as these are aliases
+   for TINYINT(1), currently.
+   SMALLINT[(M)] [UNSIGNED] SMALLINT [UNSIGNED]
+   java.lang.Integer (regardless if UNSIGNED or not)
+   MEDIUMINT[(M)] [UNSIGNED] MEDIUMINT [UNSIGNED]
+   java.lang.Integer, if UNSIGNED java.lang.Long
+   INT,INTEGER[(M)] [UNSIGNED] INTEGER [UNSIGNED]
+   java.lang.Integer, if UNSIGNED java.lang.Long
+   BIGINT[(M)] [UNSIGNED] BIGINT [UNSIGNED] java.lang.Long, if
+   UNSIGNED java.math.BigInteger
+   FLOAT[(M,D)] FLOAT java.lang.Float
+   DOUBLE[(M,B)] DOUBLE java.lang.Double
+   DECIMAL[(M[,D])] DECIMAL java.math.BigDecimal
+   DATE DATE java.sql.Date
+   DATETIME DATETIME java.sql.Timestamp
+   TIMESTAMP[(M)] TIMESTAMP java.sql.Timestamp
+   TIME TIME java.sql.Time
+   YEAR[(2|4)] YEAR If yearIsDateType configuration property is
+   set to false, then the returned object type is
+   java.sql.Short. If set to true (the default) then an object
+   of type java.sql.Date (with the date set to January 1st, at
+   midnight).
+   CHAR(M) CHAR java.lang.String (unless the character set for
+   the column is BINARY, then byte[] is returned.
+   VARCHAR(M) [BINARY] VARCHAR java.lang.String (unless the
+   character set for the column is BINARY, then byte[] is
+   returned.
+   BINARY(M) BINARY byte[]
+   VARBINARY(M) VARBINARY byte[]
+   TINYBLOB TINYBLOB byte[]
+   TINYTEXT VARCHAR java.lang.String
+   BLOB BLOB byte[]
+   TEXT VARCHAR java.lang.String
+   MEDIUMBLOB MEDIUMBLOB byte[]
+   MEDIUMTEXT VARCHAR java.lang.String
+   LONGBLOB LONGBLOB byte[]
+   LONGTEXT VARCHAR java.lang.String
+   ENUM('value1','value2',...) CHAR java.lang.String
+   SET('value1','value2',...) CHAR java.lang.String
+
+1.4.4. Using Character Sets and Unicode
+
+   All strings sent from the JDBC driver to the server are
+   converted automatically from native Java Unicode form to the
+   client character encoding, including all queries sent via
+   Statement.execute(), Statement.executeUpdate(),
+   Statement.executeQuery() as well as all PreparedStatement and
+   CallableStatement parameters with the exclusion of parameters
+   set using setBytes(), setBinaryStream(), setAsciiStream(),
+   setUnicodeStream() and setBlob() .
+
+   Prior to MySQL Server 4.1, Connector/J supported a single
+   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.
+
+   Starting with MySQL Server 4.1, Connector/J supports a single
+   character encoding between client and server, and any number
+   of character encodings for data returned by the server to the
+   client in ResultSets.
+
+   The character encoding between client and server is
+   automatically detected upon connection. The encoding used by
+   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)]
+
+   To override the automatically-detected encoding on the client
+   side, use the characterEncoding property in the URL used to
+   connect to the server.
+
+   When specifying character encodings on the client side,
+   Java-style names should be used. The following table lists
+   Java-style names for MySQL character sets:
+
+   MySQL to Java Encoding Name Translations.
+   MySQL Character Set Name Java-Style Character Encoding Name
+   ascii US-ASCII
+   big5 Big5
+   gbk GBK
+   sjis SJIS (or Cp932 or MS932 for MySQL Server < 4.1.11)
+   cp932 Cp932 or MS932 (MySQL Server > 4.1.11)
+   gb2312 EUC_CN
+   ujis EUC_JP
+   euckr EUC_KR
+   latin1 ISO8859_1
+   latin2 ISO8859_2
+   greek ISO8859_7
+   hebrew ISO8859_8
+   cp866 Cp866
+   tis620 TIS620
+   cp1250 Cp1250
+   cp1251 Cp1251
+   cp1257 Cp1257
+   macroman MacRoman
+   macce MacCentralEurope
+   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.
+
+   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
+   JDBC driver to use UTF-8 through the characterEncoding
+   property.
+
+1.4.5. Connecting Securely Using SSL
+
+   SSL in MySQL Connector/J encrypts all data (other than the
+   initial handshake) between the JDBC driver and the server.
+   The performance penalty for enabling SSL is an increase in
+   query processing time between 35% and 50%, depending on the
+   size of the query, and the amount of data it returns.
+
+   For SSL Support to work, you must have the following:
+     * A JDK that includes JSSE (Java Secure Sockets Extension),
+       like JDK-1.4.1 or newer. SSL does not currently work with
+       a JDK that you can add JSSE to, like JDK-1.2.x or
+       JDK-1.3.x due to the following JSSE bug:
+       http://developer.java.sun.com/developer/bugParade/bugs/42
+       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.
+     * A client certificate (covered later in this section)
+
+   You will first need to import the MySQL server CA Certificate
+   into a Java truststore. A sample MySQL server CA Certificate
+   is located in the SSL subdirectory of the MySQL source
+   distribution. This is what SSL will use to determine if you
+   are communicating with a secure MySQL server.
+
+   To use Java's keytool to create a truststore in the current
+   directory , and import the server's CA certificate
+   (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
+
+   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
+Serial number: 0
+Valid from: Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CD
+T 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
+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
+
+   Keytool will prompt you for the following information, and
+   create a keystore named keystore in the current directory.
+
+   You should respond with information that is appropriate for
+   your situation:
+Enter keystore password:  *********
+What is your first and last name?
+  [Unknown]:  Matthews
+What is the name of your organizational unit?
+  [Unknown]:  Software Development
+What is the name of your organization?
+  [Unknown]:  MySQL AB
+What is the name of your City or Locality?
+  [Unknown]:  Flossmoor
+What is the name of your State or Province?
+  [Unknown]:  IL
+What is the two-letter country code for this unit?
+  [Unknown]:  US
+Is <CN=Matthews, OU=Software Development, O=MySQL AB,
+ L=Flossmoor, ST=IL, C=US> correct?
+  [no]:  y
+
+Enter key password for <mysqlClientCertificate>
+        (RETURN if same as keystore password):
+
+   Finally, to get JSSE to use the keystore and truststore that
+   you have generated, you need to set the following system
+   properties when you start your JVM, replacing
+   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.
+-Djavax.net.ssl.keyStore=path_to_keystore_file
+-Djavax.net.ssl.keyStorePassword=*********
+-Djavax.net.ssl.trustStore=path_to_truststore_file
+-Djavax.net.ssl.trustStorePassword=*********
+
+   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
+   true in the java.util.Properties instance you pass to
+   DriverManager.getConnection().
+
+   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
+ ...
+
+   JSSE provides debugging (to STDOUT) when you set the
+   following system property: -Djavax.net.debug=all This will
+   tell you what keystores and truststores are being used, as
+   well as what is going on during the SSL handshake and
+   certificate exchange. It will be helpful when trying to
+   determine what is not working when trying to get an SSL
+   connection to happen.
+
+1.4.6. Using Master/Slave Replication with ReplicationConnection
+
+   Starting with Connector/J 3.1.7, we've made available a
+   variant of the driver that will automatically send queries to
+   a read/write master, or a failover or round-robin
+   loadbalanced set of slaves based on the state of
+   Connection.getReadOnly() .
+
+   An application signals that it wants a transaction to be
+   read-only by calling Connection.setReadOnly(true), this
+   replication-aware connection will use one of the slave
+   connections, which are load-balanced per-vm using a
+   round-robin scheme (a given connection is sticky to a slave
+   unless that slave is removed from service). If you have a
+   write transaction, or if you have a read that is
+   time-sensitive (remember, replication in MySQL is
+   asynchronous), set the connection to be not read-only, by
+   calling Connection.setReadOnly(false) and the driver will
+   ensure that further calls are sent to the master MySQL
+   server. The driver takes care of propagating the current
+   state of autocommit, isolation level, and catalog between all
+   of the connections that it uses to accomplish this load
+   balancing functionality.
+
+   To enable this functionality, use the "
+   com.mysql.jdbc.ReplicationDriver " class when configuring
+   your application server's connection pool or when creating an
+   instance of a JDBC driver for your standalone application.
+   Because it accepts the same URL format as the standard MySQL
+   JDBC driver, ReplicationDriver does not currently work with
+   java.sql.DriverManager -based connection creation unless it
+   is the only MySQL JDBC driver registered with the
+   DriverManager .
+
+   Here is a short, simple example of how ReplicationDriver
+   might be used in a standalone application.
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.util.Properties;
+
+import com.mysql.jdbc.ReplicationDriver;
+
+public class ReplicationDriverDemo {
+
+    public static void main(String[] args) throws Exception {
+        ReplicationDriver driver = new ReplicationDriver();
+
+        Properties props = new Properties();
+
+        // We want this for failover on the slaves
+        props.put("autoReconnect", "true");
+
+        // We want to load balance between the slaves
+        props.put("roundRobinLoadBalance", "true");
+
+        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
+        //
+
+        Connection conn =
+            driver.connect("jdbc:mysql://master,slave1,slave2,slave3/t
+est",
+                props);
+
+        //
+        // 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();
+
+        //
+        // Now, do a query from a slave, the driver automatically pick
+s one
+        // from the list
+        //
+
+        conn.setReadOnly(true);
+
+        ResultSet rs = conn.createStatement().executeQuery("SELECT a,b
+,c FROM some_other_table");
+
+         .......
+    }
+}
+
+1.5. Connector/J Notes and Tips
+
+1.5.1. Basic JDBC Concepts
+
+   This section provides some general JDBC background.
+
+1.5.1.1. Connecting to MySQL Using the DriverManager Interface
+
+   When you are using JDBC outside of an application server, the
+   DriverManager class manages the establishment of Connections.
+
+   The DriverManager needs to be told which JDBC drivers it
+   should try to make Connections with. The easiest way to do
+   this is to use Class.forName() on the class that implements
+   the java.sql.Driver interface. With MySQL Connector/J, the
+   name of this class is com.mysql.jdbc.Driver. With this
+   method, you could use an external configuration file to
+   supply the driver class name and driver parameters to use
+   when connecting to a database.
+
+   The following section of Java code shows how you might
+   register MySQL Connector/J from the main() method of your
+   application:
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+// Notice, do not import com.mysql.jdbc.*
+// or you will have problems!
+
+public class LoadDriver {
+    public static void main(String[] args) {
+        try {
+            // The newInstance() call is a work around for some
+            // broken Java implementations
+
+            Class.forName("com.mysql.jdbc.Driver").newInstance();
+        } catch (Exception ex) {
+            // handle the error
+        }
+}
+
+   After the driver has been registered with the DriverManager,
+   you can obtain a Connection instance that is connected to a
+   particular database by calling DriverManager.getConnection():
+
+   Example 1. Obtaining a connection from the DriverManager
+
+   This example shows how you can obtain a Connection instance
+   from the DriverManager. There are a few different signatures
+   for the getConnection() method. You should see the API
+   documentation that comes with your JDK for more specific
+   information on how to use them.
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+    ... try {
+            Connection conn = DriverManager.getConnection("jdbc:mysql:
+//localhost/test?user=monty&password=greatsqldb");
+
+            // 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());
+        }
+
+   Once a Connection is established, it can be used to create
+   Statement and PreparedStatement objects, as well as retrieve
+   metadata about the database. This is explained in the
+   following sections.
+
+1.5.1.2. Using Statements to Execute SQL
+
+   Statement objects allow you to execute basic SQL queries and
+   retrieve the results through the ResultSet class which is
+   described later.
+
+   To create a Statement instance, you call the
+   createStatement() method on the Connection object you have
+   retrieved via one of the DriverManager.getConnection() or
+   DataSource.getConnection() methods described earlier.
+
+   Once you have a Statement instance, you can execute a SELECT
+   query by calling the executeQuery(String) method with the SQL
+   you want to use.
+
+   To update data in the database, use the executeUpdate(String
+   SQL) method. This method returns the number of rows affected
+   by the update statement.
+
+   If you don't know ahead of time whether the SQL statement
+   will be a SELECT or an UPDATE/INSERT, then you can use the
+   execute(String SQL) method. This method will return true if
+   the SQL query was a SELECT, or false if it was an UPDATE,
+   INSERT, or DELETE statement. If the statement was a SELECT
+   query, you can retrieve the results by calling the
+   getResultSet() method. If the statement was an UPDATE,
+   INSERT, or DELETE statement, you can retrieve the affected
+   rows count by calling getUpdateCount() on the Statement
+   instance.
+
+   Example 2. Using java.sql.Statement to execute a SELECT query
+// assume that conn is an already created JDBC connection
+Statement stmt = null;
+ResultSet rs = null;
+
+try {
+    stmt = conn.createStatement();
+    rs = stmt.executeQuery("SELECT foo FROM bar");
+
+    // or alternatively, if you don't know ahead of time that
+    // the query will be a SELECT...
+
+    if (stmt.execute("SELECT foo FROM bar")) {
+        rs = stmt.getResultSet();
+    }
+
+    // Now do something with the ResultSet ....
+} finally {
+    // it is a good idea to release
+    // resources in a finally{} block
+    // in reverse-order of their creation
+    // if they are no-longer needed
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException sqlEx) { // ignore }
+
+        rs = null;
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException sqlEx) { // ignore }
+
+        stmt = null;
+    }
+}
+
+1.5.1.3. Using CallableStatements to Execute Stored Procedures
+
+   Starting with MySQL server version 5.0 when used with
+   Connector/J 3.1.1 or newer, the java.sql.CallableStatement
+   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.
+
+   Connector/J exposes stored procedure functionality through
+   JDBC's CallableStatement interface.
+
+   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.
+
+   The following example shows a stored procedure that returns
+   the value of inOutParam incremented by 1, and the string
+   passed in via inputParam as a ResultSet:
+
+   Example 3. Stored Procedures
+CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam I
+NT)
+BEGIN
+    DECLARE z INT;
+    SET z = inOutParam + 1;
+    SET inOutParam = z;
+
+    SELECT inputParam;
+
+    SELECT CONCAT('zyxw', inputParam);
+END
+
+   To use the demoSp procedure with Connector/J, follow these
+   steps:
+    1. Prepare the callable statement by using
+       Connection.prepareCall() .
+       Notice that you have to use JDBC escape syntax, and that
+       the parentheses surrounding the parameter placeholders
+       are not optional:
+       Example 4. Using Connection.prepareCall()
+import java.sql.CallableStatement;
+
+...
+
+    //
+    // Prepare a call to the stored procedure 'demoSp'
+    // with two parameters
+    //
+    // Notice the use of JDBC-escape syntax ({call ...})
+    //
+
+    CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}");
+
+
+
+    cStmt.setString(1, "abcdefg");
+       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
+       instances in your code.
+    2. Register the output parameters (if any exist)
+       To retrieve the values of output parameters (parameters
+       specified as OUT or INOUT when you created the stored
+       procedure), JDBC requires that they be specified before
+       statement execution using the various
+       registerOutputParameter() methods in the
+       CallableStatement interface:
+       Example 5. Registering output parameters
+import java.sql.Types;
+...
+//
+// Connector/J supports both named and indexed
+// output parameters. You can register output
+// parameters using either method, as well
+// as retrieve output parameters using either
+// method, regardless of what method was
+// used to register them.
+//
+// The following examples show how to use
+// the various methods of registering
+// output parameters (you should of course
+// use only one registration per parameter).
+//
+
+//
+// Registers the second parameter as output, and
+// uses the type 'INTEGER' for values returned from
+// getObject()
+//
+
+cStmt.registerOutParameter(2, Types.INTEGER);
+
+//
+// Registers the named parameter 'inOutParam', and
+// uses the type 'INTEGER' for values returned from
+// getObject()
+//
+
+cStmt.registerOutParameter("inOutParam", Types.INTEGER);
+...
+
+    3. Set the input parameters (if any exist)
+       Input and in/out parameters are set as for
+       PreparedStatement objects. However, CallableStatement
+       also supports setting parameters by name:
+       Example 6. Setting CallableStatement input parameters
+...
+
+    //
+    // Set a parameter by index
+    //
+
+    cStmt.setString(1, "abcdefg");
+
+    //
+    // Alternatively, set a parameter using
+    // the parameter name
+    //
+
+    cStmt.setString("inputParameter", "abcdefg");
+
+    //
+    // Set the 'in/out' parameter using an index
+    //
+
+    cStmt.setInt(2, 1);
+
+    //
+    // Alternatively, set the 'in/out' parameter
+    // by name
+    //
+
+    cStmt.setInt("inOutParam", 1);
+
+...
+    4. Execute the CallableStatement, and retrieve any result
+       sets or output parameters.
+       Although CallableStatement supports calling any of the
+       Statement execute methods (executeUpdate(),
+       executeQuery() or execute()), the most flexible method to
+       call is execute(), as you do not need to know ahead of
+       time if the stored procedure returns result sets:
+       Example 7. Retrieving results and output parameter values
+...
+
+    boolean hadResults = cStmt.execute();
+
+    //
+    // Process all returned result sets
+    //
+
+    while (hadResults) {
+        ResultSet rs = cStmt.getResultSet();
+
+        // process result set
+        ...
+
+        hadResults = rs.getMoreResults();
+    }
+
+    //
+    // Retrieve output parameters
+    //
+    // Connector/J supports both index-based and
+    // name-based retrieval
+    //
+
+    int outputValue = cStmt.getInt(2); // index-based
+
+    outputValue = cStmt.getInt("inOutParam"); // name-based
+
+...
+
+1.5.1.4. Retrieving AUTO_INCREMENT Column Values
+
+   Before version 3.0 of the JDBC API, there was no standard way
+   of retrieving key values from databases that supported auto
+   increment or identity columns. With older JDBC drivers for
+   MySQL, you could always use a MySQL-specific method on the
+   Statement interface, or issue the query SELECT
+   LAST_INSERT_ID() after issuing an INSERT to a table that had
+   an AUTO_INCREMENT key. Using the MySQL-specific method call
+   isn't portable, and issuing a SELECT to get the
+   AUTO_INCREMENT key's value requires another round-trip to the
+   database, which isn't as efficient as possible. The following
+   code snippets demonstrate the three different ways to
+   retrieve AUTO_INCREMENT values. First, we demonstrate the use
+   of the new JDBC-3.0 method getGeneratedKeys() which is now
+   the preferred method to use if you need to retrieve
+   AUTO_INCREMENT keys and have access to JDBC-3.0. The second
+   example shows how you can retrieve the same value using a
+   standard SELECT LAST_INSERT_ID() query. The final example
+   shows how updatable result sets can retrieve the
+   AUTO_INCREMENT value when using the insertRow() method.
+
+   Example 8. Retrieving AUTO_INCREMENT column values using
+   Statement.getGeneratedKeys()
+   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets assuming you have a
+    // Connection 'conn' to a MySQL database already
+    // available
+
+    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+                                java.sql.ResultSet.CONCUR_UPDATABLE);
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Insert one row that will generate an AUTO INCREMENT
+    // key in the 'priKey' field
+    //
+
+    stmt.executeUpdate(
+            "INSERT INTO autoIncTutorial (dataField) "
+            + "values ('Can I Get the Auto Increment Field?')",
+            Statement.RETURN_GENERATED_KEYS);
+
+    //
+    // Example of using Statement.getGeneratedKeys()
+    // to retrieve the value of an auto-increment
+    // value
+    //
+
+    int autoIncKeyFromApi = -1;
+
+    rs = stmt.getGeneratedKeys();
+
+    if (rs.next()) {
+        autoIncKeyFromApi = rs.getInt(1);
+    } else {
+
+        // throw an exception from here
+    }
+
+    rs.close();
+
+    rs = null;
+
+    System.out.println("Key returned from getGeneratedKeys():"
+        + autoIncKeyFromApi);
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+
+   Example 9. Retrieving AUTO_INCREMENT column values using
+   SELECT LAST_INSERT_ID()
+   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets.
+
+    stmt = conn.createStatement();
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Insert one row that will generate an AUTO INCREMENT
+    // key in the 'priKey' field
+    //
+
+    stmt.executeUpdate(
+            "INSERT INTO autoIncTutorial (dataField) "
+            + "values ('Can I Get the Auto Increment Field?')");
+
+    //
+    // Use the MySQL LAST_INSERT_ID()
+    // function to do the same thing as getGeneratedKeys()
+    //
+
+    int autoIncKeyFromFunc = -1;
+    rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
+
+    if (rs.next()) {
+        autoIncKeyFromFunc = rs.getInt(1);
+    } else {
+        // throw an exception from here
+    }
+
+    rs.close();
+
+    System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID(
+)': "
+        + autoIncKeyFromFunc);
+
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+
+   Example 10. Retrieving AUTO_INCREMENT column values in
+   Updatable ResultSets
+   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets as well as an 'updatable'
+    // one, assuming you have a Connection 'conn' to
+    // a MySQL database already available
+    //
+
+    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+                                java.sql.ResultSet.CONCUR_UPDATABLE);
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Example of retrieving an AUTO INCREMENT key
+    // from an updatable result set
+    //
+
+    rs = stmt.executeQuery("SELECT priKey, dataField "
+       + "FROM autoIncTutorial");
+
+    rs.moveToInsertRow();
+
+    rs.updateString("dataField", "AUTO INCREMENT here?");
+    rs.insertRow();
+
+    //
+    // the driver adds rows at the end
+    //
+
+    rs.last();
+
+    //
+    // We should now be on the row we just inserted
+    //
+
+    int autoIncKeyFromRS = rs.getInt("priKey");
+
+    rs.close();
+
+    rs = null;
+
+    System.out.println("Key returned for inserted row: "
+        + autoIncKeyFromRS);
+
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+
+
+
+   When you run the preceding example code, you should get the
+   following output: Key returned from getGeneratedKeys(): 1 Key
+   returned from SELECT LAST_INSERT_ID(): 1 Key returned for
+   inserted row: 2 You should be aware, that at times, it can be
+   tricky to use the SELECT LAST_INSERT_ID() query, as that
+   function's value is scoped to a connection. So, if some other
+   query happens on the same connection, the value will be
+   overwritten. On the other hand, the getGeneratedKeys() method
+   is scoped by the Statement instance, so it can be used even
+   if other queries happen on the same connection, but not on
+   the same Statement instance.
+
+1.5.2. Using Connector/J with J2EE and Other Java Frameworks
+
+   This section describes how to use Connector/J in several
+   contexts.
+
+1.5.2.1. General J2EE Concepts
+
+   This section provides general background on J2EE concepts
+   that pertain to use of Connector/J.
+
+1.5.2.1.1. Understanding Connection Pooling
+
+   Connection pooling is a technique of creating and managing a
+   pool of connections that are ready for use by any thread that
+   needs them.
+
+   This technique of pooling connections is based on the fact
+   that most applications only need a thread to have access to a
+   JDBC connection when they are actively processing a
+   transaction, which usually take only milliseconds to
+   complete. When not processing a transaction, the connection
+   would otherwise sit idle. Instead, connection pooling allows
+   the idle connection to be used by some other thread to do
+   useful work.
+
+   In practice, when a thread needs to do work against a MySQL
+   or other database with JDBC, it requests a connection from
+   the pool. When the thread is finished using the connection,
+   it returns it to the pool, so that it may be used by any
+   other threads that want to use it.
+
+   When the connection is loaned out from the pool, it is used
+   exclusively by the thread that requested it. From a
+   programming point of view, it is the same as if your thread
+   called DriverManager.getConnection() every time it needed a
+   JDBC connection, however with connection pooling, your thread
+   may end up using either a new, or already-existing
+   connection.
+
+   Connection pooling can greatly increase the performance of
+   your Java application, while reducing overall resource usage.
+   The main benefits to connection pooling are:
+     * Reduced connection creation time
+       Although this is not usually an issue with the quick
+       connection setup that MySQL offers compared to other
+       databases, creating new JDBC connections still incurs
+       networking and JDBC driver overhead that will be avoided
+       if connections are recycled.
+     * Simplified programming model
+       When using connection pooling, each individual thread can
+       act as though it has created its own JDBC connection,
+       allowing you to use straight-forward JDBC programming
+       techniques.
+     * Controlled resource usage
+       If you don't use connection pooling, and instead create a
+       new connection every time a thread needs one, your
+       application's resource usage can be quite wasteful and
+       lead to unpredictable behavior under load.
+
+   Remember that each connection to MySQL has overhead (memory,
+   CPU, context switches, and so forth) on both the client and
+   server side. Every connection limits how many resources there
+   are available to your application as well as the MySQL
+   server. Many of these resources will be used whether or not
+   the connection is actually doing any useful work!
+
+   Connection pools can be tuned to maximize performance, while
+   keeping resource utilization below the point where your
+   application will start to fail rather than just run slower.
+
+   Luckily, Sun has standardized the concept of connection
+   pooling in JDBC through the JDBC-2.0 Optional interfaces, and
+   all major application servers have implementations of these
+   APIs that work fine with MySQL Connector/J.
+
+   Generally, you configure a connection pool in your
+   application server configuration files, and access it via the
+   Java Naming and Directory Interface (JNDI). The following
+   code shows how you might use a connection pool from an
+   application deployed in a J2EE application server:
+
+   Example 11. Using a connection pool with a J2EE application
+   server
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
+
+
+public class MyServletJspOrEjb {
+
+    public void doSomething() throws Exception {
+        /*
+         * Create a JNDI Initial context to be able to
+         *  lookup  the DataSource
+         *
+         * In production-level code, this should be cached as
+         * an instance or static variable, as it can
+         * be quite expensive to create a JNDI context.
+         *
+         * Note: This code only works when you are using servlets
+         * or EJBs in a J2EE application server. If you are
+         * using connection pooling in standalone Java code, you
+         * will have to create/configure datasources using whatever
+         * mechanisms your particular connection pooling library
+         * provides.
+         */
+
+        InitialContext ctx = new InitialContext();
+
+         /*
+          * Lookup the DataSource, which will be backed by a pool
+          * that the application server provides. DataSource instances
+          * are also a good candidate for caching as an instance
+          * variable, as JNDI lookups can be expensive as well.
+          */
+
+        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MyS
+QLDB");
+
+        /*
+         * The following code is what would actually be in your
+         * Servlet, JSP or EJB 'service' method...where you need
+         * to work with a JDBC connection.
+         */
+
+        Connection conn = null;
+        Statement stmt = null;
+
+        try {
+            conn = ds.getConnection();
+
+            /*
+             * Now, use normal JDBC programming to work with
+             * MySQL, making sure to close each resource when you're
+             * finished with it, which allows the connection pool
+             * resources to be recovered as quickly as possible
+             */
+
+            stmt = conn.createStatement();
+            stmt.execute("SOME SQL QUERY");
+
+            stmt.close();
+            stmt = null;
+
+            conn.close();
+            conn = null;
+        } finally {
+            /*
+             * close any jdbc instances here that weren't
+             * explicitly closed during normal code path, so
+             * that we don't 'leak' resources...
+             */
+
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (sqlexception sqlex) {
+                    // ignore -- as we can't do anything about it here
+                }
+
+                stmt = null;
+            }
+
+            if (conn != null) {
+                try {
+                    conn.close();
+                } catch (sqlexception sqlex) {
+                    // ignore -- as we can't do anything about it here
+                }
+
+                conn = null;
+            }
+        }
+    }
+}
+
+   As shown in the example above, after obtaining the JNDI
+   InitialContext, and looking up the DataSource, the rest of
+   the code should look familiar to anyone who has done JDBC
+   programming in the past.
+
+   The most important thing to remember when using connection
+   pooling is to make sure that no matter what happens in your
+   code (exceptions, flow-of-control, and so forth),
+   connections, and anything created by them (such as statements
+   or result sets) are closed, so that they may be re-used,
+   otherwise they will be stranded, which in the best case means
+   that the MySQL server resources they represent (such as
+   buffers, locks, or sockets) may be tied up for some time, or
+   worst case, may be tied up forever.
+
+   What's the Best Size for my Connection Pool?
+
+   As with all other configuration rules-of-thumb, the answer
+   is: it depends. Although the optimal size depends on
+   anticipated load and average database transaction time, the
+   optimum connection pool size is smaller than you might
+   expect. If you take Sun's Java Petstore blueprint application
+   for example, a connection pool of 15-20 connections can serve
+   a relatively moderate load (600 concurrent users) using MySQL
+   and Tomcat with response times that are acceptable.
+
+   To correctly size a connection pool for your application, you
+   should create load test scripts with tools such as Apache
+   JMeter or The Grinder, and load test your application.
+
+   An easy way to determine a starting point is to configure
+   your connection pool's maximum number of connections to be
+   unbounded, run a load test, and measure the largest amount of
+   concurrently used connections. You can then work backward
+   from there to determine what values of minimum and maximum
+   pooled connections give the best performance for your
+   particular application.
+
+1.5.2.2. Using Connector/J with Tomcat
+
+   The following instructions are based on the instructions for
+   Tomcat-5.x, available at
+   http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jndi-datasour
+   ce-examples-howto.html which is current at the time this
+   document was written.
+
+   First, install the .jar file that comes with Connector/J in
+   $CATALINA_HOME/common/lib so that it is available to all
+   applications installed in the container.
+
+   Next, Configure the JNDI DataSource by adding a declaration
+   resource to $CATALINA_HOME/conf/server.xml in the context
+   that defines your web application:
+<Context ....>
+
+  ...
+
+  <Resource name="jdbc/MySQLDB"
+               auth="Container"
+               type="javax.sql.DataSource"/>
+
+  <!-- The name you used above, must match _exactly_ here!
+
+       The connection pool will be bound into JNDI with the name
+       "java:/comp/env/jdbc/MySQLDB"
+  -->
+
+  <ResourceParams name="jdbc/MySQLDB">
+    <parameter>
+      <name>factory</name>
+      <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
+    </parameter>
+
+    <!-- Don't set this any higher than max_connections on your
+         MySQL server, usually this should be a 10 or a few 10's
+         of connections, not hundreds or thousands -->
+
+    <parameter>
+      <name>maxActive</name>
+      <value>10</value>
+    </parameter>
+
+    <!-- You don't want to many idle connections hanging around
+         if you can avoid it, only enough to soak up a spike in
+         the load -->
+
+    <parameter>
+      <name>maxIdle</name>
+      <value>5</value>
+    </parameter>
+
+    <!-- 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
+         how much of a performance penalty you're willing to pay
+         to ensure 'freshness' of the connection -->
+
+    <parameter>
+      <name>validationQuery</name>
+      <value>SELECT 1</value>
+    </parameter>
+
+   <!-- The most conservative approach is to test connections
+        before they're given to your application. For most application
+s
+        this is okay, the query used above is very small and takes
+        no real server resources to process, other than the time used
+        to traverse the network.
+
+        If you have a high-load application you'll need to rely on
+        something else. -->
+
+    <parameter>
+      <name>testOnBorrow</name>
+      <value>true</value>
+    </parameter>
+
+   <!-- Otherwise, or in addition to testOnBorrow, you can test
+        while connections are sitting idle -->
+
+    <parameter>
+      <name>testWhileIdle</name>
+      <value>true</value>
+    </parameter>
+
+    <!-- You have to set this value, otherwise even though
+         you've asked connections to be tested while idle,
+         the idle evicter thread will never run -->
+
+    <parameter>
+      <name>timeBetweenEvictionRunsMillis</name>
+      <value>10000</value>
+    </parameter>
+
+    <!-- Don't allow connections to hang out idle too long,
+         never longer than what wait_timeout is set to on the
+         server...A few minutes or even fraction of a minute
+         is sometimes okay here, it depends on your application
+         and how much spikey load it will see -->
+
+    <parameter>
+      <name>minEvictableIdleTimeMillis</name>
+      <value>60000</value>
+    </parameter>
+
+    <!-- Username and password used when connecting to MySQL -->
+
+    <parameter>
+     <name>username</name>
+     <value>someuser</value>
+    </parameter>
+
+    <parameter>
+     <name>password</name>
+     <value>somepass</value>
+    </parameter>
+
+    <!-- Class name for the Connector/J driver -->
+
+    <parameter>
+       <name>driverClassName</name>
+       <value>com.mysql.jdbc.Driver</value>
+    </parameter>
+
+    <!-- The JDBC connection url for connecting to MySQL, notice
+         that if you want to pass any other MySQL-specific parameters
+         you should pass them here in the URL, setting them using the
+         parameter tags above will have no effect, you will also
+         need to use &amp; to separate parameter values as the
+         ampersand is a reserved character in XML -->
+
+    <parameter>
+      <name>url</name>
+      <value>jdbc:mysql://localhost:3306/test</value>
+    </parameter>
+
+  </ResourceParams>
+</Context>
+
+   In general, you should follow the installation instructions
+   that come with your version of Tomcat, as the way you
+   configure datasources in Tomcat changes from time-to-time,
+   and unfortunately if you use the wrong syntax in your XML
+   file, you will most likely end up with an exception similar
+   to the following:
+Error: java.sql.SQLException: Cannot load JDBC driver class 'null ' SQ
+L
+state: null
+
+1.5.2.3. Using Connector/J with JBoss
+
+   These instructions cover JBoss-4.x. To make the JDBC driver
+   classes available to the application server, copy the .jar
+   file that comes with Connector/J to the lib directory for
+   your server configuration (which is usually called default).
+   Then, in the same configuration directory, in the
+   subdirectory named deploy, create a datasource configuration
+   file that ends with "-ds.xml", which tells JBoss to deploy
+   this file as a JDBC Datasource. The file should have the
+   following contents:
+<datasources>
+    <local-tx-datasource>
+        <!-- This connection pool will be bound into JNDI with the nam
+e
+             "java:/MySQLDB" -->
+
+        <jndi-name>MySQLDB</jndi-name>
+        <connection-url>jdbc:mysql://localhost:3306/dbname</connection
+-url>
+        <driver-class>com.mysql.jdbc.Driver</driver-class>
+        <user-name>user</user-name>
+        <password>pass</password>
+
+        <min-pool-size>5</min-pool-size>
+
+        <!-- Don't set this any higher than max_connections on your
+         MySQL server, usually this should be a 10 or a few 10's
+         of connections, not hundreds or thousands -->
+
+        <max-pool-size>20</max-pool-size>
+
+        <!-- Don't allow connections to hang out idle too long,
+         never longer than what wait_timeout is set to on the
+         server...A few minutes is usually okay here,
+         it depends on your application
+         and how much spikey load it will see -->
+
+        <idle-timeout-minutes>5</idle-timeout-minutes>
+
+        <!-- If you're using Connector/J 3.1.8 or newer, you can use
+             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>
+
+    </local-tx-datasource>
+</datasources>
+
+1.5.3. Common Problems and Solutions
+
+   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.
+
+   Questions
+     * [1]1.5.3.1: When I try to connect to the database with
+       MySQL Connector/J, I get the following exception:
+SQLException: Server configuration denies access to data source
+SQLState: 08001
+VendorError: 0
+       What's going on? I can connect just fine with the MySQL
+       command-line client.
+     * [2]1.5.3.2: My application throws an SQLException 'No
+       Suitable Driver'. Why is this happening?
+     * [3]1.5.3.3: I'm trying to use MySQL Connector/J in an
+       applet or application and I get an exception similar to:
+SQLException: Cannot connect to MySQL server on host:3306.
+Is there a MySQL server running on the machine/port you
+are trying to connect to?
+
+(java.security.AccessControlException)
+SQLState: 08S01
+VendorError: 0
+     * [4]1.5.3.4: I have a servlet/application that works fine
+       for a day, and then stops working overnight
+     * [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.
+
+   Questions and Answers
+
+   1.5.3.1: When I try to connect to the database with MySQL
+   Connector/J, I get the following exception:
+SQLException: Server configuration denies access to data source
+SQLState: 08001
+VendorError: 0
+
+   What's going on? I can connect just fine with the MySQL
+   command-line client.
+
+   MySQL Connector/J must use TCP/IP sockets to connect to
+   MySQL, as Java does not support Unix Domain Sockets.
+   Therefore, when MySQL Connector/J connects to MySQL, the
+   security manager in MySQL server will use its grant tables to
+   determine whether the connection should be allowed.
+
+   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.
+
+   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.
+
+   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."
+     * The format of your connection URL is incorrect, or you
+       are referencing the wrong JDBC driver.
+     * When using DriverManager, the jdbc.drivers system
+       property has not been populated with the location of the
+       Connector/J driver.
+
+   1.5.3.3: I'm trying to use MySQL Connector/J in an applet or
+   application and I get an exception similar to:
+SQLException: Cannot connect to MySQL server on host:3306.
+Is there a MySQL server running on the machine/port you
+are trying to connect to?
+
+(java.security.AccessControlException)
+SQLState: 08S01
+VendorError: 0
+
+   Either you're running an Applet, your MySQL server has been
+   installed with the "--skip-networking" option set, or your
+   MySQL server has a firewall sitting in front of it.
+
+   Applets can only make network connections back to the machine
+   that runs the web server that served the .class files for the
+   applet. This means that MySQL must run on the same machine
+   (or you must have some sort of port re-direction) for this to
+   work. This also means that you will not be able to test
+   applets from your local file system, you must always deploy
+   them to a web server.
+
+   MySQL Connector/J can only communicate with MySQL using
+   TCP/IP, as Java does not support Unix domain sockets. TCP/IP
+   communication with MySQL might be affected if MySQL was
+   started with the "--skip-networking" flag, or if it is
+   firewalled.
+
+   If MySQL has been started with the "--skip-networking" option
+   set (the Debian Linux package of MySQL server does this for
+   example), you need to comment it out in the file
+   /etc/mysql/my.cnf or /etc/my.cnf. Of course your my.cnf file
+   might also exist in the data directory of your MySQL server,
+   or anywhere else (depending on how MySQL was compiled for
+   your system). Binaries created by MySQL AB always look in
+   /etc/my.cnf and [datadir]/my.cnf. If your MySQL server has
+   been firewalled, you will need to have the firewall
+   configured to allow TCP/IP connections from the host where
+   your Java code is running to the MySQL server on the port
+   that MySQL is listening to (by default, 3306).
+
+   1.5.3.4: I have a servlet/application that works fine for a
+   day, and then stops working overnight
+
+   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
+   Configuration Properties for Connector/J").
+
+   Also, you should be catching SQLExceptions in your
+   application and dealing with them, rather than propagating
+   them all the way until your application exits, this is just
+   good programming practice. MySQL Connector/J will set the
+   SQLState (see java.sql.SQLException.getSQLState() in your
+   APIDOCS) to "08S01" when it encounters network-connectivity
+   issues during the processing of a query. Your application
+   code should then attempt to re-connect to MySQL at this
+   point.
+
+   The following (simplistic) example shows what code that can
+   handle these exceptions might look like:
+
+   Example 12. Example of transaction with retry logic
+public void doBusinessOp() throws SQLException {
+        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;
+
+        boolean transactionCompleted = false;
+
+        do {
+            try {
+                conn = getConnection(); // assume getting this from a
+                                        // javax.sql.DataSource, or th
+e
+                                        // java.sql.DriverManager
+
+                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;
+
+                stmt = conn.createStatement();
+
+                String query = "SELECT foo FROM bar ORDER BY baz";
+
+                rs = stmt.executeQuery(query);
+
+                while (rs.next()) {
+                }
+
+                rs.close();
+                rs = null;
+
+                stmt.close();
+                stmt = null;
+
+                conn.commit();
+                conn.close();
+                conn = null;
+
+                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
+                //
+
+                String sqlState = sqlEx.getSQLState();
+
+                if ("08S01".equals(sqlState) || "40001".equals(sqlStat
+e)) {
+                    retryCount--;
+                } else {
+                    retryCount = 0;
+                }
+            } 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 (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;
+                    }
+                }
+            }
+        } 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.
+
+   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.
+
+   Because MySQL does not have row identifiers, MySQL
+   Connector/J can only update result sets that have come from
+   queries on tables that have at least one primary key, the
+   query must select every primary key and the query can only
+   span one table (that is, no joins). This is outlined in the
+   JDBC specification.
+
+   Note that this issue only occurs when using updatable result
+   sets, and is caused because Connector/J is unable to
+   guarantee that it can identify the correct rows within the
+   result set to be updated without having a unique reference to
+   each row. There is no requirement to have a unique field on a
+   table if you are using UPDATE or DELETE statements on a table
+   where you can individually specify the criteria to be matched
+   using a WHERE clause.
+
+1.6. Connector/J Support
+
+1.6.1. Connector/J Community Support
+
+   MySQL AB provides assistance to the user community by means
+   of its mailing lists. For Connector/J related issues, you can
+   get help from experienced users by using the MySQL and Java
+   mailing list. Archives and subscription information is
+   available online at http://lists.mysql.com/java.
+
+   For information about subscribing to MySQL mailing lists or
+   to browse list archives, visit http://lists.mysql.com/. See
+   MySQL Mailing Lists
+   (http://dev.mysql.com/doc/refman/5.1/en/mailing-lists.html).
+
+   Community support from experienced users is also available
+   through the JDBC Forum (http://forums.mysql.com/list.php?39).
+   You may also find help from other users in the other MySQL
+   Forums, located at http://forums.mysql.com. See MySQL
+   Community Support at the MySQL Forums
+   (http://dev.mysql.com/doc/refman/5.1/en/forums.html).
+
+1.6.2. How to Report Connector/J Bugs or Problems
+
+   The normal place to report bugs is http://bugs.mysql.com/,
+   which is the address for our bugs database. This database is
+   public, and can be browsed and searched by anyone. If you log
+   in to the system, you will also be able to enter new reports.
+
+   If you have found a sensitive security bug in MySQL, you can
+   send email to security_at_mysql.com
+   (mailto:security_at_mysql.com).
+
+   Writing a good bug report takes patience, but doing it right
+   the first time saves time both for us and for yourself. A
+   good bug report, containing a full test case for the bug,
+   makes it very likely that we will fix the bug in the next
+   release.
+
+   This section will help you write your report correctly so
+   that you don't waste your time doing things that may not help
+   us much or at all.
+
+   If you have a repeatable bug report, please report it to the
+   bugs database at http://bugs.mysql.com/. Any bug that we are
+   able to repeat has a high chance of being fixed in the next
+   MySQL release.
+
+   To report other problems, you can use one of the MySQL
+   mailing lists.
+
+   Remember that it is possible for us to respond to a message
+   containing too much information, but not to one containing
+   too little. People often omit facts because they think they
+   know the cause of a problem and assume that some details
+   don't matter.
+
+   A good principle is this: If you are in doubt about stating
+   something, state it. It is faster and less troublesome to
+   write a couple more lines in your report than to wait longer
+   for the answer if we must ask you to provide information that
+   was missing from the initial report.
+
+   The most common errors made in bug reports are (a) not
+   including the version number of Connector/J or MySQL used,
+   and (b) not fully describing the platform on which
+   Connector/J is installed (including the JVM version, and the
+   platform type and version number that MySQL itself is
+   installed on).
+
+   This is highly relevant information, and in 99 cases out of
+   100, the bug report is useless without it. Very often we get
+   questions like, "Why doesn't this work for me?" Then we find
+   that the feature requested wasn't implemented in that MySQL
+   version, or that a bug described in a report has already been
+   fixed in newer MySQL versions.
+
+   Sometimes the error is platform-dependent; in such cases, it
+   is next to impossible for us to fix anything without knowing
+   the operating system and the version number of the platform.
+
+   If at all possible, you should create a repeatable, stanalone
+   testcase that doesn't involve any third-party classes.
+
+   To streamline this process, we ship a base class for
+   testcases with Connector/J, named
+   'com.mysql.jdbc.util.BaseBugReport'. To create a testcase for
+   Connector/J using this class, create your own class that
+   inherits from com.mysql.jdbc.util.BaseBugReport and override
+   the methods setUp(), tearDown() and runTest().
+
+   In the setUp() method, create code that creates your tables,
+   and populates them with any data needed to demonstrate the
+   bug.
+
+   In the runTest() method, create code that demonstrates the
+   bug using the tables and data you created in the setUp
+   method.
+
+   In the tearDown() method, drop any tables you created in the
+   setUp() method.
+
+   In any of the above three methods, you should use one of the
+   variants of the getConnection() method to create a JDBC
+   connection to MySQL:
+     * getConnection() - Provides a connection to the JDBC URL
+       specified in getUrl(). If a connection already exists,
+       that connection is returned, otherwise a new connection
+       is created.
+     * getNewConnection() - Use this if you need to get a new
+       connection for your bug report (i.e. there's more than
+       one connection involved).
+     * getConnection(String url) - Returns a connection using
+       the given URL.
+     * getConnection(String url, Properties props) - Returns a
+       connection using the given URL and properties.
+
+   If you need to use a JDBC URL that is different from
+   'jdbc:mysql:///test', override the method getUrl() as well.
+
+   Use the assertTrue(boolean expression) and assertTrue(String
+   failureMessage, boolean expression) methods to create
+   conditions that must be met in your testcase demonstrating
+   the behavior you are expecting (vs. the behavior you are
+   observing, which is why you are most likely filing a bug
+   report).
+
+   Finally, create a main() method that creates a new instance
+   of your testcase, and calls the run method:
+public static void main(String[] args) throws Exception {
+      new MyBugReport().run();
+ }
+
+   Once you have finished your testcase, and have verified that
+   it demonstrates the bug you are reporting, upload it with
+   your bug report to http://bugs.mysql.com/.
+
+1.6.3. Connector/J Change History
+
+   The Connector/J Change History (Changelog) is located with
+   the main Changelog for MySQL. See MySQL Connector/J Change
+   History
+   (http://dev.mysql.com/doc/refman/5.1/en/cj-news.html).
+
+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

Added: branches/mysql-connector-java/upstream/5.0.4/docs/README.txt
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/docs/README.txt	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/docs/README.txt	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,2933 @@
+1. MySQL Connector/J
+     ________________________________________________________
+
+   MySQL provides connectivity for client applications developed
+   in the Java programming language via a JDBC driver, which is
+   called MySQL Connector/J.
+
+   MySQL Connector/J is a JDBC-3.0 Type 4 driver, which means
+   that is pure Java, implements version 3.0 of the JDBC
+   specification, and communicates directly with the MySQL
+   server using the MySQL protocol.
+
+   Although JDBC is useful by itself, we would hope that if you
+   are not familiar with JDBC that after reading the first few
+   sections of this manual, that you would avoid using naked
+   JDBC for all but the most trivial problems and consider using
+   one of the popular persistence frameworks such as Hibernate
+   (http://www.hibernate.org/), Spring's JDBC templates
+   (http://www.springframework.org/) or Ibatis SQL Maps
+   (http://ibatis.apache.org/) to do the majority of repetitive
+   work and heavier lifting that is sometimes required with
+   JDBC.
+
+   This section is not designed to be a complete JDBC tutorial.
+   If you need more information about using JDBC you might be
+   interested in the following online tutorials that are more
+   in-depth than the information presented here:
+     * JDBC Basics
+       (http://java.sun.com/docs/books/tutorial/jdbc/basics/inde
+       x.html) --- A tutorial from Sun covering beginner topics
+       in JDBC
+     * JDBC Short Course
+       (http://java.sun.com/developer/onlineTraining/Database/JD
+       BCShortCourse/index.html) --- A more in-depth tutorial
+       from Sun and JGuru
+
+1.1. Connector/J Versions
+
+   There are currently three version of MySQL Connector/J
+   available:
+     * 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.
+     * 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
+   notes given where a setting applies to a specific option.
+
+1.1.1. Java Versions Supported
+
+   MySQL Connector/J supports Java-2 JVMs, including:
+     * JDK 1.2.x (only for Connector/J 3.1.x or earlier)
+     * JDK 1.3.x
+     * JDK 1.4.x
+     * JDK 1.5.x
+
+   If you are building Connector/J from source using the source
+   distribution (see Section A.2.4, "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
+
+   Because of the implementation of java.sql.Savepoint,
+   Connector/J 3.1.0 and newer will not run on JDKs older than
+   1.4 unless the class verifier is turned off (by setting the
+   -Xverify:none option to the Java runtime). This is because
+   the class verifier will try to load the class definition for
+   java.sql.Savepoint even though it is not accessed by the
+   driver unless you actually use savepoint functionality.
+
+   Caching functionality provided by Connector/J 3.1.0 or newer
+   is also not available on JVMs older than 1.4.x, as it relies
+   on java.util.LinkedHashMap which was first available in
+   JDK-1.4.0.
+
+1.2. Installing Connector/J
+
+   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
+
+1.2.1. Installing Connector/J from a Binary Distribution
+
+   The easiest method of installation is to use the binary
+   distribution of the Connector/J package. The binary
+   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
+   the Driver and Configuring the CLASSPATH").
+
+   MySQL Connector/J is distributed as a .zip or .tar.gz archive
+   containing the sources, the class files, and the JAR archive
+   named mysql-connector-java-[version]-bin.jar, and starting
+   with Connector/J 3.1.8 a debug build of the driver in a file
+   named mysql-connector-java-[version]-bin-g.jar.
+
+   Starting with Connector/J 3.1.9, the .class files that
+   constitute the JAR files are only included as part of the
+   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
+   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
+   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
+   need to use GNU tar (or an application that understands the
+   GNU tar archive format) to unpack the .tar.gz variant of the
+   distribution.
+
+1.2.2. Installing the Driver and Configuring the CLASSPATH
+
+   Once you have extracted the distribution archive, you can
+   install the driver by placing
+   mysql-connector-java-[version]-bin.jar in your classpath,
+   either by adding the full path to it to your CLASSPATH
+   environment variable, or by directly specifying it with the
+   command line switch -cp when starting your JVM.
+
+   If you are going to use the driver with the JDBC
+   DriverManager, you would use com.mysql.jdbc.Driver as the
+   class that implements java.sql.Driver.
+
+   You can set the CLASSPATH environment variableunder 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
+
+   Or with a Bourne-compatible shell (sh, ksh, bash):
+export set CLASSPATH=/path/to/mysql-connector-java-[version]-bin.jar:$
+CLASSPATH
+
+   Within Windows 2000, Windows XP and Windows Server 2003, you
+   must set the environment variable through the System control
+   panel.
+
+   If you want to use MySQL Connector/J with an application
+   server such as Tomcat or JBoss, you will have to read your
+   vendor's documentation for more information on how to
+   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
+   Frameworks." However, the authoritative source for JDBC
+   connection pool configuration information for your particular
+   application server is the documentation for that application
+   server.
+
+   If you are developing servlets or JSPs, and your application
+   server is J2EE-compliant, you can put the driver's .jar file
+   in the WEB-INF/lib subdirectory of your webapp, as this is a
+   standard location for third party class libraries in J2EE web
+   applications.
+
+   You can also use the MysqlDataSource or
+   MysqlConnectionPoolDataSource classes in the
+   com.mysql.jdbc.jdbc2.optional package, if your J2EE
+   application server supports or requires them. Starting with
+   Connector/J 5.0.0, the javax.sql.XADataSource interface is
+   implemented via the
+   com.mysql.jdbc.jdbc2.optional.MysqlXADataSource class, which
+   supports XA distributed transactions when used in combination
+   with MySQL server version 5.0.
+
+   The various MysqlDataSource classes support the following
+   parameters (through standard set mutators):
+     * user
+     * password
+     * serverName (see the previous section about fail-over
+       hosts)
+     * databaseName
+     * port
+
+1.2.3. Upgrading from an Older Version
+
+   MySQL AB tries to keep the upgrade process as easy as
+   possible, however as is the case with any software, sometimes
+   changes need to be made in new versions to support new
+   features, improve existing functionality, or comply with new
+   standards.
+
+   This section has information about what users who are
+   upgrading from one version of Connector/J to another (or to a
+   new version of the MySQL server, with respect to JDBC
+   functionality) should be aware of.
+
+1.2.3.1. Upgrading from MySQL Connector/J 3.0 to 3.1
+
+   Connector/J 3.1 is designed to be backward-compatible with
+   Connector/J 3.0 as much as possible. Major changes are
+   isolated to new functionality exposed in MySQL-4.1 and newer,
+   which includes Unicode character sets, server-side prepared
+   statements, SQLState codes returned in error messages by the
+   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
+       something misconfigured, it will usually show up as an
+       error with a message similar to Illegal mix of
+       collations.
+     * Server-side Prepared Statements --- Connector/J 3.1 will
+       automatically detect and use server-side prepared
+       statements when they are available (MySQL server version
+       4.1.0 and newer).
+       Starting with version 3.1.7, the driver scans SQL you are
+       preparing via all variants of
+       Connection.prepareStatement() to determine if it is a
+       supported type of statement to prepare on the server
+       side, and if it is not supported by the server, it
+       instead prepares it as a client-side emulated prepared
+       statement. You can disable this feature by passing
+       emulateUnsupportedPstmts=false in your JDBC URL.
+       If your application encounters issues with server-side
+       prepared statements, you can revert to the older
+       client-side emulated prepared statement code that is
+       still presently used for MySQL servers older than 4.1.0
+       with the connection property useServerPrepStmts=false
+     * Datetimes with all-zero components (0000-00-00 ...) ---
+       These values can not be represented reliably in Java.
+       Connector/J 3.0.x always converted them to NULL when
+       being read from a ResultSet.
+       Connector/J 3.1 throws an exception by default when these
+       values are encountered as this is the most correct
+       behavior according to the JDBC and SQL standards. This
+       behavior can be modified using the zeroDateTimeBehavior
+       configuration property. The allowable values are:
+          + exception (the default), which throws an
+            SQLException with an SQLState of S1009.
+          + convertToNull, which returns NULL instead of the
+            date.
+          + round, which rounds the date to the nearest closest
+            value which is 0001-01-01.
+       Starting with Connector/J 3.1.7, ResultSet.getString()
+       can be decoupled from this behavior via
+       noDatetimeStringSync=true (the default value is false) so
+       that you can get retrieve the unaltered all-zero value as
+       a String. It should be noted that this also precludes
+       using any time zone conversions, therefore the driver
+       will not allow you to enable noDatetimeStringSync and
+       useTimezone at the same time.
+     * New SQLState Codes --- Connector/J 3.1 uses SQL:1999
+       SQLState codes returned by the MySQL server (if
+       supported), which are different from the legacy X/Open
+       state codes that Connector/J 3.0 uses. If connected to a
+       MySQL server older than MySQL-4.1.0 (the oldest version
+       to return SQLStates as part of the error code), the
+       driver will use a built-in mapping. You can revert to the
+       old mapping by using the configuration property
+       useSqlStateCodes=false.
+     * ResultSet.getString() --- Calling ResultSet.getString()
+       on a BLOB column will now return the address of the
+       byte[] array that represents it, instead of a String
+       representation of the BLOB. BLOBs have no character set,
+       so they can't be converted to java.lang.Strings without
+       data loss or corruption.
+       To store strings in MySQL with LOB behavior, use one of
+       the TEXT types, which the driver will treat as a
+       java.sql.Clob.
+     * Debug builds --- Starting with Connector/J 3.1.8 a debug
+       build of the driver in a file named
+       mysql-connector-java-[version]-bin-g.jar is shipped
+       alongside the normal binary jar file that is named
+       mysql-connector-java-[version]-bin.jar.
+       Starting with Connector/J 3.1.9, we don't ship the .class
+       files unbundled, they are only available in the JAR
+       archives that ship with the driver.
+       You should not use the debug build of the driver unless
+       instructed to do so when reporting a problem or 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.
+
+1.2.3.2. JDBC-Specific Issues When Upgrading to MySQL Server 4.1 or
+Newer
+
+     * Using the UTF-8 Character Encoding - Prior to MySQL
+       server version 4.1, the UTF-8 character encoding was not
+       supported by the server, however the JDBC driver could
+       use it, allowing storage of multiple character sets in
+       latin1 tables on the server.
+       Starting with MySQL-4.1, this functionality is
+       deprecated. If you have applications that rely on this
+       functionality, and can not upgrade them to use the
+       official Unicode character support in MySQL server
+       version 4.1 or newer, you should add the following
+       property to your connection URL:
+       useOldUTF8Behavior=true
+     * Server-side Prepared Statements - Connector/J 3.1 will
+       automatically detect and use server-side prepared
+       statements when they are available (MySQL server version
+       4.1.0 and newer). If your application encounters issues
+       with server-side prepared statements, you can revert to
+       the older client-side emulated prepared statement code
+       that is still presently used for MySQL servers older than
+       4.1.0 with the following connection property:
+       useServerPrepStmts=false
+
+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.
+
+   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
+       (available from http://subversion.tigris.org/).
+     * Apache Ant version 1.6 or newer (available from
+       http://ant.apache.org/).
+     * JDK-1.4.2 or later. Although MySQL Connector/J can be
+       installed on older JDKs, to compile it from source you
+       must have at least JDK-1.4.2.
+
+   The Subversion source code repository for MySQL Connector/J
+   is located at http://svn.mysql.com/svnpublic/connector-j. In
+   general, you should not check out the entire repository
+   because it contains every branch and tag for MySQL
+   Connector/J and is quite large.
+
+   To check out and compile a specific branch of MySQL
+   Connector/J, follow these steps:
+    1. At the time of this writing, there are three active
+       branches of Connector/J: branch_3_0, branch_3_1 and
+       branch_5_0. Check out the latest code from the branch
+       that you want with the following command (replacing
+       [major] and [minor] with appropriate version numbers):
+shell> svn co �
+http://svn.mysql.com/svnpublic/connector-j/branches/branch_[major]_[mi
+nor]/connector-j
+       This creates a connector-j subdirectory in the current
+       directory that contains the latest sources for the
+       requested branch.
+    2. Change location to the connector-j directory to make it
+       your current working directory:
+shell> cd connector-j
+    3. Issue the following command to compile the driver and
+       create a .jar file suitable for installation:
+shell> ant dist
+       This creates a build directory in the current directory,
+       where all build output will go. A directory is created in
+       the build directory that includes the version number of
+       the sources you are building from. This directory
+       contains the sources, compiled .class files, and a .jar
+       file suitable for deployment. For other possible targets,
+       including ones that will create a fully packaged
+       distribution, issue the following command:
+shell> ant --projecthelp
+    4. A newly created .jar file containing the JDBC driver will
+       be placed in the directory
+       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."
+
+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
+       DriverManager"
+     * Section A.5.1.2, "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
+       parameters"
+     * Section A.5.1.3, "Retrieving results and output parameter
+       values"
+     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+       using Statement.getGeneratedKeys()"
+     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+       using SELECT LAST_INSERT_ID()"
+     * Section A.5.1.4, "Retrieving AUTO_INCREMENT column values
+       in Updatable ResultSets"
+     * Section A.5.2.1.1, "Using a connection pool with a J2EE
+       application server"
+     * Section A.5.3, "Example of transaction with retry logic"
+
+1.4. Connector/J (JDBC) Reference
+
+   This section of the manual contains reference material for
+   MySQL Connector/J, some of which is automatically generated
+   during the Connector/J build process.
+
+1.4.1. Driver/Datasource Class Names, URL Syntax and Configuration
+Properties for Connector/J
+
+   The name of the class that implements java.sql.Driver in
+   MySQL Connector/J is com.mysql.jdbc.Driver. The
+   org.gjt.mm.mysql.Driver class name is also usable to remain
+   backward-compatible with MM.MySQL. You should use this class
+   name when registering the driver, or when otherwise
+   configuring software to use MySQL Connector/J.
+
+   The JDBC URL format for MySQL Connector/J is as follows, with
+   items in square brackets ([, ]) being optional:
+jdbc:mysql://[host][,failoverhost...][:port]/[database] �
+[?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
+
+   If the hostname is not specified, it defaults to 127.0.0.1.
+   If the port is not specified, it defaults to 3306, the
+   default port number for MySQL servers.
+jdbc:mysql://[host:port],[host:port].../[database] �
+[?propertyName1][=propertyValue1][&propertyName2][=propertyValue2]...
+
+   If the database is not specified, the connection will be made
+   with no default database. In this case, you will need to
+   either call the setCatalog() method on the Connection
+   instance or fully-specify table names using the database name
+   (i.e. SELECT dbname.tablename.colname FROM
+   dbname.tablename...) in your SQL. Not specifying the database
+   to use upon connection is generally only useful when building
+   tools that work with multiple databases, such as GUI database
+   managers.
+
+   MySQL Connector/J has fail-over support. This allows the
+   driver to fail-over to any number of slave hosts and still
+   perform read-only queries. Fail-over only happens when the
+   connection is in an autoCommit(true) state, because fail-over
+   can not happen reliably when a transaction is in progress.
+   Most application servers and connection pools set autoCommit
+   to true at the end of every transaction/connection use.
+
+   The fail-over functionality has the following behavior:
+     * If the URL property autoReconnect is false: Failover only
+       happens at connection initialization, and failback occurs
+       when the driver determines that the first host has become
+       available again.
+     * If the URL property autoReconnect is true: Failover
+       happens when the driver determines that the connection
+       has failed (before every query), and falls back to the
+       first host when it determines that the host has become
+       available again (after queriesBeforeRetryMaster queries
+       have been issued).
+
+   In either case, whenever you are connected to a "failed-over"
+   server, the connection will be set to read-only state, so
+   queries that would modify data will have exceptions thrown
+   (the query will never be processed by the MySQL server).
+
+   Configuration properties define how Connector/J will make a
+   connection to a MySQL server. Unless otherwise noted,
+   properties can be set for a DataSource object or for a
+   Connection object.
+
+   Configuration Properties can be set in one of the following
+   ways:
+     * Using the set*() methods on MySQL implementations of
+       java.sql.DataSource (which is the preferred method when
+       using implementations of java.sql.DataSource):
+          + com.mysql.jdbc.jdbc2.optional.MysqlDataSource
+          + com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDat
+            aSource
+     * As a key/value pair in the java.util.Properties instance
+       passed to DriverManager.getConnection() or
+       Driver.connect()
+     * As a JDBC URL parameter in the URL given to
+       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.
+
+   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
+   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.
+   com.mysql.jdbc.StandardSocketFactory 3.0.3
+   connectTimeout Timeout for socket connect (in milliseconds),
+   with 0 being no timeout. Only works on JDK-1.4 or newer.
+   Defaults to '0'. 0 3.0.1
+   socketTimeout Timeout on network socket operations (0, the
+   default means no timeout). 0 3.0.1
+   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
+   interactiveClient Set the CLIENT_INTERACTIVE flag, which
+   tells MySQL to timeout connections based on
+   INTERACTIVE_TIMEOUT instead of WAIT_TIMEOUT false 3.1.0
+   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
+   useCompression Use zlib compression when communicating with
+   the server (true/false)? Defaults to 'false'. false 3.0.17
+
+   High Availability and Clustering.
+   Property Name Definition Default Value Since Version
+   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'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. 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
+   reconnectAtTxEnd If autoReconnect is set to true, should the
+   driver attempt reconnectionsat the end of every transaction?
+   false 3.0.10
+   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
+   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. 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
+   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
+
+   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
+   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
+   allowUrlInLocalInfile Should the driver allow URLs in 'LOAD
+   DATA LOCAL INFILE' statements? false 3.1.4
+   paranoid Take measures to prevent exposure sensitive
+   information in error messages and clear data structures
+   holding sensitive data when possible? (defaults to 'false')
+   false 3.0.1
+
+   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
+   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
+   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
+   blobSendChunkSize Chunk to use when sending BLOB/CLOBs via
+   ServerPreparedStatements 1048576 3.1.9
+   cacheCallableStmts Should the driver cache the parsing stage
+   of CallableStatements false 3.1.2
+   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? false 3.0.10
+   cacheResultSetMetadata Should the driver cache
+   ResultSetMetaData for Statements and PreparedStatements?
+   (Req. JDK-1.4+, true/false, default 'false') false 3.1.1
+   cacheServerConfiguration Should the driver cache the results
+   of 'SHOW VARIABLES' and 'SHOW COLLATION' on a per-URL basis?
+   false 3.1.5
+   defaultFetchSize The driver will call setFetchSize(n) with
+   this value on all newly-created Statements 0 3.1.9
+   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. false 3.1.7
+   dynamicCalendars Should the driver retrieve the default
+   calendar when required, or cache it per connection/session?
+   false 3.1.5
+   elideSetAutoCommits 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)? false 3.1.3
+   holdResultsOpenOverStatementClose Should the driver close
+   result sets on Statement.close() as required by the JDBC
+   specification? false 3.1.7
+   locatorFetchBufferSize If 'emulateLocators' is configured to
+   'true', what size buffer should be used when fetching BLOB
+   data for getBinaryInputStream? 1048576 3.2.1
+   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 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. false 3.1.13
+   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
+   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
+   useReadAheadInput Use newer, optimized non-blocking, buffered
+   input stream when reading from the server? true 3.1.5
+
+   Debuging/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
+   logs to STDERR) com.mysql.jdbc.log.StandardLogger 3.1.1
+   profileSQL Trace queries and their execution/fetch times to
+   the configured logger (true/false) defaults to 'false' false
+   3.1.0
+   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
+   that will get logged when profiling or tracing 2048 3.1.3
+   packetDebugBufferSize The maximum number of packets to retain
+   when 'enablePacketDebug' is true 20 3.1.3
+   slowQueryThresholdMillis If 'logSlowQueries' is enabled, how
+   long should a query (in ms) before it is logged as 'slow'?
+   2000 3.1.2
+   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')?
+   false 3.1.1
+   autoGenerateTestcaseScript Should the driver dump the SQL it
+   is executing, including server-side prepared statements to
+   STDERR? false 3.1.9
+   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
+   dumpQueriesOnException Should the driver dump the contents of
+   the query sent to the server in the message for
+   SQLExceptions? false 3.1.3
+   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 false
+   3.1.3
+   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? false
+   3.1.2
+   logSlowQueries Should queries that take longer than
+   'slowQueryThresholdMillis' be logged? false 3.1.2
+   traceProtocol Should trace-level network protocol be logged?
+   false 3.1.2
+
+   Miscellaneous.
+   Property Name Definition Default Value Since Version
+   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' 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
+   characterSetResults Character set to tell the server to
+   return results as. 3.0.13
+   connectionCollation If set, tells the server to use this
+   collation via 'set collation_connection' 3.0.13
+   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
+   allowNanAndInf Should the driver allow NaN or +/- INF values
+   in PreparedStatement.setDouble()? false 3.1.5
+   autoClosePStmtStreams Should the driver automatically call
+   .close() on streams/readers passed as arguments via set*()
+   methods? false 3.1.12
+   autoDeserialize Should the driver automatically detect and
+   de-serialize objects stored in BLOB fields? false 3.1.5
+   capitalizeTypeNames Capitalize type names in
+   DatabaseMetaData? (usually only useful when using WebObjects,
+   true/false, defaults to 'false') false 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
+   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. false 3.0.9
+   continueBatchOnError Should the driver continue processing
+   batch commands if one statement fails. The JDBC spec allows
+   either way (defaults to 'true'). true 3.0.3
+   createDatabaseIfNotExist Creates the database given in the
+   URL if it doesn't yet exist. Assumes the configured user has
+   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
+   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
+   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
+   maxRows The maximum number of rows to return (0, the default
+   means return all rows). -1 all versions
+   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
+   noDatetimeStringSync Don't ensure that
+   ResultSet.getDatetimeType().toString().equals(ResultSet.getSt
+   ring()) false 3.1.7
+   noTimezoneConversionForTimeType Don't convert TIME values
+   using the server timezone if 'useTimezone'='true' false 5.0.0
+   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) true
+   3.1.8
+   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) true
+   3.1.8
+   overrideSupportsIntegrityEnhancementFacility 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)? false 3.1.12
+   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
+   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
+   to does not support transactions, still allow calls to
+   commit(), rollback() and setAutoCommit() (true/false,
+   defaults to 'false')? false 2.0.13
+   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. false 3.1.11
+   rollbackOnPooledClose Should the driver issue a rollback()
+   when the logical connection in a pool is closed? true 3.0.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
+   strictFloatingPoint Used only in older versions of compliance
+   test false 3.0.0
+   strictUpdates Should the driver do strict checking (all
+   primary keys selected) of updatable result sets (true, false,
+   defaults to 'true')? true 3.0.4
+   tinyInt1isBit Should the driver treat the datatype TINYINT(1)
+   as the BIT type (because the server silently converts BIT ->
+   TINYINT(1) when creating tables)? true 3.0.16
+   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? false 3.1.9
+   ultraDevHack Create PreparedStatements for prepareCall() when
+   required, because UltraDev is broken and issues a
+   prepareCall() for _all_ statements? (true/false, defaults to
+   'false') false 2.0.3
+   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. false 3.1.12
+   useHostsInPrivileges Add '@hostname' to users in
+   DatabaseMetaData.getColumn/TablePrivileges() (true/false),
+   defaults to 'true'. true 3.0.2
+   useInformationSchema When connected to MySQL-5.0.7 or newer,
+   should the driver use the INFORMATION_SCHEMA to derive
+   information used by DatabaseMetaData? false 5.0.0
+   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.)
+   false 5.0.0
+   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? true 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
+   useServerPrepStmts Use server-side prepared statements if the
+   server supports them? (defaults to 'true'). true 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
+   useStreamLengthsInPrepStmts Honor stream length parameter in
+   PreparedStatement/ResultSet.setXXXStream() method calls
+   (true/false, defaults to 'true')? true 3.0.2
+   useTimezone Convert time/date types between client and server
+   timezones (true/false, defaults to 'false')? false 3.0.2
+   useUnbufferedInput Don't use BufferedInputStream for reading
+   data from the server true 3.0.11
+   yearIsDateType Should the JDBC driver treat the MySQL type
+   "YEAR" as a java.sql.Date, or as a SHORT? true 3.1.9
+   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'.
+   exception 3.1.4
+
+   Connector/J also supports access to MySQL via named pipes on
+   Windows NT/2000/XP using the NamedPipeSocketFactory as a
+   plugin-socket factory via the socketFactory property. If you
+   don't use a namedPipePath property, the default of
+   '\\.\pipe\MySQL' will be used. If you use the
+   NamedPipeSocketFactory, the hostname and port number values
+   in the JDBC url will be ignored. You can enable this feature
+   using:
+socketFactory=com.mysql.jdbc.NamedPipeSocketFactory
+
+   Named pipes only work when connecting to a MySQL server on
+   the same physical machine as the one the JDBC driver is being
+   used on. In simple performance tests, it appears that named
+   pipe access is between 30%-50% faster than the standard
+   TCP/IP access.
+
+   You can create your own socket factories by following the
+   example code in com.mysql.jdbc.NamedPipeSocketFactory, or
+   com.mysql.jdbc.StandardSocketFactory.
+
+1.4.2. JDBC API Implementation Notes
+
+   MySQL Connector/J passes all of the tests in the
+   publicly-available version of Sun's JDBC compliance test
+   suite. However, in many places the JDBC specification is
+   vague about how certain functionality should be implemented,
+   or the specification allows leeway in implementation.
+
+   This section gives details on a interface-by-interface level
+   about how certain implementation decisions may affect how you
+   use MySQL Connector/J.
+     * Blob
+       The Blob implementation does not allow in-place
+       modification (they are copies, as reported by the
+       DatabaseMetaData.locatorsUpdateCopies() method). Because
+       of this, you should use the corresponding
+       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.
+     * CallableStatement
+       Starting with Connector/J 3.1.1, stored procedures are
+       supported when connecting to MySQL version 5.0 or newer
+       via the CallableStatement interface. Currently, the
+       getParameterMetaData() method of CallableStatement is not
+       supported.
+     * Clob
+       The Clob implementation does not allow in-place
+       modification (they are copies, as reported by the
+       DatabaseMetaData.locatorsUpdateCopies() method). Because
+       of this, you should use the PreparedStatement.setClob()
+       method to save changes back to the database. The JDBC API
+       does not have a ResultSet.updateClob() method.
+     * Connection
+       Unlike older versions of MM.MySQL the isClosed() method
+       does not ping the server to determine if it is alive. In
+       accordance with the JDBC specification, it only returns
+       true if closed() has been called on the connection. If
+       you need to determine if the connection is still valid,
+       you should issue a simple query, such as SELECT 1. The
+       driver will throw an exception if the connection is no
+       longer valid.
+     * DatabaseMetaData
+       Foreign Key information
+       (getImportedKeys()/getExportedKeys() and
+       getCrossReference()) is only available from InnoDB
+       tables. However, the driver uses SHOW CREATE TABLE to
+       retrieve this information, so when other storage engines
+       support foreign keys, the driver will transparently
+       support them as well.
+     * PreparedStatement
+       PreparedStatements are implemented by the driver, as
+       MySQL does not have a prepared statement feature. Because
+       of this, the driver does not implement
+       getParameterMetaData() or getMetaData() as it would
+       require the driver to have a complete SQL parser in the
+       client.
+       Starting with version 3.1.0 MySQL Connector/J,
+       server-side prepared statements and binary-encoded result
+       sets are used when the server supports them.
+       Take care when using a server-side prepared statement
+       with large parameters that are set via setBinaryStream(),
+       setAsciiStream(), setUnicodeStream(), setBlob(), or
+       setClob(). If you want to re-execute the statement with
+       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).
+          + 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.
+          + If a parameter changes from large to non-large, the
+            driver must reset the server-side state of the
+            prepared statement to allow the parameter that is
+            being changed to take the place of the prior large
+            value. This removes all of the large data that has
+            already been sent to the server, thus requiring the
+            data to be re-sent, via the setBinaryStream(),
+            setAsciiStream(), setUnicodeStream(), setBlob() or
+            setClob() methods.
+       Consequently, if you want to change the type of a
+       parameter to a non-large one, you must call
+       clearParameters() and set all parameters of the prepared
+       statement again before it can be re-executed.
+     * ResultSet
+       By default, ResultSets are completely retrieved and
+       stored in memory. In most cases this is the most
+       efficient way to operate, and due to the design of the
+       MySQL network protocol is easier to implement. If you are
+       working with ResultSets that have a large number of rows
+       or large values, and can not allocate heap space in your
+       JVM for the memory required, you can tell the driver to
+       stream the results back one row at a time.
+       To enable this functionality, you need to create a
+       Statement instance in the following manner:
+stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+              java.sql.ResultSet.CONCUR_READ_ONLY);
+stmt.setFetchSize(Integer.MIN_VALUE);
+       The combination of a forward-only, read-only result set,
+       with a fetch size of Integer.MIN_VALUE serves as a signal
+       to the driver to stream result sets row-by-row. After
+       this any result sets created with the statement will be
+       retrieved row-by-row.
+       There are some caveats with this approach. You will have
+       to read all of the rows in the result set (or close it)
+       before you can issue any other queries on the connection,
+       or an exception will be thrown.
+       The earliest the locks these statements hold can be
+       released (whether they be MyISAM table-level locks or
+       row-level locks in some other storage engine such as
+       InnoDB) is when the statement completes.
+       If the statement is within scope of a transaction, then
+       locks are released when the transaction completes (which
+       implies that the statement needs to complete first). As
+       with most other databases, statements are not complete
+       until all the results pending on the statement are read
+       or the active result set for the statement is closed.
+       Therefore, if using streaming results, you should process
+       them as quickly as possible if you want to maintain
+       concurrent access to the tables referenced by the
+       statement producing the result set.
+     * ResultSetMetaData
+       The isAutoIncrement() method only works when using MySQL
+       servers 4.0 and newer.
+     * 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
+       than to toggle result set streaming as described above.
+       MySQL does not support SQL cursors, and the JDBC driver
+       doesn't emulate them, so "setCursorName()" has no effect.
+
+1.4.3. Java, JDBC and MySQL Types
+
+   MySQL Connector/J is flexible in the way it handles
+   conversions between MySQL data types and Java data types.
+
+   In general, any MySQL data type can be converted to a
+   java.lang.String, and any numerical type can be converted to
+   any of the Java numerical types, although round-off,
+   overflow, or loss of precision may occur.
+
+   Starting with Connector/J 3.1.0, the JDBC driver will issue
+   warnings or throw DataTruncation exceptions as is required by
+   the JDBC specification unless the connection was configured
+   not to do so by using the property jdbcCompliantTruncation
+   and setting it to false.
+
+   The conversions that are always guaranteed to work are listed
+   in the following table:
+
+   Connection Properties - Miscellaneous.
+   These MySQL Data Types Can always be converted to these Java
+   types
+   CHAR, VARCHAR, BLOB, TEXT, ENUM, and SET java.lang.String,
+   java.io.InputStream, java.io.Reader, java.sql.Blob,
+   java.sql.Clob
+   FLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT,
+   SMALLINT, MEDIUMINT, INTEGER, BIGINT java.lang.String,
+   java.lang.Short, java.lang.Integer, java.lang.Long,
+   java.lang.Double, java.math.BigDecimal
+   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.
+
+   The ResultSet.getObject() method uses the type conversions
+   between MySQL and Java types, following the JDBC
+   specification where appropriate. The value returned by
+   ResultSetMetaData.GetColumnClassName() is also shown below.
+   For more information on the java.sql.Types classes see Java 2
+   Platform Types
+   (http://java.sun.com/j2se/1.4.2/docs/api/java/sql/Types.html)
+   .
+
+   MySQL Types to Java Types for ResultSet.getObject().
+   MySQL Type Name Return value of GetColumnClassName Returned
+   as Java Class
+   BIT(1) (new in MySQL-5.0) BIT java.lang.Boolean
+   BIT( > 1) (new in MySQL-5.0) BIT byte[]
+   TINYINT TINYINT java.lang.Boolean if the configuration
+   property tinyInt1isBit is set to true (the default) and the
+   storage size is 1, or java.lang.Integer if not.
+   BOOL, BOOLEAN TINYINT See TINYINT, above as these are aliases
+   for TINYINT(1), currently.
+   SMALLINT[(M)] [UNSIGNED] SMALLINT [UNSIGNED]
+   java.lang.Integer (regardless if UNSIGNED or not)
+   MEDIUMINT[(M)] [UNSIGNED] MEDIUMINT [UNSIGNED]
+   java.lang.Integer, if UNSIGNED java.lang.Long
+   INT,INTEGER[(M)] [UNSIGNED] INTEGER [UNSIGNED]
+   java.lang.Integer, if UNSIGNED java.lang.Long
+   BIGINT[(M)] [UNSIGNED] BIGINT [UNSIGNED] java.lang.Long, if
+   UNSIGNED java.math.BigInteger
+   FLOAT[(M,D)] FLOAT java.lang.Float
+   DOUBLE[(M,B)] DOUBLE java.lang.Double
+   DECIMAL[(M[,D])] DECIMAL java.math.BigDecimal
+   DATE DATE java.sql.Date
+   DATETIME DATETIME java.sql.Timestamp
+   TIMESTAMP[(M)] TIMESTAMP java.sql.Timestamp
+   TIME TIME java.sql.Time
+   YEAR[(2|4)] YEAR If yearIsDateType configuration property is
+   set to false, then the returned object type is
+   java.sql.Short. If set to true (the default) then an object
+   of type java.sql.Date (with the date set to January 1st, at
+   midnight).
+   CHAR(M) CHAR java.lang.String (unless the character set for
+   the column is BINARY, then byte[] is returned.
+   VARCHAR(M) [BINARY] VARCHAR java.lang.String (unless the
+   character set for the column is BINARY, then byte[] is
+   returned.
+   BINARY(M) BINARY byte[]
+   VARBINARY(M) VARBINARY byte[]
+   TINYBLOB TINYBLOB byte[]
+   TINYTEXT VARCHAR java.lang.String
+   BLOB BLOB byte[]
+   TEXT VARCHAR java.lang.String
+   MEDIUMBLOB MEDIUMBLOB byte[]
+   MEDIUMTEXT VARCHAR java.lang.String
+   LONGBLOB LONGBLOB byte[]
+   LONGTEXT VARCHAR java.lang.String
+   ENUM('value1','value2',...) CHAR java.lang.String
+   SET('value1','value2',...) CHAR java.lang.String
+
+1.4.4. Using Character Sets and Unicode
+
+   All strings sent from the JDBC driver to the server are
+   converted automatically from native Java Unicode form to the
+   client character encoding, including all queries sent via
+   Statement.execute(), Statement.executeUpdate(),
+   Statement.executeQuery() as well as all PreparedStatement and
+   CallableStatement parameters with the exclusion of parameters
+   set using setBytes(), setBinaryStream(), setAsciiStream(),
+   setUnicodeStream() and setBlob() .
+
+   Prior to MySQL Server 4.1, Connector/J supported a single
+   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.
+
+   Starting with MySQL Server 4.1, Connector/J supports a single
+   character encoding between client and server, and any number
+   of character encodings for data returned by the server to the
+   client in ResultSets.
+
+   The character encoding between client and server is
+   automatically detected upon connection. The encoding used by
+   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)]
+
+   To override the automatically-detected encoding on the client
+   side, use the characterEncoding property in the URL used to
+   connect to the server.
+
+   When specifying character encodings on the client side,
+   Java-style names should be used. The following table lists
+   Java-style names for MySQL character sets:
+
+   MySQL to Java Encoding Name Translations.
+   MySQL Character Set Name Java-Style Character Encoding Name
+   ascii US-ASCII
+   big5 Big5
+   gbk GBK
+   sjis SJIS (or Cp932 or MS932 for MySQL Server < 4.1.11)
+   cp932 Cp932 or MS932 (MySQL Server > 4.1.11)
+   gb2312 EUC_CN
+   ujis EUC_JP
+   euckr EUC_KR
+   latin1 ISO8859_1
+   latin2 ISO8859_2
+   greek ISO8859_7
+   hebrew ISO8859_8
+   cp866 Cp866
+   tis620 TIS620
+   cp1250 Cp1250
+   cp1251 Cp1251
+   cp1257 Cp1257
+   macroman MacRoman
+   macce MacCentralEurope
+   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.
+
+   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
+   JDBC driver to use UTF-8 through the characterEncoding
+   property.
+
+1.4.5. Connecting Securely Using SSL
+
+   SSL in MySQL Connector/J encrypts all data (other than the
+   initial handshake) between the JDBC driver and the server.
+   The performance penalty for enabling SSL is an increase in
+   query processing time between 35% and 50%, depending on the
+   size of the query, and the amount of data it returns.
+
+   For SSL Support to work, you must have the following:
+     * A JDK that includes JSSE (Java Secure Sockets Extension),
+       like JDK-1.4.1 or newer. SSL does not currently work with
+       a JDK that you can add JSSE to, like JDK-1.2.x or
+       JDK-1.3.x due to the following JSSE bug:
+       http://developer.java.sun.com/developer/bugParade/bugs/42
+       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.
+     * A client certificate (covered later in this section)
+
+   You will first need to import the MySQL server CA Certificate
+   into a Java truststore. A sample MySQL server CA Certificate
+   is located in the SSL subdirectory of the MySQL source
+   distribution. This is what SSL will use to determine if you
+   are communicating with a secure MySQL server.
+
+   To use Java's keytool to create a truststore in the current
+   directory , and import the server's CA certificate
+   (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
+
+   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
+Serial number: 0
+Valid from: Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CD
+T 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
+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
+
+   Keytool will prompt you for the following information, and
+   create a keystore named keystore in the current directory.
+
+   You should respond with information that is appropriate for
+   your situation:
+Enter keystore password:  *********
+What is your first and last name?
+  [Unknown]:  Matthews
+What is the name of your organizational unit?
+  [Unknown]:  Software Development
+What is the name of your organization?
+  [Unknown]:  MySQL AB
+What is the name of your City or Locality?
+  [Unknown]:  Flossmoor
+What is the name of your State or Province?
+  [Unknown]:  IL
+What is the two-letter country code for this unit?
+  [Unknown]:  US
+Is <CN=Matthews, OU=Software Development, O=MySQL AB,
+ L=Flossmoor, ST=IL, C=US> correct?
+  [no]:  y
+
+Enter key password for <mysqlClientCertificate>
+        (RETURN if same as keystore password):
+
+   Finally, to get JSSE to use the keystore and truststore that
+   you have generated, you need to set the following system
+   properties when you start your JVM, replacing
+   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.
+-Djavax.net.ssl.keyStore=path_to_keystore_file
+-Djavax.net.ssl.keyStorePassword=*********
+-Djavax.net.ssl.trustStore=path_to_truststore_file
+-Djavax.net.ssl.trustStorePassword=*********
+
+   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
+   true in the java.util.Properties instance you pass to
+   DriverManager.getConnection().
+
+   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
+ ...
+
+   JSSE provides debugging (to STDOUT) when you set the
+   following system property: -Djavax.net.debug=all This will
+   tell you what keystores and truststores are being used, as
+   well as what is going on during the SSL handshake and
+   certificate exchange. It will be helpful when trying to
+   determine what is not working when trying to get an SSL
+   connection to happen.
+
+1.4.6. Using Master/Slave Replication with ReplicationConnection
+
+   Starting with Connector/J 3.1.7, we've made available a
+   variant of the driver that will automatically send queries to
+   a read/write master, or a failover or round-robin
+   loadbalanced set of slaves based on the state of
+   Connection.getReadOnly() .
+
+   An application signals that it wants a transaction to be
+   read-only by calling Connection.setReadOnly(true), this
+   replication-aware connection will use one of the slave
+   connections, which are load-balanced per-vm using a
+   round-robin scheme (a given connection is sticky to a slave
+   unless that slave is removed from service). If you have a
+   write transaction, or if you have a read that is
+   time-sensitive (remember, replication in MySQL is
+   asynchronous), set the connection to be not read-only, by
+   calling Connection.setReadOnly(false) and the driver will
+   ensure that further calls are sent to the master MySQL
+   server. The driver takes care of propagating the current
+   state of autocommit, isolation level, and catalog between all
+   of the connections that it uses to accomplish this load
+   balancing functionality.
+
+   To enable this functionality, use the "
+   com.mysql.jdbc.ReplicationDriver " class when configuring
+   your application server's connection pool or when creating an
+   instance of a JDBC driver for your standalone application.
+   Because it accepts the same URL format as the standard MySQL
+   JDBC driver, ReplicationDriver does not currently work with
+   java.sql.DriverManager -based connection creation unless it
+   is the only MySQL JDBC driver registered with the
+   DriverManager .
+
+   Here is a short, simple example of how ReplicationDriver
+   might be used in a standalone application.
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.util.Properties;
+
+import com.mysql.jdbc.ReplicationDriver;
+
+public class ReplicationDriverDemo {
+
+    public static void main(String[] args) throws Exception {
+        ReplicationDriver driver = new ReplicationDriver();
+
+        Properties props = new Properties();
+
+        // We want this for failover on the slaves
+        props.put("autoReconnect", "true");
+
+        // We want to load balance between the slaves
+        props.put("roundRobinLoadBalance", "true");
+
+        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
+        //
+
+        Connection conn =
+            driver.connect("jdbc:mysql://master,slave1,slave2,slave3/t
+est",
+                props);
+
+        //
+        // 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();
+
+        //
+        // Now, do a query from a slave, the driver automatically pick
+s one
+        // from the list
+        //
+
+        conn.setReadOnly(true);
+
+        ResultSet rs = conn.createStatement().executeQuery("SELECT a,b
+,c FROM some_other_table");
+
+         .......
+    }
+}
+
+1.5. Connector/J Notes and Tips
+
+1.5.1. Basic JDBC Concepts
+
+   This section provides some general JDBC background.
+
+1.5.1.1. Connecting to MySQL Using the DriverManager Interface
+
+   When you are using JDBC outside of an application server, the
+   DriverManager class manages the establishment of Connections.
+
+   The DriverManager needs to be told which JDBC drivers it
+   should try to make Connections with. The easiest way to do
+   this is to use Class.forName() on the class that implements
+   the java.sql.Driver interface. With MySQL Connector/J, the
+   name of this class is com.mysql.jdbc.Driver. With this
+   method, you could use an external configuration file to
+   supply the driver class name and driver parameters to use
+   when connecting to a database.
+
+   The following section of Java code shows how you might
+   register MySQL Connector/J from the main() method of your
+   application:
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+// Notice, do not import com.mysql.jdbc.*
+// or you will have problems!
+
+public class LoadDriver {
+    public static void main(String[] args) {
+        try {
+            // The newInstance() call is a work around for some
+            // broken Java implementations
+
+            Class.forName("com.mysql.jdbc.Driver").newInstance();
+        } catch (Exception ex) {
+            // handle the error
+        }
+}
+
+   After the driver has been registered with the DriverManager,
+   you can obtain a Connection instance that is connected to a
+   particular database by calling DriverManager.getConnection():
+
+   Example 1. Obtaining a connection from the DriverManager
+
+   This example shows how you can obtain a Connection instance
+   from the DriverManager. There are a few different signatures
+   for the getConnection() method. You should see the API
+   documentation that comes with your JDK for more specific
+   information on how to use them.
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+    ... try {
+            Connection conn = DriverManager.getConnection("jdbc:mysql:
+//localhost/test?user=monty&password=greatsqldb");
+
+            // 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());
+        }
+
+   Once a Connection is established, it can be used to create
+   Statement and PreparedStatement objects, as well as retrieve
+   metadata about the database. This is explained in the
+   following sections.
+
+1.5.1.2. Using Statements to Execute SQL
+
+   Statement objects allow you to execute basic SQL queries and
+   retrieve the results through the ResultSet class which is
+   described later.
+
+   To create a Statement instance, you call the
+   createStatement() method on the Connection object you have
+   retrieved via one of the DriverManager.getConnection() or
+   DataSource.getConnection() methods described earlier.
+
+   Once you have a Statement instance, you can execute a SELECT
+   query by calling the executeQuery(String) method with the SQL
+   you want to use.
+
+   To update data in the database, use the executeUpdate(String
+   SQL) method. This method returns the number of rows affected
+   by the update statement.
+
+   If you don't know ahead of time whether the SQL statement
+   will be a SELECT or an UPDATE/INSERT, then you can use the
+   execute(String SQL) method. This method will return true if
+   the SQL query was a SELECT, or false if it was an UPDATE,
+   INSERT, or DELETE statement. If the statement was a SELECT
+   query, you can retrieve the results by calling the
+   getResultSet() method. If the statement was an UPDATE,
+   INSERT, or DELETE statement, you can retrieve the affected
+   rows count by calling getUpdateCount() on the Statement
+   instance.
+
+   Example 2. Using java.sql.Statement to execute a SELECT query
+// assume that conn is an already created JDBC connection
+Statement stmt = null;
+ResultSet rs = null;
+
+try {
+    stmt = conn.createStatement();
+    rs = stmt.executeQuery("SELECT foo FROM bar");
+
+    // or alternatively, if you don't know ahead of time that
+    // the query will be a SELECT...
+
+    if (stmt.execute("SELECT foo FROM bar")) {
+        rs = stmt.getResultSet();
+    }
+
+    // Now do something with the ResultSet ....
+} finally {
+    // it is a good idea to release
+    // resources in a finally{} block
+    // in reverse-order of their creation
+    // if they are no-longer needed
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException sqlEx) { // ignore }
+
+        rs = null;
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException sqlEx) { // ignore }
+
+        stmt = null;
+    }
+}
+
+1.5.1.3. Using CallableStatements to Execute Stored Procedures
+
+   Starting with MySQL server version 5.0 when used with
+   Connector/J 3.1.1 or newer, the java.sql.CallableStatement
+   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.
+
+   Connector/J exposes stored procedure functionality through
+   JDBC's CallableStatement interface.
+
+   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.
+
+   The following example shows a stored procedure that returns
+   the value of inOutParam incremented by 1, and the string
+   passed in via inputParam as a ResultSet:
+
+   Example 3. Stored Procedures
+CREATE PROCEDURE demoSp(IN inputParam VARCHAR(255), INOUT inOutParam I
+NT)
+BEGIN
+    DECLARE z INT;
+    SET z = inOutParam + 1;
+    SET inOutParam = z;
+
+    SELECT inputParam;
+
+    SELECT CONCAT('zyxw', inputParam);
+END
+
+   To use the demoSp procedure with Connector/J, follow these
+   steps:
+    1. Prepare the callable statement by using
+       Connection.prepareCall() .
+       Notice that you have to use JDBC escape syntax, and that
+       the parentheses surrounding the parameter placeholders
+       are not optional:
+       Example 4. Using Connection.prepareCall()
+import java.sql.CallableStatement;
+
+...
+
+    //
+    // Prepare a call to the stored procedure 'demoSp'
+    // with two parameters
+    //
+    // Notice the use of JDBC-escape syntax ({call ...})
+    //
+
+    CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}");
+
+
+
+    cStmt.setString(1, "abcdefg");
+       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
+       instances in your code.
+    2. Register the output parameters (if any exist)
+       To retrieve the values of output parameters (parameters
+       specified as OUT or INOUT when you created the stored
+       procedure), JDBC requires that they be specified before
+       statement execution using the various
+       registerOutputParameter() methods in the
+       CallableStatement interface:
+       Example 5. Registering output parameters
+import java.sql.Types;
+...
+//
+// Connector/J supports both named and indexed
+// output parameters. You can register output
+// parameters using either method, as well
+// as retrieve output parameters using either
+// method, regardless of what method was
+// used to register them.
+//
+// The following examples show how to use
+// the various methods of registering
+// output parameters (you should of course
+// use only one registration per parameter).
+//
+
+//
+// Registers the second parameter as output, and
+// uses the type 'INTEGER' for values returned from
+// getObject()
+//
+
+cStmt.registerOutParameter(2, Types.INTEGER);
+
+//
+// Registers the named parameter 'inOutParam', and
+// uses the type 'INTEGER' for values returned from
+// getObject()
+//
+
+cStmt.registerOutParameter("inOutParam", Types.INTEGER);
+...
+
+    3. Set the input parameters (if any exist)
+       Input and in/out parameters are set as for
+       PreparedStatement objects. However, CallableStatement
+       also supports setting parameters by name:
+       Example 6. Setting CallableStatement input parameters
+...
+
+    //
+    // Set a parameter by index
+    //
+
+    cStmt.setString(1, "abcdefg");
+
+    //
+    // Alternatively, set a parameter using
+    // the parameter name
+    //
+
+    cStmt.setString("inputParameter", "abcdefg");
+
+    //
+    // Set the 'in/out' parameter using an index
+    //
+
+    cStmt.setInt(2, 1);
+
+    //
+    // Alternatively, set the 'in/out' parameter
+    // by name
+    //
+
+    cStmt.setInt("inOutParam", 1);
+
+...
+    4. Execute the CallableStatement, and retrieve any result
+       sets or output parameters.
+       Although CallableStatement supports calling any of the
+       Statement execute methods (executeUpdate(),
+       executeQuery() or execute()), the most flexible method to
+       call is execute(), as you do not need to know ahead of
+       time if the stored procedure returns result sets:
+       Example 7. Retrieving results and output parameter values
+...
+
+    boolean hadResults = cStmt.execute();
+
+    //
+    // Process all returned result sets
+    //
+
+    while (hadResults) {
+        ResultSet rs = cStmt.getResultSet();
+
+        // process result set
+        ...
+
+        hadResults = rs.getMoreResults();
+    }
+
+    //
+    // Retrieve output parameters
+    //
+    // Connector/J supports both index-based and
+    // name-based retrieval
+    //
+
+    int outputValue = cStmt.getInt(2); // index-based
+
+    outputValue = cStmt.getInt("inOutParam"); // name-based
+
+...
+
+1.5.1.4. Retrieving AUTO_INCREMENT Column Values
+
+   Before version 3.0 of the JDBC API, there was no standard way
+   of retrieving key values from databases that supported auto
+   increment or identity columns. With older JDBC drivers for
+   MySQL, you could always use a MySQL-specific method on the
+   Statement interface, or issue the query SELECT
+   LAST_INSERT_ID() after issuing an INSERT to a table that had
+   an AUTO_INCREMENT key. Using the MySQL-specific method call
+   isn't portable, and issuing a SELECT to get the
+   AUTO_INCREMENT key's value requires another round-trip to the
+   database, which isn't as efficient as possible. The following
+   code snippets demonstrate the three different ways to
+   retrieve AUTO_INCREMENT values. First, we demonstrate the use
+   of the new JDBC-3.0 method getGeneratedKeys() which is now
+   the preferred method to use if you need to retrieve
+   AUTO_INCREMENT keys and have access to JDBC-3.0. The second
+   example shows how you can retrieve the same value using a
+   standard SELECT LAST_INSERT_ID() query. The final example
+   shows how updatable result sets can retrieve the
+   AUTO_INCREMENT value when using the insertRow() method.
+
+   Example 8. Retrieving AUTO_INCREMENT column values using
+   Statement.getGeneratedKeys()
+   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets assuming you have a
+    // Connection 'conn' to a MySQL database already
+    // available
+
+    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+                                java.sql.ResultSet.CONCUR_UPDATABLE);
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Insert one row that will generate an AUTO INCREMENT
+    // key in the 'priKey' field
+    //
+
+    stmt.executeUpdate(
+            "INSERT INTO autoIncTutorial (dataField) "
+            + "values ('Can I Get the Auto Increment Field?')",
+            Statement.RETURN_GENERATED_KEYS);
+
+    //
+    // Example of using Statement.getGeneratedKeys()
+    // to retrieve the value of an auto-increment
+    // value
+    //
+
+    int autoIncKeyFromApi = -1;
+
+    rs = stmt.getGeneratedKeys();
+
+    if (rs.next()) {
+        autoIncKeyFromApi = rs.getInt(1);
+    } else {
+
+        // throw an exception from here
+    }
+
+    rs.close();
+
+    rs = null;
+
+    System.out.println("Key returned from getGeneratedKeys():"
+        + autoIncKeyFromApi);
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+
+   Example 9. Retrieving AUTO_INCREMENT column values using
+   SELECT LAST_INSERT_ID()
+   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets.
+
+    stmt = conn.createStatement();
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Insert one row that will generate an AUTO INCREMENT
+    // key in the 'priKey' field
+    //
+
+    stmt.executeUpdate(
+            "INSERT INTO autoIncTutorial (dataField) "
+            + "values ('Can I Get the Auto Increment Field?')");
+
+    //
+    // Use the MySQL LAST_INSERT_ID()
+    // function to do the same thing as getGeneratedKeys()
+    //
+
+    int autoIncKeyFromFunc = -1;
+    rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
+
+    if (rs.next()) {
+        autoIncKeyFromFunc = rs.getInt(1);
+    } else {
+        // throw an exception from here
+    }
+
+    rs.close();
+
+    System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID(
+)': "
+        + autoIncKeyFromFunc);
+
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+
+   Example 10. Retrieving AUTO_INCREMENT column values in
+   Updatable ResultSets
+   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets as well as an 'updatable'
+    // one, assuming you have a Connection 'conn' to
+    // a MySQL database already available
+    //
+
+    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+                                java.sql.ResultSet.CONCUR_UPDATABLE);
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Example of retrieving an AUTO INCREMENT key
+    // from an updatable result set
+    //
+
+    rs = stmt.executeQuery("SELECT priKey, dataField "
+       + "FROM autoIncTutorial");
+
+    rs.moveToInsertRow();
+
+    rs.updateString("dataField", "AUTO INCREMENT here?");
+    rs.insertRow();
+
+    //
+    // the driver adds rows at the end
+    //
+
+    rs.last();
+
+    //
+    // We should now be on the row we just inserted
+    //
+
+    int autoIncKeyFromRS = rs.getInt("priKey");
+
+    rs.close();
+
+    rs = null;
+
+    System.out.println("Key returned for inserted row: "
+        + autoIncKeyFromRS);
+
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+
+
+
+   When you run the preceding example code, you should get the
+   following output: Key returned from getGeneratedKeys(): 1 Key
+   returned from SELECT LAST_INSERT_ID(): 1 Key returned for
+   inserted row: 2 You should be aware, that at times, it can be
+   tricky to use the SELECT LAST_INSERT_ID() query, as that
+   function's value is scoped to a connection. So, if some other
+   query happens on the same connection, the value will be
+   overwritten. On the other hand, the getGeneratedKeys() method
+   is scoped by the Statement instance, so it can be used even
+   if other queries happen on the same connection, but not on
+   the same Statement instance.
+
+1.5.2. Using Connector/J with J2EE and Other Java Frameworks
+
+   This section describes how to use Connector/J in several
+   contexts.
+
+1.5.2.1. General J2EE Concepts
+
+   This section provides general background on J2EE concepts
+   that pertain to use of Connector/J.
+
+1.5.2.1.1. Understanding Connection Pooling
+
+   Connection pooling is a technique of creating and managing a
+   pool of connections that are ready for use by any thread that
+   needs them.
+
+   This technique of pooling connections is based on the fact
+   that most applications only need a thread to have access to a
+   JDBC connection when they are actively processing a
+   transaction, which usually take only milliseconds to
+   complete. When not processing a transaction, the connection
+   would otherwise sit idle. Instead, connection pooling allows
+   the idle connection to be used by some other thread to do
+   useful work.
+
+   In practice, when a thread needs to do work against a MySQL
+   or other database with JDBC, it requests a connection from
+   the pool. When the thread is finished using the connection,
+   it returns it to the pool, so that it may be used by any
+   other threads that want to use it.
+
+   When the connection is loaned out from the pool, it is used
+   exclusively by the thread that requested it. From a
+   programming point of view, it is the same as if your thread
+   called DriverManager.getConnection() every time it needed a
+   JDBC connection, however with connection pooling, your thread
+   may end up using either a new, or already-existing
+   connection.
+
+   Connection pooling can greatly increase the performance of
+   your Java application, while reducing overall resource usage.
+   The main benefits to connection pooling are:
+     * Reduced connection creation time
+       Although this is not usually an issue with the quick
+       connection setup that MySQL offers compared to other
+       databases, creating new JDBC connections still incurs
+       networking and JDBC driver overhead that will be avoided
+       if connections are recycled.
+     * Simplified programming model
+       When using connection pooling, each individual thread can
+       act as though it has created its own JDBC connection,
+       allowing you to use straight-forward JDBC programming
+       techniques.
+     * Controlled resource usage
+       If you don't use connection pooling, and instead create a
+       new connection every time a thread needs one, your
+       application's resource usage can be quite wasteful and
+       lead to unpredictable behavior under load.
+
+   Remember that each connection to MySQL has overhead (memory,
+   CPU, context switches, and so forth) on both the client and
+   server side. Every connection limits how many resources there
+   are available to your application as well as the MySQL
+   server. Many of these resources will be used whether or not
+   the connection is actually doing any useful work!
+
+   Connection pools can be tuned to maximize performance, while
+   keeping resource utilization below the point where your
+   application will start to fail rather than just run slower.
+
+   Luckily, Sun has standardized the concept of connection
+   pooling in JDBC through the JDBC-2.0 Optional interfaces, and
+   all major application servers have implementations of these
+   APIs that work fine with MySQL Connector/J.
+
+   Generally, you configure a connection pool in your
+   application server configuration files, and access it via the
+   Java Naming and Directory Interface (JNDI). The following
+   code shows how you might use a connection pool from an
+   application deployed in a J2EE application server:
+
+   Example 11. Using a connection pool with a J2EE application
+   server
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
+
+
+public class MyServletJspOrEjb {
+
+    public void doSomething() throws Exception {
+        /*
+         * Create a JNDI Initial context to be able to
+         *  lookup  the DataSource
+         *
+         * In production-level code, this should be cached as
+         * an instance or static variable, as it can
+         * be quite expensive to create a JNDI context.
+         *
+         * Note: This code only works when you are using servlets
+         * or EJBs in a J2EE application server. If you are
+         * using connection pooling in standalone Java code, you
+         * will have to create/configure datasources using whatever
+         * mechanisms your particular connection pooling library
+         * provides.
+         */
+
+        InitialContext ctx = new InitialContext();
+
+         /*
+          * Lookup the DataSource, which will be backed by a pool
+          * that the application server provides. DataSource instances
+          * are also a good candidate for caching as an instance
+          * variable, as JNDI lookups can be expensive as well.
+          */
+
+        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MyS
+QLDB");
+
+        /*
+         * The following code is what would actually be in your
+         * Servlet, JSP or EJB 'service' method...where you need
+         * to work with a JDBC connection.
+         */
+
+        Connection conn = null;
+        Statement stmt = null;
+
+        try {
+            conn = ds.getConnection();
+
+            /*
+             * Now, use normal JDBC programming to work with
+             * MySQL, making sure to close each resource when you're
+             * finished with it, which allows the connection pool
+             * resources to be recovered as quickly as possible
+             */
+
+            stmt = conn.createStatement();
+            stmt.execute("SOME SQL QUERY");
+
+            stmt.close();
+            stmt = null;
+
+            conn.close();
+            conn = null;
+        } finally {
+            /*
+             * close any jdbc instances here that weren't
+             * explicitly closed during normal code path, so
+             * that we don't 'leak' resources...
+             */
+
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (sqlexception sqlex) {
+                    // ignore -- as we can't do anything about it here
+                }
+
+                stmt = null;
+            }
+
+            if (conn != null) {
+                try {
+                    conn.close();
+                } catch (sqlexception sqlex) {
+                    // ignore -- as we can't do anything about it here
+                }
+
+                conn = null;
+            }
+        }
+    }
+}
+
+   As shown in the example above, after obtaining the JNDI
+   InitialContext, and looking up the DataSource, the rest of
+   the code should look familiar to anyone who has done JDBC
+   programming in the past.
+
+   The most important thing to remember when using connection
+   pooling is to make sure that no matter what happens in your
+   code (exceptions, flow-of-control, and so forth),
+   connections, and anything created by them (such as statements
+   or result sets) are closed, so that they may be re-used,
+   otherwise they will be stranded, which in the best case means
+   that the MySQL server resources they represent (such as
+   buffers, locks, or sockets) may be tied up for some time, or
+   worst case, may be tied up forever.
+
+   What's the Best Size for my Connection Pool?
+
+   As with all other configuration rules-of-thumb, the answer
+   is: it depends. Although the optimal size depends on
+   anticipated load and average database transaction time, the
+   optimum connection pool size is smaller than you might
+   expect. If you take Sun's Java Petstore blueprint application
+   for example, a connection pool of 15-20 connections can serve
+   a relatively moderate load (600 concurrent users) using MySQL
+   and Tomcat with response times that are acceptable.
+
+   To correctly size a connection pool for your application, you
+   should create load test scripts with tools such as Apache
+   JMeter or The Grinder, and load test your application.
+
+   An easy way to determine a starting point is to configure
+   your connection pool's maximum number of connections to be
+   unbounded, run a load test, and measure the largest amount of
+   concurrently used connections. You can then work backward
+   from there to determine what values of minimum and maximum
+   pooled connections give the best performance for your
+   particular application.
+
+1.5.2.2. Using Connector/J with Tomcat
+
+   The following instructions are based on the instructions for
+   Tomcat-5.x, available at
+   http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jndi-datasour
+   ce-examples-howto.html which is current at the time this
+   document was written.
+
+   First, install the .jar file that comes with Connector/J in
+   $CATALINA_HOME/common/lib so that it is available to all
+   applications installed in the container.
+
+   Next, Configure the JNDI DataSource by adding a declaration
+   resource to $CATALINA_HOME/conf/server.xml in the context
+   that defines your web application:
+<Context ....>
+
+  ...
+
+  <Resource name="jdbc/MySQLDB"
+               auth="Container"
+               type="javax.sql.DataSource"/>
+
+  <!-- The name you used above, must match _exactly_ here!
+
+       The connection pool will be bound into JNDI with the name
+       "java:/comp/env/jdbc/MySQLDB"
+  -->
+
+  <ResourceParams name="jdbc/MySQLDB">
+    <parameter>
+      <name>factory</name>
+      <value>org.apache.commons.dbcp.BasicDataSourceFactory</value>
+    </parameter>
+
+    <!-- Don't set this any higher than max_connections on your
+         MySQL server, usually this should be a 10 or a few 10's
+         of connections, not hundreds or thousands -->
+
+    <parameter>
+      <name>maxActive</name>
+      <value>10</value>
+    </parameter>
+
+    <!-- You don't want to many idle connections hanging around
+         if you can avoid it, only enough to soak up a spike in
+         the load -->
+
+    <parameter>
+      <name>maxIdle</name>
+      <value>5</value>
+    </parameter>
+
+    <!-- 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
+         how much of a performance penalty you're willing to pay
+         to ensure 'freshness' of the connection -->
+
+    <parameter>
+      <name>validationQuery</name>
+      <value>SELECT 1</value>
+    </parameter>
+
+   <!-- The most conservative approach is to test connections
+        before they're given to your application. For most application
+s
+        this is okay, the query used above is very small and takes
+        no real server resources to process, other than the time used
+        to traverse the network.
+
+        If you have a high-load application you'll need to rely on
+        something else. -->
+
+    <parameter>
+      <name>testOnBorrow</name>
+      <value>true</value>
+    </parameter>
+
+   <!-- Otherwise, or in addition to testOnBorrow, you can test
+        while connections are sitting idle -->
+
+    <parameter>
+      <name>testWhileIdle</name>
+      <value>true</value>
+    </parameter>
+
+    <!-- You have to set this value, otherwise even though
+         you've asked connections to be tested while idle,
+         the idle evicter thread will never run -->
+
+    <parameter>
+      <name>timeBetweenEvictionRunsMillis</name>
+      <value>10000</value>
+    </parameter>
+
+    <!-- Don't allow connections to hang out idle too long,
+         never longer than what wait_timeout is set to on the
+         server...A few minutes or even fraction of a minute
+         is sometimes okay here, it depends on your application
+         and how much spikey load it will see -->
+
+    <parameter>
+      <name>minEvictableIdleTimeMillis</name>
+      <value>60000</value>
+    </parameter>
+
+    <!-- Username and password used when connecting to MySQL -->
+
+    <parameter>
+     <name>username</name>
+     <value>someuser</value>
+    </parameter>
+
+    <parameter>
+     <name>password</name>
+     <value>somepass</value>
+    </parameter>
+
+    <!-- Class name for the Connector/J driver -->
+
+    <parameter>
+       <name>driverClassName</name>
+       <value>com.mysql.jdbc.Driver</value>
+    </parameter>
+
+    <!-- The JDBC connection url for connecting to MySQL, notice
+         that if you want to pass any other MySQL-specific parameters
+         you should pass them here in the URL, setting them using the
+         parameter tags above will have no effect, you will also
+         need to use &amp; to separate parameter values as the
+         ampersand is a reserved character in XML -->
+
+    <parameter>
+      <name>url</name>
+      <value>jdbc:mysql://localhost:3306/test</value>
+    </parameter>
+
+  </ResourceParams>
+</Context>
+
+   In general, you should follow the installation instructions
+   that come with your version of Tomcat, as the way you
+   configure datasources in Tomcat changes from time-to-time,
+   and unfortunately if you use the wrong syntax in your XML
+   file, you will most likely end up with an exception similar
+   to the following:
+Error: java.sql.SQLException: Cannot load JDBC driver class 'null ' SQ
+L
+state: null
+
+1.5.2.3. Using Connector/J with JBoss
+
+   These instructions cover JBoss-4.x. To make the JDBC driver
+   classes available to the application server, copy the .jar
+   file that comes with Connector/J to the lib directory for
+   your server configuration (which is usually called default).
+   Then, in the same configuration directory, in the
+   subdirectory named deploy, create a datasource configuration
+   file that ends with "-ds.xml", which tells JBoss to deploy
+   this file as a JDBC Datasource. The file should have the
+   following contents:
+<datasources>
+    <local-tx-datasource>
+        <!-- This connection pool will be bound into JNDI with the nam
+e
+             "java:/MySQLDB" -->
+
+        <jndi-name>MySQLDB</jndi-name>
+        <connection-url>jdbc:mysql://localhost:3306/dbname</connection
+-url>
+        <driver-class>com.mysql.jdbc.Driver</driver-class>
+        <user-name>user</user-name>
+        <password>pass</password>
+
+        <min-pool-size>5</min-pool-size>
+
+        <!-- Don't set this any higher than max_connections on your
+         MySQL server, usually this should be a 10 or a few 10's
+         of connections, not hundreds or thousands -->
+
+        <max-pool-size>20</max-pool-size>
+
+        <!-- Don't allow connections to hang out idle too long,
+         never longer than what wait_timeout is set to on the
+         server...A few minutes is usually okay here,
+         it depends on your application
+         and how much spikey load it will see -->
+
+        <idle-timeout-minutes>5</idle-timeout-minutes>
+
+        <!-- If you're using Connector/J 3.1.8 or newer, you can use
+             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>
+
+    </local-tx-datasource>
+</datasources>
+
+1.5.3. Common Problems and Solutions
+
+   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.
+
+   Questions
+     * [1]1.5.3.1: When I try to connect to the database with
+       MySQL Connector/J, I get the following exception:
+SQLException: Server configuration denies access to data source
+SQLState: 08001
+VendorError: 0
+       What's going on? I can connect just fine with the MySQL
+       command-line client.
+     * [2]1.5.3.2: My application throws an SQLException 'No
+       Suitable Driver'. Why is this happening?
+     * [3]1.5.3.3: I'm trying to use MySQL Connector/J in an
+       applet or application and I get an exception similar to:
+SQLException: Cannot connect to MySQL server on host:3306.
+Is there a MySQL server running on the machine/port you
+are trying to connect to?
+
+(java.security.AccessControlException)
+SQLState: 08S01
+VendorError: 0
+     * [4]1.5.3.4: I have a servlet/application that works fine
+       for a day, and then stops working overnight
+     * [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.
+
+   Questions and Answers
+
+   1.5.3.1: When I try to connect to the database with MySQL
+   Connector/J, I get the following exception:
+SQLException: Server configuration denies access to data source
+SQLState: 08001
+VendorError: 0
+
+   What's going on? I can connect just fine with the MySQL
+   command-line client.
+
+   MySQL Connector/J must use TCP/IP sockets to connect to
+   MySQL, as Java does not support Unix Domain Sockets.
+   Therefore, when MySQL Connector/J connects to MySQL, the
+   security manager in MySQL server will use its grant tables to
+   determine whether the connection should be allowed.
+
+   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.
+
+   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.
+
+   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."
+     * The format of your connection URL is incorrect, or you
+       are referencing the wrong JDBC driver.
+     * When using DriverManager, the jdbc.drivers system
+       property has not been populated with the location of the
+       Connector/J driver.
+
+   1.5.3.3: I'm trying to use MySQL Connector/J in an applet or
+   application and I get an exception similar to:
+SQLException: Cannot connect to MySQL server on host:3306.
+Is there a MySQL server running on the machine/port you
+are trying to connect to?
+
+(java.security.AccessControlException)
+SQLState: 08S01
+VendorError: 0
+
+   Either you're running an Applet, your MySQL server has been
+   installed with the "--skip-networking" option set, or your
+   MySQL server has a firewall sitting in front of it.
+
+   Applets can only make network connections back to the machine
+   that runs the web server that served the .class files for the
+   applet. This means that MySQL must run on the same machine
+   (or you must have some sort of port re-direction) for this to
+   work. This also means that you will not be able to test
+   applets from your local file system, you must always deploy
+   them to a web server.
+
+   MySQL Connector/J can only communicate with MySQL using
+   TCP/IP, as Java does not support Unix domain sockets. TCP/IP
+   communication with MySQL might be affected if MySQL was
+   started with the "--skip-networking" flag, or if it is
+   firewalled.
+
+   If MySQL has been started with the "--skip-networking" option
+   set (the Debian Linux package of MySQL server does this for
+   example), you need to comment it out in the file
+   /etc/mysql/my.cnf or /etc/my.cnf. Of course your my.cnf file
+   might also exist in the data directory of your MySQL server,
+   or anywhere else (depending on how MySQL was compiled for
+   your system). Binaries created by MySQL AB always look in
+   /etc/my.cnf and [datadir]/my.cnf. If your MySQL server has
+   been firewalled, you will need to have the firewall
+   configured to allow TCP/IP connections from the host where
+   your Java code is running to the MySQL server on the port
+   that MySQL is listening to (by default, 3306).
+
+   1.5.3.4: I have a servlet/application that works fine for a
+   day, and then stops working overnight
+
+   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
+   Configuration Properties for Connector/J").
+
+   Also, you should be catching SQLExceptions in your
+   application and dealing with them, rather than propagating
+   them all the way until your application exits, this is just
+   good programming practice. MySQL Connector/J will set the
+   SQLState (see java.sql.SQLException.getSQLState() in your
+   APIDOCS) to "08S01" when it encounters network-connectivity
+   issues during the processing of a query. Your application
+   code should then attempt to re-connect to MySQL at this
+   point.
+
+   The following (simplistic) example shows what code that can
+   handle these exceptions might look like:
+
+   Example 12. Example of transaction with retry logic
+public void doBusinessOp() throws SQLException {
+        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;
+
+        boolean transactionCompleted = false;
+
+        do {
+            try {
+                conn = getConnection(); // assume getting this from a
+                                        // javax.sql.DataSource, or th
+e
+                                        // java.sql.DriverManager
+
+                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;
+
+                stmt = conn.createStatement();
+
+                String query = "SELECT foo FROM bar ORDER BY baz";
+
+                rs = stmt.executeQuery(query);
+
+                while (rs.next()) {
+                }
+
+                rs.close();
+                rs = null;
+
+                stmt.close();
+                stmt = null;
+
+                conn.commit();
+                conn.close();
+                conn = null;
+
+                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
+                //
+
+                String sqlState = sqlEx.getSQLState();
+
+                if ("08S01".equals(sqlState) || "40001".equals(sqlStat
+e)) {
+                    retryCount--;
+                } else {
+                    retryCount = 0;
+                }
+            } 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 (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;
+                    }
+                }
+            }
+        } 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.
+
+   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.
+
+   Because MySQL does not have row identifiers, MySQL
+   Connector/J can only update result sets that have come from
+   queries on tables that have at least one primary key, the
+   query must select every primary key and the query can only
+   span one table (that is, no joins). This is outlined in the
+   JDBC specification.
+
+   Note that this issue only occurs when using updatable result
+   sets, and is caused because Connector/J is unable to
+   guarantee that it can identify the correct rows within the
+   result set to be updated without having a unique reference to
+   each row. There is no requirement to have a unique field on a
+   table if you are using UPDATE or DELETE statements on a table
+   where you can individually specify the criteria to be matched
+   using a WHERE clause.
+
+1.6. Connector/J Support
+
+1.6.1. Connector/J Community Support
+
+   MySQL AB provides assistance to the user community by means
+   of its mailing lists. For Connector/J related issues, you can
+   get help from experienced users by using the MySQL and Java
+   mailing list. Archives and subscription information is
+   available online at http://lists.mysql.com/java.
+
+   For information about subscribing to MySQL mailing lists or
+   to browse list archives, visit http://lists.mysql.com/. See
+   MySQL Mailing Lists
+   (http://dev.mysql.com/doc/refman/5.1/en/mailing-lists.html).
+
+   Community support from experienced users is also available
+   through the JDBC Forum (http://forums.mysql.com/list.php?39).
+   You may also find help from other users in the other MySQL
+   Forums, located at http://forums.mysql.com. See MySQL
+   Community Support at the MySQL Forums
+   (http://dev.mysql.com/doc/refman/5.1/en/forums.html).
+
+1.6.2. How to Report Connector/J Bugs or Problems
+
+   The normal place to report bugs is http://bugs.mysql.com/,
+   which is the address for our bugs database. This database is
+   public, and can be browsed and searched by anyone. If you log
+   in to the system, you will also be able to enter new reports.
+
+   If you have found a sensitive security bug in MySQL, you can
+   send email to security_at_mysql.com
+   (mailto:security_at_mysql.com).
+
+   Writing a good bug report takes patience, but doing it right
+   the first time saves time both for us and for yourself. A
+   good bug report, containing a full test case for the bug,
+   makes it very likely that we will fix the bug in the next
+   release.
+
+   This section will help you write your report correctly so
+   that you don't waste your time doing things that may not help
+   us much or at all.
+
+   If you have a repeatable bug report, please report it to the
+   bugs database at http://bugs.mysql.com/. Any bug that we are
+   able to repeat has a high chance of being fixed in the next
+   MySQL release.
+
+   To report other problems, you can use one of the MySQL
+   mailing lists.
+
+   Remember that it is possible for us to respond to a message
+   containing too much information, but not to one containing
+   too little. People often omit facts because they think they
+   know the cause of a problem and assume that some details
+   don't matter.
+
+   A good principle is this: If you are in doubt about stating
+   something, state it. It is faster and less troublesome to
+   write a couple more lines in your report than to wait longer
+   for the answer if we must ask you to provide information that
+   was missing from the initial report.
+
+   The most common errors made in bug reports are (a) not
+   including the version number of Connector/J or MySQL used,
+   and (b) not fully describing the platform on which
+   Connector/J is installed (including the JVM version, and the
+   platform type and version number that MySQL itself is
+   installed on).
+
+   This is highly relevant information, and in 99 cases out of
+   100, the bug report is useless without it. Very often we get
+   questions like, "Why doesn't this work for me?" Then we find
+   that the feature requested wasn't implemented in that MySQL
+   version, or that a bug described in a report has already been
+   fixed in newer MySQL versions.
+
+   Sometimes the error is platform-dependent; in such cases, it
+   is next to impossible for us to fix anything without knowing
+   the operating system and the version number of the platform.
+
+   If at all possible, you should create a repeatable, stanalone
+   testcase that doesn't involve any third-party classes.
+
+   To streamline this process, we ship a base class for
+   testcases with Connector/J, named
+   'com.mysql.jdbc.util.BaseBugReport'. To create a testcase for
+   Connector/J using this class, create your own class that
+   inherits from com.mysql.jdbc.util.BaseBugReport and override
+   the methods setUp(), tearDown() and runTest().
+
+   In the setUp() method, create code that creates your tables,
+   and populates them with any data needed to demonstrate the
+   bug.
+
+   In the runTest() method, create code that demonstrates the
+   bug using the tables and data you created in the setUp
+   method.
+
+   In the tearDown() method, drop any tables you created in the
+   setUp() method.
+
+   In any of the above three methods, you should use one of the
+   variants of the getConnection() method to create a JDBC
+   connection to MySQL:
+     * getConnection() - Provides a connection to the JDBC URL
+       specified in getUrl(). If a connection already exists,
+       that connection is returned, otherwise a new connection
+       is created.
+     * getNewConnection() - Use this if you need to get a new
+       connection for your bug report (i.e. there's more than
+       one connection involved).
+     * getConnection(String url) - Returns a connection using
+       the given URL.
+     * getConnection(String url, Properties props) - Returns a
+       connection using the given URL and properties.
+
+   If you need to use a JDBC URL that is different from
+   'jdbc:mysql:///test', override the method getUrl() as well.
+
+   Use the assertTrue(boolean expression) and assertTrue(String
+   failureMessage, boolean expression) methods to create
+   conditions that must be met in your testcase demonstrating
+   the behavior you are expecting (vs. the behavior you are
+   observing, which is why you are most likely filing a bug
+   report).
+
+   Finally, create a main() method that creates a new instance
+   of your testcase, and calls the run method:
+public static void main(String[] args) throws Exception {
+      new MyBugReport().run();
+ }
+
+   Once you have finished your testcase, and have verified that
+   it demonstrates the bug you are reporting, upload it with
+   your bug report to http://bugs.mysql.com/.
+
+1.6.3. Connector/J Change History
+
+   The Connector/J Change History (Changelog) is located with
+   the main Changelog for MySQL. See MySQL Connector/J Change
+   History
+   (http://dev.mysql.com/doc/refman/5.1/en/cj-news.html).
+
+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

Added: branches/mysql-connector-java/upstream/5.0.4/docs/connector-j.html
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/docs/connector-j.html	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/docs/connector-j.html	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,2629 @@
+<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>
+    MySQL provides connectivity for client applications developed in the
+    Java programming language via a JDBC driver, which is called MySQL
+    Connector/J.
+  </p><p>
+    MySQL Connector/J is a JDBC-3.0 Type 4 driver, which means that is
+    pure Java, implements version 3.0 of the JDBC specification, and
+    communicates directly with the MySQL server using the MySQL
+    protocol.
+  </p><p>
+    Although JDBC is useful by itself, we would hope that if you are not
+    familiar with JDBC that after reading the first few sections of this
+    manual, that you would avoid using naked JDBC for all but the most
+    trivial problems and consider using one of the popular persistence
+    frameworks such as
+    <a href="http://www.hibernate.org/" target="_top">Hibernate</a>,
+    <a href="http://www.springframework.org/" target="_top">Spring's JDBC
+    templates</a> or <a href="http://ibatis.apache.org/" target="_top">Ibatis
+    SQL Maps</a> to do the majority of repetitive work and heavier
+    lifting that is sometimes required with JDBC.
+  </p><p>
+    This section is not designed to be a complete JDBC tutorial. If you
+    need more information about using JDBC you might be interested in
+    the following online tutorials that are more in-depth than the
+    information presented here:
+  </p><div class="itemizedlist"><ul type="disc"><li><p>
+        <a href="http://java.sun.com/docs/books/tutorial/jdbc/basics/index.html" target="_top">JDBC
+        Basics</a> — A tutorial from Sun covering beginner
+        topics in JDBC
+      </p></li><li><p>
+        <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><div class="itemizedlist"><ul type="disc"><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>
+            JDK 1.3.x
+          </p></li><li><p>
+            JDK 1.4.x
+          </p></li><li><p>
+            JDK 1.5.x
+          </p></li></ul></div><p>
+        If you are building Connector/J from source using the source
+        distribution (see
+        <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
+      </p><p>
+        Because of the implementation of
+        <code class="classname">java.sql.Savepoint</code>, Connector/J 3.1.0 and
+        newer will not run on JDKs older than 1.4 unless the class
+        verifier is turned off (by setting the
+        <code class="option">-Xverify:none</code> option to the Java runtime). This
+        is because the class verifier will try to load the class
+        definition for <code class="classname">java.sql.Savepoint</code> even
+        though it is not accessed by the driver unless you actually use
+        savepoint functionality.
+      </p><p>
+        Caching functionality provided by Connector/J 3.1.0 or newer is
+        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>
+      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
+    </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
+        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
+        <code class="literal">CLASSPATH</code> (see
+        <a href="#connector-j-installing-classpath" title="1.2.2. Installing the Driver and Configuring the CLASSPATH">Section 1.2.2, “Installing the Driver and Configuring the <code class="literal">CLASSPATH</code>”</a>).
+      </p><p>
+        MySQL Connector/J is distributed as a .zip or .tar.gz archive
+        containing the sources, the class files, and the JAR archive
+        named
+        <code class="filename">mysql-connector-java-<em class="replaceable"><code>[version]</code></em>-bin.jar</code>,
+        and starting with Connector/J 3.1.8 a debug build of the driver
+        in a file named
+        <code class="filename">mysql-connector-java-<em class="replaceable"><code>[version]</code></em>-bin-g.jar</code>.
+      </p><p>
+        Starting with Connector/J 3.1.9, the <code class="filename">.class</code>
+        files that constitute the JAR files are only included as part of
+        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
+        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 <code class="filename">src/lib/aspectjrt.jar</code> file
+        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
+        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
+        archive format) to unpack the .tar.gz variant of the
+        distribution.
+      </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-installing-classpath"></a>1.2.2. Installing the Driver and Configuring the <code class="literal">CLASSPATH</code></h4></div></div></div><p>
+        Once you have extracted the distribution archive, you can
+        install the driver by placing
+        <code class="filename">mysql-connector-java-[version]-bin.jar </code>in
+        your classpath, either by adding the full path to it to your
+        <code class="literal">CLASSPATH</code> environment variable, or by
+        directly specifying it with the command line switch -cp when
+        starting your JVM.
+      </p><p>
+        If you are going to use the driver with the JDBC DriverManager,
+        you would use <code class="literal">com.mysql.jdbc.Driver</code> as the
+        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
+        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
+        <code class="literal">/etc/profile</code> file.
+      </p><p>
+        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>
+        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>
+        Within Windows 2000, Windows XP and Windows Server 2003, you
+        must set the environment variable through the System control
+        panel.
+      </p><p>
+        If you want to use MySQL Connector/J with an application server
+        such as Tomcat or JBoss, you will have to read your vendor's
+        documentation for more information on how to configure
+        third-party class libraries, as most application servers ignore
+        the <code class="literal">CLASSPATH</code> environment variable. For
+        configuration examples for some J2EE application servers, 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>. However, the
+        authoritative source for JDBC connection pool configuration
+        information for your particular application server is the
+        documentation for that application server.
+      </p><p>
+        If you are developing servlets or JSPs, and your application
+        server is J2EE-compliant, you can put the driver's .jar file in
+        the WEB-INF/lib subdirectory of your webapp, as this is a
+        standard location for third party class libraries in J2EE web
+        applications.
+      </p><p>
+        You can also use the MysqlDataSource or
+        MysqlConnectionPoolDataSource classes in the
+        <code class="literal">com.mysql.jdbc.jdbc2.optional</code> package, if
+        your J2EE application server supports or requires them. Starting
+        with Connector/J 5.0.0, the
+        <code class="literal">javax.sql.XADataSource</code> interface is
+        implemented via the
+        <code class="literal">com.mysql.jdbc.jdbc2.optional.MysqlXADataSource</code>
+        class, which supports XA distributed transactions when used in
+        combination with MySQL server version 5.0.
+      </p><p>
+        The various MysqlDataSource classes support the following
+        parameters (through standard set mutators):
+      </p><div class="itemizedlist"><ul type="disc"><li><p>
+            user
+          </p></li><li><p>
+            password
+          </p></li><li><p>
+            serverName (see the previous section about fail-over hosts)
+          </p></li><li><p>
+            databaseName
+          </p></li><li><p>
+            port
+          </p></li></ul></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-installing-upgrading"></a>1.2.3. Upgrading from an Older Version</h4></div></div></div><p>
+        MySQL AB tries to keep the upgrade process as easy as possible,
+        however as is the case with any software, sometimes changes need
+        to be made in new versions to support new features, improve
+        existing functionality, or comply with new standards.
+      </p><p>
+        This section has information about what users who are upgrading
+        from one version of Connector/J to another (or to a new version
+        of the MySQL server, with respect to JDBC functionality) should
+        be aware of.
+      </p><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-installing-upgrading-3-0-to-3-1"></a>1.2.3.1. Upgrading from MySQL Connector/J 3.0 to 3.1</h5></div></div></div><p>
+          Connector/J 3.1 is designed to be backward-compatible with
+          Connector/J 3.0 as much as possible. Major changes are
+          isolated to new functionality exposed in MySQL-4.1 and newer,
+          which includes Unicode character sets, server-side prepared
+          statements, SQLState codes returned in error messages by the
+          server and various performance enhancements that can be
+          enabled or disabled via configuration properties.
+        </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
+              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>.
+            </p></li><li><p>
+              <span class="bold"><strong>Server-side Prepared
+              Statements</strong></span> — Connector/J 3.1 will
+              automatically detect and use server-side prepared
+              statements when they are available (MySQL server version
+              4.1.0 and newer).
+            </p><p>
+              Starting with version 3.1.7, the driver scans SQL you are
+              preparing via all variants of
+              <code class="literal">Connection.prepareStatement()</code> to
+              determine if it is a supported type of statement to
+              prepare on the server side, and if it is not supported by
+              the server, it instead prepares it as a client-side
+              emulated prepared statement. You can disable this feature
+              by passing
+              <span class="property">emulateUnsupportedPstmts=false</span> in
+              your JDBC URL.
+            </p><p>
+              If your application encounters issues with server-side
+              prepared statements, you can revert to the older
+              client-side emulated prepared statement code that is still
+              presently used for MySQL servers older than 4.1.0 with the
+              connection property
+              <span class="property">useServerPrepStmts=false</span>
+            </p></li><li><p>
+              <span class="bold"><strong>Datetimes</strong></span> with all-zero
+              components (<code class="literal">0000-00-00 ...</code>) —
+              These values can not be represented reliably in Java.
+              Connector/J 3.0.x always converted them to NULL when being
+              read from a ResultSet.
+            </p><p>
+              Connector/J 3.1 throws an exception by default when these
+              values are encountered as this is the most correct
+              behavior according to the JDBC and SQL standards. This
+              behavior can be modified using the
+              <span class="property">zeroDateTimeBehavior</span> configuration
+              property. The allowable values are:
+            </p><div class="itemizedlist"><ul type="circle"><li><p>
+                  <code class="literal">exception</code> (the default), which
+                  throws an SQLException with an SQLState of
+                  <code class="literal">S1009</code>.
+                </p></li><li><p>
+                  <code class="literal">convertToNull</code>, which returns
+                  <code class="literal">NULL</code> instead of the date.
+                </p></li><li><p>
+                  <code class="literal">round</code>, which rounds the date to the
+                  nearest closest value which is
+                  <code class="literal">0001-01-01</code>.
+                </p></li></ul></div><p>
+              Starting with Connector/J 3.1.7,
+              <code class="literal">ResultSet.getString()</code> can be decoupled
+              from this behavior via
+              <span class="property">noDatetimeStringSync=true</span> (the
+              default value is <code class="literal">false</code>) so that you can
+              get retrieve the unaltered all-zero value as a String. It
+              should be noted that this also precludes using any time
+              zone conversions, therefore the driver will not allow you
+              to enable <span class="property">noDatetimeStringSync</span> and
+              <span class="property">useTimezone</span> at the same time.
+            </p></li><li><p>
+              <span class="bold"><strong>New SQLState Codes</strong></span>
+              — Connector/J 3.1 uses SQL:1999 SQLState codes
+              returned by the MySQL server (if supported), which are
+              different from the legacy X/Open state codes that
+              Connector/J 3.0 uses. If connected to a MySQL server older
+              than MySQL-4.1.0 (the oldest version to return SQLStates
+              as part of the error code), the driver will use a built-in
+              mapping. You can revert to the old mapping by using the
+              configuration property
+              <span class="property">useSqlStateCodes=false</span>.
+            </p></li><li><p>
+              <span class="bold"><strong><code class="literal">ResultSet.getString()</code></strong></span>
+              — Calling <code class="literal">ResultSet.getString()</code>
+              on a BLOB column will now return the address of the byte[]
+              array that represents it, instead of a String
+              representation of the BLOB. BLOBs have no character set,
+              so they can't be converted to java.lang.Strings without
+              data loss or corruption.
+            </p><p>
+              To store strings in MySQL with LOB behavior, use one of
+              the TEXT types, which the driver will treat as a
+              java.sql.Clob.
+            </p></li><li><p>
+              <span class="bold"><strong>Debug builds</strong></span> —
+              Starting with Connector/J 3.1.8 a debug build of the
+              driver in a file named
+              <code class="filename">mysql-connector-java-<em class="replaceable"><code>[version]</code></em>-bin-g.jar</code>
+              is shipped alongside the normal binary jar file that is
+              named
+              <code class="filename">mysql-connector-java-<em class="replaceable"><code>[version]</code></em>-bin.jar</code>.
+            </p><p>
+              Starting with Connector/J 3.1.9, we don't ship the .class
+              files unbundled, they are only available in the JAR
+              archives that ship with the driver.
+            </p><p>
+              You should not use the debug build of the driver unless
+              instructed to do so when reporting a problem or 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
+              <code class="filename">src/lib/aspectjrt.jar</code> file that comes
+              with the Connector/J distribution.
+            </p></li></ul></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-installing-upgrading-issues"></a>1.2.3.2. JDBC-Specific Issues When Upgrading to MySQL Server 4.1 or Newer</h5></div></div></div><div class="itemizedlist"><ul type="disc"><li><p>
+              <span class="emphasis"><em>Using the UTF-8 Character Encoding</em></span> -
+              Prior to MySQL server version 4.1, the UTF-8 character
+              encoding was not supported by the server, however the JDBC
+              driver could use it, allowing storage of multiple
+              character sets in latin1 tables on the server.
+            </p><p>
+              Starting with MySQL-4.1, this functionality is deprecated.
+              If you have applications that rely on this functionality,
+              and can not upgrade them to use the official Unicode
+              character support in MySQL server version 4.1 or newer,
+              you should add the following property to your connection
+              URL:
+            </p><p>
+              <code class="computeroutput">useOldUTF8Behavior=true</code>
+            </p></li><li><p>
+              <span class="emphasis"><em>Server-side Prepared Statements</em></span> -
+              Connector/J 3.1 will automatically detect and use
+              server-side prepared statements when they are available
+              (MySQL server version 4.1.0 and newer). If your
+              application encounters issues with server-side prepared
+              statements, you can revert to the older client-side
+              emulated prepared statement code that is still presently
+              used for MySQL servers older than 4.1.0 with the following
+              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>
+          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>
+        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>
+            Subversion, to check out the sources from our repository
+            (available from
+            <a href="http://subversion.tigris.org/" target="_top">http://subversion.tigris.org/</a>).
+          </p></li><li><p>
+            Apache Ant version 1.6 or newer (available from
+            <a href="http://ant.apache.org/" target="_top">http://ant.apache.org/</a>).
+          </p></li><li><p>
+            JDK-1.4.2 or later. Although MySQL Connector/J can be
+            installed on older JDKs, to compile it from source you must
+            have at least JDK-1.4.2.
+          </p></li></ul></div><p>
+        The Subversion source code repository for MySQL Connector/J is
+        located at
+        <a href="http://svn.mysql.com/svnpublic/connector-j" target="_top">http://svn.mysql.com/svnpublic/connector-j</a>. In
+        general, you should not check out the entire repository because
+        it contains every branch and tag for MySQL Connector/J and is
+        quite large.
+      </p><p>
+        To check out and compile a specific branch of MySQL Connector/J,
+        follow these steps:
+      </p><div class="orderedlist"><ol type="1"><li><p>
+            At the time of this writing, there are three active branches
+            of Connector/J: <code class="literal">branch_3_0</code>,
+            <code class="literal">branch_3_1</code> and
+            <code class="literal">branch_5_0</code>. Check out the latest code
+            from the branch that you want with the following command
+            (replacing <em class="replaceable"><code>[major]</code></em> and
+            <em class="replaceable"><code>[minor]</code></em> with appropriate version
+            numbers):
+          </p><pre class="programlisting">shell&gt; <strong class="userinput"><code>svn co »
+http://svn.mysql.com/svnpublic/connector-j/branches/branch_<em class="replaceable"><code>[major]</code></em>_<em class="replaceable"><code>[minor]</code></em>/connector-j</code></strong></pre><p>
+            This creates a <code class="filename">connector-j</code> subdirectory
+            in the current directory that contains the latest sources
+            for the requested branch.
+          </p></li><li><p>
+            Change location to the <code class="filename">connector-j</code>
+            directory to make it your current working directory:
+          </p><pre class="programlisting">shell&gt; <strong class="userinput"><code>cd connector-j</code></strong></pre></li><li><p>
+            Issue the following command to compile the driver and create
+            a <code class="filename">.jar</code> file suitable for installation:
+          </p><pre class="programlisting">shell&gt; <strong class="userinput"><code>ant dist</code></strong></pre><p>
+            This creates a <code class="filename">build</code> directory in the
+            current directory, where all build output will go. A
+            directory is created in the <code class="filename">build</code>
+            directory that includes the version number of the sources
+            you are building from. This directory contains the sources,
+            compiled <code class="filename">.class</code> files, and a
+            <code class="filename">.jar</code> file suitable for deployment. For
+            other possible targets, including ones that will create a
+            fully packaged distribution, issue the following command:
+          </p><pre class="programlisting">shell&gt; <strong class="userinput"><code>ant --projecthelp</code></strong></pre></li><li><p>
+            A newly created <code class="filename">.jar</code> file containing
+            the JDBC driver will be placed in the directory
+            <code class="filename">build/mysql-connector-java-<em class="replaceable"><code>[version]</code></em></code>.
+          </p><p>
+            Install the newly created JDBC driver as you would a binary
+            <code class="filename">.jar</code> file that you download from MySQL
+            by following the instructions in
+            <a href="#connector-j-installing-classpath" title="1.2.2. Installing the Driver and Configuring the CLASSPATH">Section 1.2.2, “Installing the Driver and Configuring the <code class="literal">CLASSPATH</code>”</a>.
+          </p></li></ol></div></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h3 class="title"><a name="connector-j-examples"></a>1.3. Connector/J Examples</h3></div></div></div><p>
+      Examples of using Connector/J are located throughout this
+      document, this section provides a summary and links to these
+      examples.
+    </p><div class="itemizedlist"><ul type="disc"><li><p>
+          <a href="#connector-j-examples-connection-drivermanager" title="Example 1. Obtaining a connection from the DriverManager">Example 1, “Obtaining a connection from the <code class="literal">DriverManager</code>”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-execute-select" title="Example 2. Using java.sql.Statement to execute a SELECT query">Example 2, “Using java.sql.Statement to execute a <code class="literal">SELECT</code> query”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-stored-procedure" title="Example 3. Stored Procedures">Example 3, “Stored Procedures”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-preparecall" title="Example 4. Using Connection.prepareCall()">Example 4, “Using <code class="literal">Connection.prepareCall()</code>”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-output-param" title="Example 5. Registering output parameters">Example 5, “Registering output parameters”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-callablestatement" title="Example 6. Setting CallableStatement input parameters">Example 6, “Setting <code class="literal">CallableStatement</code> input parameters”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-retrieving-results-params" title="Example 7. Retrieving results and output parameter values">Example 7, “Retrieving results and output parameter values”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-autoincrement-getgeneratedkeys" title="Example 8. Retrieving AUTO_INCREMENT column values using
+              Statement.getGeneratedKeys()">Example 8, “Retrieving <code class="literal">AUTO_INCREMENT</code> column values using
+              <code class="literal">Statement.getGeneratedKeys()</code>”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-autoincrement-select" title="Example 9. Retrieving AUTO_INCREMENT column values using
+              SELECT LAST_INSERT_ID()">Example 9, “Retrieving <code class="literal">AUTO_INCREMENT</code> column values using
+              <code class="literal">SELECT LAST_INSERT_ID()</code>”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-autoincrement-updateable-resultsets" title="Example 10. Retrieving AUTO_INCREMENT column values in
+              Updatable ResultSets">Example 10, “Retrieving <code class="literal">AUTO_INCREMENT</code> column values in
+              <code class="literal">Updatable ResultSets</code>”</a>
+        </p></li><li><p>
+          <a href="#connector-j-examples-connectionpool-j2ee" title="Example 11. Using a connection pool with a J2EE application server">Example 11, “Using a connection pool with a J2EE application server”</a>
+        </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>
+      This section of the manual contains reference material for MySQL
+      Connector/J, some of which is automatically generated during the
+      Connector/J build process.
+    </p><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-reference-configuration-properties"></a>1.4.1. Driver/Datasource Class Names, URL Syntax and Configuration Properties
+        for Connector/J</h4></div></div></div><p>
+        The name of the class that implements java.sql.Driver in MySQL
+        Connector/J is <code class="literal">com.mysql.jdbc.Driver</code>. The
+        <code class="literal">org.gjt.mm.mysql.Driver</code> class name is also
+        usable to remain backward-compatible with MM.MySQL. You should
+        use this class name when registering the driver, or when
+        otherwise configuring software to use MySQL Connector/J.
+      </p><p>
+        The JDBC URL format for MySQL Connector/J is as follows, with
+        items in square brackets ([, ]) being optional:
+      </p><pre class="programlisting">jdbc:mysql://[host][,failoverhost...][:port]/[database] »
+[?propertyName1][=propertyValue1][&amp;propertyName2][=propertyValue2]...</pre><p>
+        If the hostname is not specified, it defaults to 127.0.0.1. If
+        the port is not specified, it defaults to 3306, the default port
+        number for MySQL servers.
+      </p><pre class="programlisting">jdbc:mysql://[host:port],[host:port].../[database] »
+[?propertyName1][=propertyValue1][&amp;propertyName2][=propertyValue2]...</pre><p>
+        If the database is not specified, the connection will be made
+        with no default database. In this case, you will need to either
+        call the <code class="literal">setCatalog()</code> method on the
+        Connection instance or fully-specify table names using the
+        database name (i.e. <code class="literal">SELECT dbname.tablename.colname
+        FROM dbname.tablename...</code>) in your SQL. Not specifying
+        the database to use upon connection is generally only useful
+        when building tools that work with multiple databases, such as
+        GUI database managers.
+      </p><p>
+        MySQL Connector/J has fail-over support. This allows the driver
+        to fail-over to any number of slave hosts and still perform
+        read-only queries. Fail-over only happens when the connection is
+        in an <code class="literal">autoCommit(true)</code> state, because
+        fail-over can not happen reliably when a transaction is in
+        progress. Most application servers and connection pools set
+        <code class="literal">autoCommit</code> to <code class="literal">true</code> at the
+        end of every transaction/connection use.
+      </p><p>
+        The fail-over functionality has the following behavior:
+      </p><div class="itemizedlist"><ul type="disc"><li><p>
+            If the URL property <span class="property">autoReconnect</span> is
+            false: Failover only happens at connection initialization,
+            and failback occurs when the driver determines that the
+            first host has become available again.
+          </p></li><li><p>
+            If the URL property <span class="property">autoReconnect</span> is
+            true: Failover happens when the driver determines that the
+            connection has failed (before <span class="emphasis"><em>every</em></span>
+            query), and falls back to the first host when it determines
+            that the host has become available again (after
+            <code class="literal">queriesBeforeRetryMaster</code> queries have
+            been issued).
+          </p></li></ul></div><p>
+        In either case, whenever you are connected to a "failed-over"
+        server, the connection will be set to read-only state, so
+        queries that would modify data will have exceptions thrown (the
+        query will <span class="bold"><strong>never</strong></span> be processed
+        by the MySQL server).
+      </p><p>
+        Configuration properties define how Connector/J will make a
+        connection to a MySQL server. Unless otherwise noted, properties
+        can be set for a DataSource object or for a Connection object.
+      </p><p>
+        Configuration Properties can be set in one of the following
+        ways:
+      </p><div class="itemizedlist"><ul type="disc"><li><p>
+            Using the set*() methods on MySQL implementations of
+            java.sql.DataSource (which is the preferred method when
+            using implementations of java.sql.DataSource):
+          </p><div class="itemizedlist"><ul type="circle"><li><p>
+                com.mysql.jdbc.jdbc2.optional.MysqlDataSource
+              </p></li><li><p>
+                com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
+              </p></li></ul></div></li><li><p>
+            As a key/value pair in the java.util.Properties instance
+            passed to <code class="literal">DriverManager.getConnection()</code>
+            or <code class="literal">Driver.connect()</code>
+          </p></li><li><p>
+            As a JDBC URL parameter in the URL given to
+            <code class="literal">java.sql.DriverManager.getConnection()</code>,
+            <code class="literal">java.sql.Driver.connect()</code> or the MySQL
+            implementations of the
+            <code class="literal">javax.sql.DataSource</code>
+            <code class="literal">setURL()</code> method.
+          </p><p><b>Note. </b>
+              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>
+        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>
+        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
+        factory via the <span class="property">socketFactory</span> property. If
+        you don't use a <span class="property">namedPipePath</span> property, the
+        default of '\\.\pipe\MySQL' will be used. If you use the
+        <code class="literal">NamedPipeSocketFactory</code>, the hostname and port
+        number values in the JDBC url will be ignored. You can enable
+        this feature using:
+      </p><pre class="programlisting">socketFactory=com.mysql.jdbc.NamedPipeSocketFactory
+        </pre><p>
+        Named pipes only work when connecting to a MySQL server on the
+        same physical machine as the one the JDBC driver is being used
+        on. In simple performance tests, it appears that named pipe
+        access is between 30%-50% faster than the standard TCP/IP
+        access.
+      </p><p>
+        You can create your own socket factories by following the
+        example code in
+        <code class="classname">com.mysql.jdbc.NamedPipeSocketFactory</code>, or
+        <code class="classname">com.mysql.jdbc.StandardSocketFactory</code>.
+      </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-reference-implementation-notes"></a>1.4.2. JDBC API Implementation Notes</h4></div></div></div><p>
+        MySQL Connector/J passes all of the tests in the
+        publicly-available version of Sun's JDBC compliance test suite.
+        However, in many places the JDBC specification is vague about
+        how certain functionality should be implemented, or the
+        specification allows leeway in implementation.
+      </p><p>
+        This section gives details on a interface-by-interface level
+        about how certain implementation decisions may affect how you
+        use MySQL Connector/J.
+      </p><div class="itemizedlist"><ul type="disc"><li><p>
+            Blob
+          </p><p>
+            The Blob implementation does not allow in-place modification
+            (they are copies, as reported by the
+            <code class="literal">DatabaseMetaData.locatorsUpdateCopies()</code>
+            method). Because of this, you should use the corresponding
+            <code class="literal">PreparedStatement.setBlob()</code> or
+            <code class="literal">ResultSet.updateBlob()</code> (in the case of
+            updatable result sets) methods to save changes back to the
+            database.
+          </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.
+            Currently, the <code class="literal">getParameterMetaData()</code>
+            method of <code class="classname">CallableStatement</code> is not
+            supported.
+          </p></li><li><p>
+            Clob
+          </p><p>
+            The Clob implementation does not allow in-place modification
+            (they are copies, as reported by the
+            <code class="literal">DatabaseMetaData.locatorsUpdateCopies()</code>
+            method). Because of this, you should use the
+            <code class="literal">PreparedStatement.setClob()</code> method to
+            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
+          </p><p>
+            Unlike older versions of MM.MySQL the
+            <code class="literal">isClosed()</code> method does not ping the
+            server to determine if it is alive. In accordance with the
+            JDBC specification, it only returns true if
+            <code class="literal">closed()</code> has been called on the
+            connection. If you need to determine if the connection is
+            still valid, you should issue a simple query, such as
+            <code class="literal">SELECT 1</code>. The driver will throw an
+            exception if the connection is no longer valid.
+          </p></li><li><p>
+            DatabaseMetaData
+          </p><p>
+            Foreign Key information
+            (<code class="literal">getImportedKeys()</code>/<code class="literal">getExportedKeys()</code>
+            and <code class="literal">getCrossReference()</code>) is only
+            available from InnoDB tables. However, the driver uses
+            <code class="literal">SHOW CREATE TABLE</code> to retrieve this
+            information, so when other storage engines support foreign
+            keys, the driver will transparently support them as well.
+          </p></li><li><p>
+            PreparedStatement
+          </p><p>
+            PreparedStatements are implemented by the driver, as MySQL
+            does not have a prepared statement feature. Because of this,
+            the driver does not implement
+            <code class="literal">getParameterMetaData()</code> or
+            <code class="literal">getMetaData()</code> as it would require the
+            driver to have a complete SQL parser in the client.
+          </p><p>
+            Starting with version 3.1.0 MySQL Connector/J, server-side
+            prepared statements and binary-encoded result sets are used
+            when the server supports them.
+          </p><p>
+            Take care when using a server-side prepared statement with
+            <span class="bold"><strong>large</strong></span> parameters that are
+            set via <code class="literal">setBinaryStream()</code>,
+            <code class="literal">setAsciiStream()</code>,
+            <code class="literal">setUnicodeStream()</code>,
+            <code class="literal">setBlob()</code>, or
+            <code class="literal">setClob()</code>. If you want to re-execute the
+            statement with any large parameter changed to a non-large
+            parameter, it is necessary to call
+            <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).
+              </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
+                spec), and can't be read from again.
+              </p></li></ul></div><div class="itemizedlist"><ul type="circle"><li><p>
+                If a parameter changes from large to non-large, the
+                driver must reset the server-side state of the prepared
+                statement to allow the parameter that is being changed
+                to take the place of the prior large value. This removes
+                all of the large data that has already been sent to the
+                server, thus requiring the data to be re-sent, via the
+                <code class="literal">setBinaryStream()</code>,
+                <code class="literal">setAsciiStream()</code>,
+                <code class="literal">setUnicodeStream()</code>,
+                <code class="literal">setBlob()</code> or
+                <code class="literal">setClob()</code> methods.
+              </p></li></ul></div><p>
+            Consequently, if you want to change the type of a parameter
+            to a non-large one, you must call
+            <code class="literal">clearParameters()</code> and set all parameters
+            of the prepared statement again before it can be
+            re-executed.
+          </p></li><li><p>
+            ResultSet
+          </p><p>
+            By default, ResultSets are completely retrieved and stored
+            in memory. In most cases this is the most efficient way to
+            operate, and due to the design of the MySQL network protocol
+            is easier to implement. If you are working with ResultSets
+            that have a large number of rows or large values, and can
+            not allocate heap space in your JVM for the memory required,
+            you can tell the driver to stream the results back one row
+            at a time.
+          </p><p>
+            To enable this functionality, you need to create a Statement
+            instance in the following manner:
+          </p><pre class="programlisting">stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+              java.sql.ResultSet.CONCUR_READ_ONLY);
+stmt.setFetchSize(Integer.MIN_VALUE);</pre><p>
+            The combination of a forward-only, read-only result set,
+            with a fetch size of <code class="literal">Integer.MIN_VALUE</code>
+            serves as a signal to the driver to stream result sets
+            row-by-row. After this any result sets created with the
+            statement will be retrieved row-by-row.
+          </p><p>
+            There are some caveats with this approach. You will have to
+            read all of the rows in the result set (or close it) before
+            you can issue any other queries on the connection, or an
+            exception will be thrown.
+          </p><p>
+            The earliest the locks these statements hold can be released
+            (whether they be <code class="literal">MyISAM</code> table-level locks
+            or row-level locks in some other storage engine such as
+            <code class="literal">InnoDB</code>) is when the statement completes.
+          </p><p>
+            If the statement is within scope of a transaction, then
+            locks are released when the transaction completes (which
+            implies that the statement needs to complete first). As with
+            most other databases, statements are not complete until all
+            the results pending on the statement are read or the active
+            result set for the statement is closed.
+          </p><p>
+            Therefore, if using streaming results, you should process
+            them as quickly as possible if you want to maintain
+            concurrent access to the tables referenced by the statement
+            producing the result set.
+          </p></li><li><p>
+            ResultSetMetaData
+          </p><p>
+            The <code class="literal">isAutoIncrement()</code> method only works
+            when using MySQL servers 4.0 and newer.
+          </p></li><li><p>
+            Statement
+          </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.
+          </p><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>
+        MySQL Connector/J is flexible in the way it handles conversions
+        between MySQL data types and Java data types.
+      </p><p>
+        In general, any MySQL data type can be converted to a
+        java.lang.String, and any numerical type can be converted to any
+        of the Java numerical types, although round-off, overflow, or
+        loss of precision may occur.
+      </p><p>
+        Starting with Connector/J 3.1.0, the JDBC driver will issue
+        warnings or throw DataTruncation exceptions as is required by
+        the JDBC specification unless the connection was configured not
+        to do so by using the property
+        <span class="property">jdbcCompliantTruncation</span> and setting it to
+        <code class="literal">false</code>.
+      </p><p>
+        The conversions that are always guaranteed to work are listed in
+        the following table:
+      </p><p><b>Connection Properties - Miscellaneous. </b>
+          </p><div class="informaltable"><table border="1"><colgroup><col><col></colgroup><tbody><tr><td><span class="bold"><strong>These MySQL Data Types</strong></span></td><td><span class="bold"><strong>Can always be converted to these Java
+                    types</strong></span></td></tr><tr><td><code class="literal">CHAR, VARCHAR, BLOB, TEXT, ENUM, and SET</code></td><td><code class="literal">java.lang.String, java.io.InputStream, java.io.Reader,
+                    java.sql.Blob, java.sql.Clob</code></td></tr><tr><td><code class="literal">FLOAT, REAL, DOUBLE PRECISION, NUMERIC, DECIMAL, TINYINT,
+                    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>
+        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
+        <code class="classname">ResultSetMetaData.GetColumnClassName()</code> is
+        also shown below. For more information on the
+        <code class="literal">java.sql.Types</code> classes see
+        <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
+                    <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
+                    storage size is 1, or
+                    <code class="classname">java.lang.Integer</code> if not.</td></tr><tr><td><span class="type">BOOL</span>, <span class="type">BOOLEAN</span></td><td><span class="type">TINYINT</span></td><td>See <span class="type">TINYINT</span>, above as these are aliases for
+                    <span class="type">TINYINT(1)</span>, currently.</td></tr><tr><td><span class="type">SMALLINT[(M)] [UNSIGNED]</span></td><td><span class="type">SMALLINT [UNSIGNED]</span></td><td><code class="classname">java.lang.Integer</code> (regardless if UNSIGNED or not)</td></tr><tr><td><span class="type">MEDIUMINT[(M)] [UNSIGNED]</span></td><td><span class="type">MEDIUMINT [UNSIGNED]</span></td><td><code class="classname">java.lang.Integer,</code> if UNSIGNED
+                    <code class="classname">java.lang.Long</code></td></tr><tr><td><span class="type">INT,INTEGER[(M)] [UNSIGNED]</span></td><td><span class="type">INTEGER [UNSIGNED]</span></td><td><code class="classname">java.lang.Integer</code>, if UNSIGNED
+                    <code class="classname">java.lang.Long</code></td></tr><tr><td><span class="type">BIGINT[(M)] [UNSIGNED]</span></td><td><span class="type">BIGINT [UNSIGNED]</span></td><td><code class="classname">java.lang.Long</code>, if UNSIGNED
+                    <code class="classname">java.math.BigInteger</code></td></tr><tr><td><span class="type">FLOAT[(M,D)]</span></td><td><span class="type">FLOAT</span></td><td><code class="classname">java.lang.Float</code></td></tr><tr><td><span class="type">DOUBLE[(M,B)]</span></td><td><span class="type">DOUBLE</span></td><td><code class="classname">java.lang.Double</code></td></tr><tr><td><span class="type">DECIMAL[(M[,D])]</span></td><td><span class="type">DECIMAL</span></td><td><code class="classname">java.math.BigDecimal</code></td></tr><tr><td><span class="type">DATE</span></td><td><span class="type">DATE</span></td><td><code class="classname">java.sql.Date</code></td></tr><tr><td><span class="type">DATETIME</span></td><td><span class="type">DATETIME</span></td><td><code class="classname">java.sql.Timestamp</code></td></tr><tr><td><span class="type">TIMESTAMP[(M)]</span></td><td><span class="type">TIMESTAMP</span></td><td><code class="classname">java.sql.Timestamp</code></td></tr><tr><td><span class="type">TIME</span></td><td><span class="type">TIME</span></td><td><code class="classname">java.sql.Time</code></td></tr><tr><td><span class="type">YEAR[(2|4)]</span></td><td><span class="type">YEAR</span></td><td>If <code class="literal">yearIsDateType</code> configuration property is set to
+                    false, then the returned object type is
+                    <code class="classname">java.sql.Short</code>. If set to
+                    true (the default) then an object of type
+                    <code class="classname">java.sql.Date</code> (with the date
+                    set to January 1st, at midnight).</td></tr><tr><td><span class="type">CHAR(M)</span></td><td><span class="type">CHAR</span></td><td><code class="classname">java.lang.String</code> (unless the character set for
+                    the column is <span class="type">BINARY</span>, then
+                    <code class="classname">byte[]</code> is returned.</td></tr><tr><td><span class="type">VARCHAR(M) [BINARY]</span></td><td><span class="type">VARCHAR</span></td><td><code class="classname">java.lang.String</code> (unless the character set for
+                    the column is <span class="type">BINARY</span>, then
+                    <code class="classname">byte[]</code> is returned.</td></tr><tr><td><span class="type">BINARY(M)</span></td><td><span class="type">BINARY</span></td><td><code class="classname">byte[]</code></td></tr><tr><td><span class="type">VARBINARY(M)</span></td><td><span class="type">VARBINARY</span></td><td><code class="classname">byte[]</code></td></tr><tr><td><span class="type">TINYBLOB</span></td><td><span class="type">TINYBLOB</span></td><td><code class="classname">byte[]</code></td></tr><tr><td><span class="type">TINYTEXT</span></td><td><span class="type">VARCHAR</span></td><td><code class="classname">java.lang.String</code></td></tr><tr><td><span class="type">BLOB</span></td><td><span class="type">BLOB</span></td><td><code class="classname">byte[]</code></td></tr><tr><td><span class="type">TEXT</span></td><td><span class="type">VARCHAR</span></td><td><code class="classname">java.lang.String</code></td></tr><tr><td><span class="type">MEDIUMBLOB</span></td><td><span class="type">MEDIUMBLOB</span></td><td><code class="classname">byte[]</code></td></tr><tr><td><span class="type">MEDIUMTEXT</span></td><td><span class="type">VARCHAR</span></td><td><code class="classname">java.lang.String</code></td></tr><tr><td><span class="type">LONGBLOB</span></td><td><span class="type">LONGBLOB</span></td><td><code class="classname">byte[]</code></td></tr><tr><td><span class="type">LONGTEXT</span></td><td><span class="type">VARCHAR</span></td><td><code class="classname">java.lang.String</code></td></tr><tr><td><span class="type">ENUM('value1','value2',...)</span></td><td><span class="type">CHAR</span></td><td><code class="classname">java.lang.String</code></td></tr><tr><td><span class="type">SET('value1','value2',...)</span></td><td><span class="type">CHAR</span></td><td><code class="classname">java.lang.String</code></td></tr></tbody></table></div><p>
+        </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-reference-charsets"></a>1.4.4. Using Character Sets and Unicode</h4></div></div></div><p>
+        All strings sent from the JDBC driver to the server are
+        converted automatically from native Java Unicode form to the
+        client character encoding, including all queries sent via
+        <code class="literal">Statement.execute()</code>,
+        <code class="literal">Statement.executeUpdate()</code>,
+        <code class="literal">Statement.executeQuery()</code> as well as all
+
+        <code class="interfacename">PreparedStatement</code>
+
+        and
+
+        <code class="interfacename">CallableStatement</code>
+
+        parameters with the exclusion of parameters set using
+        <code class="literal">setBytes()</code>,
+        <code class="literal">setBinaryStream()</code>,
+        <code class="literal">setAsciiStream()</code>,
+        <code class="literal">setUnicodeStream()</code> and
+        <code class="literal">setBlob()</code> .
+      </p><p>
+        Prior to MySQL Server 4.1, Connector/J supported a single
+        character encoding per connection, which could either be
+        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.
+      </p><p>
+        Starting with MySQL Server 4.1, Connector/J supports a single
+        character encoding between client and server, and any number of
+        character encodings for data returned by the server to the
+        client in <code class="classname">ResultSets</code>.
+      </p><p>
+        The character encoding between client and server is
+        automatically detected upon connection. The encoding used by the
+        driver is specified on the server via the
+        <code class="literal">character_set</code> system variable for server
+        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
+        ???.
+      </p><p>
+        To override the automatically-detected encoding on the client
+        side, use the <em class="parameter"><code>characterEncoding</code></em> property
+        in the URL used to connect to the server.
+      </p><p>
+        When specifying character encodings on the client side,
+        Java-style names should be used. The following table lists
+        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>
+          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>
+        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
+        by configuring the JDBC driver to use UTF-8 through the
+        <em class="parameter"><code>characterEncoding</code></em> property.
+      </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-reference-using-ssl"></a>1.4.5. Connecting Securely Using SSL</h4></div></div></div><p>
+        SSL in MySQL Connector/J encrypts all data (other than the
+        initial handshake) between the JDBC driver and the server. The
+        performance penalty for enabling SSL is an increase in query
+        processing time between 35% and 50%, depending on the size of
+        the query, and the amount of data it returns.
+      </p><p>
+        For SSL Support to work, you must have the following:
+      </p><div class="itemizedlist"><ul type="disc"><li><p>
+            A JDK that includes JSSE (Java Secure Sockets Extension),
+            like JDK-1.4.1 or newer. SSL does not currently work with a
+            JDK that you can add JSSE to, like JDK-1.2.x or JDK-1.3.x
+            due to the following JSSE bug:
+            <a href="http://developer.java.sun.com/developer/bugParade/bugs/4273544.html" target="_top">http://developer.java.sun.com/developer/bugParade/bugs/4273544.html</a>
+          </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.
+          </p></li><li><p>
+            A client certificate (covered later in this section)
+          </p></li></ul></div><p>
+        You will first need to import the MySQL server CA Certificate
+        into a Java truststore. A sample MySQL server CA Certificate is
+        located in the <code class="filename">SSL</code> subdirectory of the
+        MySQL source distribution. This is what SSL will use to
+        determine if you are communicating with a secure MySQL server.
+      </p><p>
+        To use Java's <span><strong class="command">keytool</strong></span> to create a truststore
+        in the current directory , and import the server's CA
+        certificate (<code class="filename">cacert.pem</code>), you can do the
+        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>
+        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
+Serial number: 0
+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
+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>
+        Keytool will prompt you for the following information, and
+        create a keystore named <code class="filename">keystore</code> in the
+        current directory.
+      </p><p>
+        You should respond with information that is appropriate for your
+        situation:
+      </p><pre class="programlisting">Enter keystore password:  *********
+What is your first and last name?
+  [Unknown]:  Matthews
+What is the name of your organizational unit?
+  [Unknown]:  Software Development
+What is the name of your organization?
+  [Unknown]:  MySQL AB
+What is the name of your City or Locality?
+  [Unknown]:  Flossmoor
+What is the name of your State or Province?
+  [Unknown]:  IL
+What is the two-letter country code for this unit?
+  [Unknown]:  US
+Is &lt;CN=Matthews, OU=Software Development, O=MySQL AB,
+ L=Flossmoor, ST=IL, C=US&gt; correct?
+  [no]:  y
+
+Enter key password for &lt;mysqlClientCertificate&gt;
+        (RETURN if same as keystore password):</pre><p>
+        Finally, to get JSSE to use the keystore and truststore that you
+        have generated, you need to set the following system properties
+        when you start your JVM, replacing
+        <span class="property">path_to_keystore_file</span> with the full path to
+        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.
+      </p><pre class="programlisting">-Djavax.net.ssl.keyStore=path_to_keystore_file
+-Djavax.net.ssl.keyStorePassword=*********
+-Djavax.net.ssl.trustStore=path_to_truststore_file
+-Djavax.net.ssl.trustStorePassword=********* </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
+        your URL, or by setting the property <span class="property">useSSL</span>
+        to <code class="literal">true</code> in the
+        <code class="classname">java.util.Properties</code> instance you pass to
+        <code class="literal">DriverManager.getConnection()</code>.
+      </p><p>
+        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>
+        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
+        well as what is going on during the SSL handshake and
+        certificate exchange. It will be helpful when trying to
+        determine what is not working when trying to get an SSL
+        connection to happen.
+      </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-reference-replication-connection"></a>1.4.6. Using Master/Slave Replication with ReplicationConnection</h4></div></div></div><p>
+        Starting with Connector/J 3.1.7, we've made available a variant
+        of the driver that will automatically send queries to a
+        read/write master, or a failover or round-robin loadbalanced set
+        of slaves based on the state of
+        <code class="literal">Connection.getReadOnly()</code> .
+      </p><p>
+        An application signals that it wants a transaction to be
+        read-only by calling
+        <code class="literal">Connection.setReadOnly(true)</code>, this
+        replication-aware connection will use one of the slave
+        connections, which are load-balanced per-vm using a round-robin
+        scheme (a given connection is sticky to a slave unless that
+        slave is removed from service). If you have a write transaction,
+        or if you have a read that is time-sensitive (remember,
+        replication in MySQL is asynchronous), set the connection to be
+        not read-only, by calling
+        <code class="literal">Connection.setReadOnly(false)</code> and the driver
+        will ensure that further calls are sent to the master MySQL
+        server. The driver takes care of propagating the current state
+        of autocommit, isolation level, and catalog between all of the
+        connections that it uses to accomplish this load balancing
+        functionality.
+      </p><p>
+        To enable this functionality, use the "
+        <code class="literal">com.mysql.jdbc.ReplicationDriver</code> " class when
+        configuring your application server's connection pool or when
+        creating an instance of a JDBC driver for your standalone
+        application. Because it accepts the same URL format as the
+        standard MySQL JDBC driver, <code class="literal">ReplicationDriver</code>
+        does not currently work with
+        <code class="literal">java.sql.DriverManager</code> -based connection
+        creation unless it is the only MySQL JDBC driver registered with
+        the <code class="literal">DriverManager</code> .
+      </p><p>
+        Here is a short, simple example of how ReplicationDriver might
+        be used in a standalone application.
+      </p><a name="connector-j-using-replication-driver-example"></a><pre class="programlisting">import java.sql.Connection;
+import java.sql.ResultSet;
+import java.util.Properties;
+
+import com.mysql.jdbc.ReplicationDriver;
+
+public class ReplicationDriverDemo {
+
+    public static void main(String[] args) throws Exception {
+        ReplicationDriver driver = new ReplicationDriver();
+
+        Properties props = new Properties();
+
+        // We want this for failover on the slaves
+        props.put("autoReconnect", "true");
+
+        // We want to load balance between the slaves
+        props.put("roundRobinLoadBalance", "true");
+
+        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
+        //
+
+        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"
+        //
+
+        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
+        //
+
+        conn.setReadOnly(true);
+
+        ResultSet rs = conn.createStatement().executeQuery("SELECT a,b,c FROM some_other_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>
+        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
+          <code class="literal">DriverManager</code> class manages the
+          establishment of Connections.
+        </p><p>
+          The <code class="literal">DriverManager</code> needs to be told which
+          JDBC drivers it should try to make Connections with. The
+          easiest way to do this is to use
+          <code class="literal">Class.forName()</code> on the class that
+          implements the <code class="literal">java.sql.Driver</code> interface.
+          With MySQL Connector/J, the name of this class is
+          <code class="literal">com.mysql.jdbc.Driver</code>. With this method,
+          you could use an external configuration file to supply the
+          driver class name and driver parameters to use when connecting
+          to a database.
+        </p><p>
+          The following section of Java code shows how you might
+          register MySQL Connector/J from the <code class="literal">main()</code>
+          method of your application:
+        </p><pre class="programlisting">import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+// Notice, do not import com.mysql.jdbc.*
+// or you will have problems!
+
+public class LoadDriver {
+    public static void main(String[] args) {
+        try {
+            // The newInstance() call is a work around for some
+            // broken Java implementations
+
+            Class.forName("com.mysql.jdbc.Driver").newInstance();
+        } catch (Exception ex) {
+            // handle the error
+        }
+}</pre><p>
+          After the driver has been registered with the
+          <code class="literal">DriverManager</code>, you can obtain a
+          <code class="literal">Connection</code> instance that is connected to a
+          particular database by calling
+          <code class="literal">DriverManager.getConnection()</code>:
+        </p><div class="example"><a name="connector-j-examples-connection-drivermanager"></a><p class="title"><b>Example 1. Obtaining a connection from the <code class="literal">DriverManager</code></b></p><p>
+            This example shows how you can obtain a
+            <code class="literal">Connection</code> instance from the
+            <code class="literal">DriverManager</code>. There are a few different
+            signatures for the <code class="literal">getConnection()</code>
+            method. You should see the API documentation that comes with
+            your JDK for more specific information on how to use them.
+          </p><pre class="programlisting">import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+
+    ... try {
+            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost/test?user=monty&amp;password=greatsqldb");
+
+            // 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());
+        }
+</pre><p>
+            Once a <code class="classname">Connection</code> is established, it
+            can be used to create <code class="classname">Statement</code> and
+            <code class="classname">PreparedStatement</code> objects, as well as
+            retrieve metadata about the database. This is explained in
+            the following sections.
+          </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-usagenotes-statements"></a>1.5.1.2. Using Statements to Execute SQL</h5></div></div></div><p>
+          <code class="classname">Statement</code> objects allow you to execute
+          basic SQL queries and retrieve the results through the
+          <code class="literal">ResultSet</code> class which is described later.
+        </p><p>
+          To create a <code class="classname">Statement</code> instance, you
+          call the <code class="literal">createStatement()</code> method on the
+          <code class="literal">Connection</code> object you have retrieved via
+          one of the <code class="literal">DriverManager.getConnection()</code> or
+          <code class="literal">DataSource.getConnection()</code> methods
+          described earlier.
+        </p><p>
+          Once you have a <code class="classname">Statement</code> instance, you
+          can execute a <code class="literal">SELECT</code> query by calling the
+          <code class="literal">executeQuery(String)</code> method with the SQL
+          you want to use.
+        </p><p>
+          To update data in the database, use the
+          <code class="literal">executeUpdate(String SQL)</code> method. This
+          method returns the number of rows affected by the update
+          statement.
+        </p><p>
+          If you don't know ahead of time whether the SQL statement will
+          be a <code class="literal">SELECT</code> or an
+          <code class="literal">UPDATE</code>/<code class="literal">INSERT</code>, then you
+          can use the <code class="literal">execute(String SQL)</code> method.
+          This method will return true if the SQL query was a
+          <code class="literal">SELECT</code>, or false if it was an
+          <code class="literal">UPDATE</code>, <code class="literal">INSERT</code>, or
+          <code class="literal">DELETE</code> statement. If the statement was a
+          <code class="literal">SELECT</code> query, you can retrieve the results
+          by calling the <code class="literal">getResultSet()</code> method. If
+          the statement was an <code class="literal">UPDATE</code>,
+          <code class="literal">INSERT</code>, or <code class="literal">DELETE</code>
+          statement, you can retrieve the affected rows count by calling
+          <code class="literal">getUpdateCount()</code> on the
+          <code class="classname">Statement</code> instance.
+        </p><div class="example"><a name="connector-j-examples-execute-select"></a><p class="title"><b>Example 2. Using java.sql.Statement to execute a <code class="literal">SELECT</code> query</b></p><pre class="programlisting">// assume that conn is an already created JDBC connection
+Statement stmt = null;
+ResultSet rs = null;
+
+try {
+    stmt = conn.createStatement();
+    rs = stmt.executeQuery("SELECT foo FROM bar");
+
+    // or alternatively, if you don't know ahead of time that
+    // the query will be a SELECT...
+
+    if (stmt.execute("SELECT foo FROM bar")) {
+        rs = stmt.getResultSet();
+    }
+
+    // Now do something with the ResultSet ....
+} finally {
+    // it is a good idea to release
+    // resources in a finally{} block
+    // in reverse-order of their creation
+    // if they are no-longer needed
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException sqlEx) { // ignore }
+
+        rs = null;
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException sqlEx) { // ignore }
+
+        stmt = null;
+    }
+}</pre></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-usagenotes-statements-callable"></a>1.5.1.3. Using <code class="literal">CallableStatements</code> to Execute Stored Procedures</h5></div></div></div><p>
+          Starting with MySQL server version 5.0 when used with
+          Connector/J 3.1.1 or newer, the
+          <code class="classname">java.sql.CallableStatement</code> interface is
+          fully implemented with the exception of the
+          <code class="literal">getParameterMetaData()</code> method.
+        </p><p>
+          See ???, for more information
+          on MySQL stored procedures.
+        </p><p>
+          Connector/J exposes stored procedure functionality through
+          JDBC's <code class="classname">CallableStatement</code> interface.
+        </p><p><b>Note. </b>
+            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>
+          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)
+BEGIN
+    DECLARE z INT;
+    SET z = inOutParam + 1;
+    SET inOutParam = z;
+
+    SELECT inputParam;
+
+    SELECT CONCAT('zyxw', inputParam);
+END</pre></div><p>
+        </p><p>
+          To use the <code class="literal">demoSp</code> procedure with
+          Connector/J, follow these steps:
+        </p><div class="orderedlist"><ol type="1"><li><p>
+              Prepare the callable statement by using
+              <code class="literal">Connection.prepareCall()</code> .
+            </p><p>
+              Notice that you have to use JDBC escape syntax, and that
+              the parentheses surrounding the parameter placeholders are
+              not optional:
+            </p><div class="example"><a name="connector-j-examples-preparecall"></a><p class="title"><b>Example 4. Using <code class="literal">Connection.prepareCall()</code></b></p><pre class="programlisting">import java.sql.CallableStatement;
+
+...
+
+    //
+    // Prepare a call to the stored procedure 'demoSp'
+    // with two parameters
+    //
+    // Notice the use of JDBC-escape syntax ({call ...})
+    //
+
+    CallableStatement cStmt = conn.prepareCall("{call demoSp(?, ?)}");
+
+
+
+    cStmt.setString(1, "abcdefg");</pre></div><p><b>Note. </b>
+                <code class="literal">Connection.prepareCall()</code> 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
+                <code class="literal">Connection.prepareCall()</code> by reusing
+                <code class="classname">CallableStatement</code> instances in
+                your code.
+              </p></li><li><p>
+              Register the output parameters (if any exist)
+            </p><p>
+              To retrieve the values of output parameters (parameters
+              specified as <code class="literal">OUT</code> or
+              <code class="literal">INOUT</code> when you created the stored
+              procedure), JDBC requires that they be specified before
+              statement execution using the various
+              <code class="literal">registerOutputParameter()</code> methods in
+              the <code class="classname">CallableStatement</code> interface:
+
+              </p><div class="example"><a name="connector-j-examples-output-param"></a><p class="title"><b>Example 5. Registering output parameters</b></p><pre class="programlisting">import java.sql.Types;
+...
+//
+// Connector/J supports both named and indexed
+// output parameters. You can register output
+// parameters using either method, as well
+// as retrieve output parameters using either
+// method, regardless of what method was
+// used to register them.
+//
+// The following examples show how to use
+// the various methods of registering
+// output parameters (you should of course
+// use only one registration per parameter).
+//
+
+//
+// Registers the second parameter as output, and
+// uses the type 'INTEGER' for values returned from
+// getObject()
+//
+
+cStmt.registerOutParameter(2, Types.INTEGER);
+
+//
+// Registers the named parameter 'inOutParam', and
+// uses the type 'INTEGER' for values returned from
+// getObject()
+//
+
+cStmt.registerOutParameter("inOutParam", Types.INTEGER);
+...
+</pre></div><p>
+            </p></li><li><p>
+              Set the input parameters (if any exist)
+            </p><p>
+              Input and in/out parameters are set as for
+              <code class="classname">PreparedStatement</code> objects. However,
+              <code class="classname">CallableStatement</code> also supports
+              setting parameters by name:
+
+              </p><div class="example"><a name="connector-j-examples-callablestatement"></a><p class="title"><b>Example 6. Setting <code class="literal">CallableStatement</code> input parameters</b></p><pre class="programlisting">...
+
+    //
+    // Set a parameter by index
+    //
+
+    cStmt.setString(1, "abcdefg");
+
+    //
+    // Alternatively, set a parameter using
+    // the parameter name
+    //
+
+    cStmt.setString("inputParameter", "abcdefg");
+
+    //
+    // Set the 'in/out' parameter using an index
+    //
+
+    cStmt.setInt(2, 1);
+
+    //
+    // Alternatively, set the 'in/out' parameter
+    // by name
+    //
+
+    cStmt.setInt("inOutParam", 1);
+
+...</pre></div><p>
+            </p></li><li><p>
+              Execute the <code class="classname">CallableStatement</code>, and
+              retrieve any result sets or output parameters.
+            </p><p>
+              Although <code class="classname">CallableStatement</code> supports
+              calling any of the <code class="classname">Statement</code>
+              execute methods (<code class="literal">executeUpdate()</code>,
+              <code class="literal">executeQuery()</code> or
+              <code class="literal">execute()</code>), the most flexible method to
+              call is <code class="literal">execute()</code>, as you do not need
+              to know ahead of time if the stored procedure returns
+              result sets:
+
+              </p><div class="example"><a name="connector-j-examples-retrieving-results-params"></a><p class="title"><b>Example 7. Retrieving results and output parameter values</b></p><pre class="programlisting">...
+
+    boolean hadResults = cStmt.execute();
+
+    //
+    // Process all returned result sets
+    //
+
+    while (hadResults) {
+        ResultSet rs = cStmt.getResultSet();
+
+        // process result set
+        ...
+
+        hadResults = rs.getMoreResults();
+    }
+
+    //
+    // Retrieve output parameters
+    //
+    // Connector/J supports both index-based and
+    // name-based retrieval
+    //
+
+    int outputValue = cStmt.getInt(2); // index-based
+
+    outputValue = cStmt.getInt("inOutParam"); // name-based
+
+...</pre></div><p>
+            </p></li></ol></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-usagenotes-last-insert-id"></a>1.5.1.4. Retrieving <code class="literal">AUTO_INCREMENT</code> Column Values</h5></div></div></div><p>
+          Before version 3.0 of the JDBC API, there was no standard way
+          of retrieving key values from databases that supported auto
+          increment or identity columns. With older JDBC drivers for
+          MySQL, you could always use a MySQL-specific method on the
+          <code class="classname">Statement</code> interface, or issue the query
+          <code class="literal">SELECT LAST_INSERT_ID()</code> after issuing an
+          <code class="literal">INSERT</code> to a table that had an
+          <code class="literal">AUTO_INCREMENT</code> key. Using the
+          MySQL-specific method call isn't portable, and issuing a
+          <code class="literal">SELECT</code> to get the
+          <code class="literal">AUTO_INCREMENT</code> key's value requires another
+          round-trip to the database, which isn't as efficient as
+          possible. The following code snippets demonstrate the three
+          different ways to retrieve <code class="literal">AUTO_INCREMENT</code>
+          values. First, we demonstrate the use of the new JDBC-3.0
+          method <code class="literal">getGeneratedKeys()</code> which is now the
+          preferred method to use if you need to retrieve
+          <code class="literal">AUTO_INCREMENT</code> keys and have access to
+          JDBC-3.0. The second example shows how you can retrieve the
+          same value using a standard <code class="literal">SELECT
+          LAST_INSERT_ID()</code> query. The final example shows how
+          updatable result sets can retrieve the
+          <code class="literal">AUTO_INCREMENT</code> value when using the
+          <code class="literal">insertRow()</code> method.
+
+          </p><div class="example"><a name="connector-j-examples-autoincrement-getgeneratedkeys"></a><p class="title"><b>Example 8. Retrieving <code class="literal">AUTO_INCREMENT</code> column values using
+              <code class="literal">Statement.getGeneratedKeys()</code></b></p><pre class="programlisting">   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets assuming you have a
+    // Connection 'conn' to a MySQL database already
+    // available
+
+    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+                                java.sql.ResultSet.CONCUR_UPDATABLE);
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Insert one row that will generate an AUTO INCREMENT
+    // key in the 'priKey' field
+    //
+
+    stmt.executeUpdate(
+            "INSERT INTO autoIncTutorial (dataField) "
+            + "values ('Can I Get the Auto Increment Field?')",
+            Statement.RETURN_GENERATED_KEYS);
+
+    //
+    // Example of using Statement.getGeneratedKeys()
+    // to retrieve the value of an auto-increment
+    // value
+    //
+
+    int autoIncKeyFromApi = -1;
+
+    rs = stmt.getGeneratedKeys();
+
+    if (rs.next()) {
+        autoIncKeyFromApi = rs.getInt(1);
+    } else {
+
+        // throw an exception from here
+    }
+
+    rs.close();
+
+    rs = null;
+
+    System.out.println("Key returned from getGeneratedKeys():"
+        + autoIncKeyFromApi);
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+</pre></div><p>
+
+          </p><div class="example"><a name="connector-j-examples-autoincrement-select"></a><p class="title"><b>Example 9. Retrieving <code class="literal">AUTO_INCREMENT</code> column values using
+              <code class="literal">SELECT LAST_INSERT_ID()</code></b></p><pre class="programlisting">   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets.
+
+    stmt = conn.createStatement();
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Insert one row that will generate an AUTO INCREMENT
+    // key in the 'priKey' field
+    //
+
+    stmt.executeUpdate(
+            "INSERT INTO autoIncTutorial (dataField) "
+            + "values ('Can I Get the Auto Increment Field?')");
+
+    //
+    // Use the MySQL LAST_INSERT_ID()
+    // function to do the same thing as getGeneratedKeys()
+    //
+
+    int autoIncKeyFromFunc = -1;
+    rs = stmt.executeQuery("SELECT LAST_INSERT_ID()");
+
+    if (rs.next()) {
+        autoIncKeyFromFunc = rs.getInt(1);
+    } else {
+        // throw an exception from here
+    }
+
+    rs.close();
+
+    System.out.println("Key returned from " + "'SELECT LAST_INSERT_ID()': "
+        + autoIncKeyFromFunc);
+
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+   </pre></div><p>
+
+          </p><div class="example"><a name="connector-j-examples-autoincrement-updateable-resultsets"></a><p class="title"><b>Example 10. Retrieving <code class="literal">AUTO_INCREMENT</code> column values in
+              <code class="literal">Updatable ResultSets</code></b></p><pre class="programlisting">   Statement stmt = null;
+   ResultSet rs = null;
+
+   try {
+
+    //
+    // Create a Statement instance that we can use for
+    // 'normal' result sets as well as an 'updatable'
+    // one, assuming you have a Connection 'conn' to
+    // a MySQL database already available
+    //
+
+    stmt = conn.createStatement(java.sql.ResultSet.TYPE_FORWARD_ONLY,
+                                java.sql.ResultSet.CONCUR_UPDATABLE);
+
+    //
+    // Issue the DDL queries for the table for this example
+    //
+
+    stmt.executeUpdate("DROP TABLE IF EXISTS autoIncTutorial");
+    stmt.executeUpdate(
+            "CREATE TABLE autoIncTutorial ("
+            + "priKey INT NOT NULL AUTO_INCREMENT, "
+            + "dataField VARCHAR(64), PRIMARY KEY (priKey))");
+
+    //
+    // Example of retrieving an AUTO INCREMENT key
+    // from an updatable result set
+    //
+
+    rs = stmt.executeQuery("SELECT priKey, dataField "
+       + "FROM autoIncTutorial");
+
+    rs.moveToInsertRow();
+
+    rs.updateString("dataField", "AUTO INCREMENT here?");
+    rs.insertRow();
+
+    //
+    // the driver adds rows at the end
+    //
+
+    rs.last();
+
+    //
+    // We should now be on the row we just inserted
+    //
+
+    int autoIncKeyFromRS = rs.getInt("priKey");
+
+    rs.close();
+
+    rs = null;
+
+    System.out.println("Key returned for inserted row: "
+        + autoIncKeyFromRS);
+
+} finally {
+
+    if (rs != null) {
+        try {
+            rs.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+
+    if (stmt != null) {
+        try {
+            stmt.close();
+        } catch (SQLException ex) {
+            // ignore
+        }
+    }
+}
+
+
+   </pre></div><p>
+
+          When you run the preceding example code, you should get the
+          following output: Key returned from
+          <code class="literal">getGeneratedKeys()</code>: 1 Key returned from
+          <code class="literal">SELECT LAST_INSERT_ID()</code>: 1 Key returned for
+          inserted row: 2 You should be aware, that at times, it can be
+          tricky to use the <code class="literal">SELECT LAST_INSERT_ID()</code>
+          query, as that function's value is scoped to a connection. So,
+          if some other query happens on the same connection, the value
+          will be overwritten. On the other hand, the
+          <code class="literal">getGeneratedKeys()</code> method is scoped by the
+          <code class="classname">Statement</code> instance, so it can be used
+          even if other queries happen on the same connection, but not
+          on the same <code class="classname">Statement</code> instance.
+        </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-usagenotes-j2ee"></a>1.5.2. Using Connector/J with J2EE and Other Java Frameworks</h4></div></div></div><p>
+        This section describes how to use Connector/J in several
+        contexts.
+      </p><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-usagenotes-j2ee-concepts"></a>1.5.2.1. General J2EE Concepts</h5></div></div></div><p>
+          This section provides general background on J2EE concepts that
+          pertain to use of Connector/J.
+        </p><div class="section" lang="en"><div class="titlepage"><div><div><h6 class="title"><a name="connector-j-usagenotes-j2ee-concepts-connection-pooling"></a>1.5.2.1.1. Understanding Connection Pooling</h6></div></div></div><p>
+            Connection pooling is a technique of creating and managing a
+            pool of connections that are ready for use by any thread
+            that needs them.
+          </p><p>
+            This technique of pooling connections is based on the fact
+            that most applications only need a thread to have access to
+            a JDBC connection when they are actively processing a
+            transaction, which usually take only milliseconds to
+            complete. When not processing a transaction, the connection
+            would otherwise sit idle. Instead, connection pooling allows
+            the idle connection to be used by some other thread to do
+            useful work.
+          </p><p>
+            In practice, when a thread needs to do work against a MySQL
+            or other database with JDBC, it requests a connection from
+            the pool. When the thread is finished using the connection,
+            it returns it to the pool, so that it may be used by any
+            other threads that want to use it.
+          </p><p>
+            When the connection is loaned out from the pool, it is used
+            exclusively by the thread that requested it. From a
+            programming point of view, it is the same as if your thread
+            called <code class="literal">DriverManager.getConnection()</code>
+            every time it needed a JDBC connection, however with
+            connection pooling, your thread may end up using either a
+            new, or already-existing connection.
+          </p><p>
+            Connection pooling can greatly increase the performance of
+            your Java application, while reducing overall resource
+            usage. The main benefits to connection pooling are:
+          </p><div class="itemizedlist"><ul type="disc"><li><p>
+                Reduced connection creation time
+              </p><p>
+                Although this is not usually an issue with the quick
+                connection setup that MySQL offers compared to other
+                databases, creating new JDBC connections still incurs
+                networking and JDBC driver overhead that will be avoided
+                if connections are recycled.
+              </p></li><li><p>
+                Simplified programming model
+              </p><p>
+                When using connection pooling, each individual thread
+                can act as though it has created its own JDBC
+                connection, allowing you to use straight-forward JDBC
+                programming techniques.
+              </p></li><li><p>
+                Controlled resource usage
+              </p><p>
+                If you don't use connection pooling, and instead create
+                a new connection every time a thread needs one, your
+                application's resource usage can be quite wasteful and
+                lead to unpredictable behavior under load.
+              </p></li></ul></div><p>
+            Remember that each connection to MySQL has overhead (memory,
+            CPU, context switches, and so forth) on both the client and
+            server side. Every connection limits how many resources
+            there are available to your application as well as the MySQL
+            server. Many of these resources will be used whether or not
+            the connection is actually doing any useful work!
+          </p><p>
+            Connection pools can be tuned to maximize performance, while
+            keeping resource utilization below the point where your
+            application will start to fail rather than just run slower.
+          </p><p>
+            Luckily, Sun has standardized the concept of connection
+            pooling in JDBC through the JDBC-2.0 Optional interfaces,
+            and all major application servers have implementations of
+            these APIs that work fine with MySQL Connector/J.
+          </p><p>
+            Generally, you configure a connection pool in your
+            application server configuration files, and access it via
+            the Java Naming and Directory Interface (JNDI). The
+            following code shows how you might use a connection pool
+            from an application deployed in a J2EE application server:
+
+            </p><div class="example"><a name="connector-j-examples-connectionpool-j2ee"></a><p class="title"><b>Example 11. Using a connection pool with a J2EE application server</b></p><pre class="programlisting">import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import javax.naming.InitialContext;
+import javax.sql.DataSource;
+
+
+public class MyServletJspOrEjb {
+
+    public void doSomething() throws Exception {
+        /*
+         * Create a JNDI Initial context to be able to
+         *  lookup  the DataSource
+         *
+         * In production-level code, this should be cached as
+         * an instance or static variable, as it can
+         * be quite expensive to create a JNDI context.
+         *
+         * Note: This code only works when you are using servlets
+         * or EJBs in a J2EE application server. If you are
+         * using connection pooling in standalone Java code, you
+         * will have to create/configure datasources using whatever
+         * mechanisms your particular connection pooling library
+         * provides.
+         */
+
+        InitialContext ctx = new InitialContext();
+
+         /*
+          * Lookup the DataSource, which will be backed by a pool
+          * that the application server provides. DataSource instances
+          * are also a good candidate for caching as an instance
+          * variable, as JNDI lookups can be expensive as well.
+          */
+
+        DataSource ds = (DataSource)ctx.lookup("java:comp/env/jdbc/MySQLDB");
+
+        /*
+         * The following code is what would actually be in your
+         * Servlet, JSP or EJB 'service' method...where you need
+         * to work with a JDBC connection.
+         */
+
+        Connection conn = null;
+        Statement stmt = null;
+
+        try {
+            conn = ds.getConnection();
+
+            /*
+             * Now, use normal JDBC programming to work with
+             * MySQL, making sure to close each resource when you're
+             * finished with it, which allows the connection pool
+             * resources to be recovered as quickly as possible
+             */
+
+            stmt = conn.createStatement();
+            stmt.execute("SOME SQL QUERY");
+
+            stmt.close();
+            stmt = null;
+
+            conn.close();
+            conn = null;
+        } finally {
+            /*
+             * close any jdbc instances here that weren't
+             * explicitly closed during normal code path, so
+             * that we don't 'leak' resources...
+             */
+
+            if (stmt != null) {
+                try {
+                    stmt.close();
+                } catch (sqlexception sqlex) {
+                    // ignore -- as we can't do anything about it here
+                }
+
+                stmt = null;
+            }
+
+            if (conn != null) {
+                try {
+                    conn.close();
+                } catch (sqlexception sqlex) {
+                    // ignore -- as we can't do anything about it here
+                }
+
+                conn = null;
+            }
+        }
+    }
+}</pre></div><p>
+
+            As shown in the example above, after obtaining the JNDI
+            InitialContext, and looking up the DataSource, the rest of
+            the code should look familiar to anyone who has done JDBC
+            programming in the past.
+          </p><p>
+            The most important thing to remember when using connection
+            pooling is to make sure that no matter what happens in your
+            code (exceptions, flow-of-control, and so forth),
+            connections, and anything created by them (such as
+            statements or result sets) are closed, so that they may be
+            re-used, otherwise they will be stranded, which in the best
+            case means that the MySQL server resources they represent
+            (such as buffers, locks, or sockets) may be tied up for some
+            time, or worst case, may be tied up forever.
+          </p><p>
+            What's the Best Size for my Connection Pool?
+          </p><p>
+            As with all other configuration rules-of-thumb, the answer
+            is: it depends. Although the optimal size depends on
+            anticipated load and average database transaction time, the
+            optimum connection pool size is smaller than you might
+            expect. If you take Sun's Java Petstore blueprint
+            application for example, a connection pool of 15-20
+            connections can serve a relatively moderate load (600
+            concurrent users) using MySQL and Tomcat with response times
+            that are acceptable.
+          </p><p>
+            To correctly size a connection pool for your application,
+            you should create load test scripts with tools such as
+            Apache JMeter or The Grinder, and load test your
+            application.
+          </p><p>
+            An easy way to determine a starting point is to configure
+            your connection pool's maximum number of connections to be
+            unbounded, run a load test, and measure the largest amount
+            of concurrently used connections. You can then work backward
+            from there to determine what values of minimum and maximum
+            pooled connections give the best performance for your
+            particular application.
+          </p></div></div><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-usagenotes-tomcat"></a>1.5.2.2. Using Connector/J with Tomcat</h5></div></div></div><p>
+          The following instructions are based on the instructions for
+          Tomcat-5.x, available at
+          <a href="http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jndi-datasource-examples-howto.html" target="_top">http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jndi-datasource-examples-howto.html</a>
+          which is current at the time this document was written.
+        </p><p>
+          First, install the .jar file that comes with Connector/J in
+          <code class="filename">$CATALINA_HOME/common/lib</code> so that it is
+          available to all applications installed in the container.
+        </p><p>
+          Next, Configure the JNDI DataSource by adding a declaration
+          resource to
+          <code class="filename">$CATALINA_HOME/conf/server.xml</code> in the
+          context that defines your web application:
+        </p><pre class="programlisting">&lt;Context ....&gt;
+
+  ...
+
+  &lt;Resource name="jdbc/MySQLDB"
+               auth="Container"
+               type="javax.sql.DataSource"/&gt;
+
+  &lt;!-- The name you used above, must match _exactly_ here!
+
+       The connection pool will be bound into JNDI with the name
+       "java:/comp/env/jdbc/MySQLDB"
+  --&gt;
+
+  &lt;ResourceParams name="jdbc/MySQLDB"&gt;
+    &lt;parameter&gt;
+      &lt;name&gt;factory&lt;/name&gt;
+      &lt;value&gt;org.apache.commons.dbcp.BasicDataSourceFactory&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &lt;!-- Don't set this any higher than max_connections on your
+         MySQL server, usually this should be a 10 or a few 10's
+         of connections, not hundreds or thousands --&gt;
+
+    &lt;parameter&gt;
+      &lt;name&gt;maxActive&lt;/name&gt;
+      &lt;value&gt;10&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &lt;!-- You don't want to many idle connections hanging around
+         if you can avoid it, only enough to soak up a spike in
+         the load --&gt;
+
+    &lt;parameter&gt;
+      &lt;name&gt;maxIdle&lt;/name&gt;
+      &lt;value&gt;5&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &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
+         how much of a performance penalty you're willing to pay
+         to ensure 'freshness' of the connection --&gt;
+
+    &lt;parameter&gt;
+      &lt;name&gt;validationQuery&lt;/name&gt;
+      &lt;value&gt;SELECT 1&lt;/value&gt;
+    &lt;/parameter&gt;
+
+   &lt;!-- The most conservative approach is to test connections
+        before they're given to your application. For most applications
+        this is okay, the query used above is very small and takes
+        no real server resources to process, other than the time used
+        to traverse the network.
+
+        If you have a high-load application you'll need to rely on
+        something else. --&gt;
+
+    &lt;parameter&gt;
+      &lt;name&gt;testOnBorrow&lt;/name&gt;
+      &lt;value&gt;true&lt;/value&gt;
+    &lt;/parameter&gt;
+
+   &lt;!-- Otherwise, or in addition to testOnBorrow, you can test
+        while connections are sitting idle --&gt;
+
+    &lt;parameter&gt;
+      &lt;name&gt;testWhileIdle&lt;/name&gt;
+      &lt;value&gt;true&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &lt;!-- You have to set this value, otherwise even though
+         you've asked connections to be tested while idle,
+         the idle evicter thread will never run --&gt;
+
+    &lt;parameter&gt;
+      &lt;name&gt;timeBetweenEvictionRunsMillis&lt;/name&gt;
+      &lt;value&gt;10000&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &lt;!-- Don't allow connections to hang out idle too long,
+         never longer than what wait_timeout is set to on the
+         server...A few minutes or even fraction of a minute
+         is sometimes okay here, it depends on your application
+         and how much spikey load it will see --&gt;
+
+    &lt;parameter&gt;
+      &lt;name&gt;minEvictableIdleTimeMillis&lt;/name&gt;
+      &lt;value&gt;60000&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &lt;!-- Username and password used when connecting to MySQL --&gt;
+
+    &lt;parameter&gt;
+     &lt;name&gt;username&lt;/name&gt;
+     &lt;value&gt;someuser&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &lt;parameter&gt;
+     &lt;name&gt;password&lt;/name&gt;
+     &lt;value&gt;somepass&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &lt;!-- Class name for the Connector/J driver --&gt;
+
+    &lt;parameter&gt;
+       &lt;name&gt;driverClassName&lt;/name&gt;
+       &lt;value&gt;com.mysql.jdbc.Driver&lt;/value&gt;
+    &lt;/parameter&gt;
+
+    &lt;!-- The JDBC connection url for connecting to MySQL, notice
+         that if you want to pass any other MySQL-specific parameters
+         you should pass them here in the URL, setting them using the
+         parameter tags above will have no effect, you will also
+         need to use &amp;amp; to separate parameter values as the
+         ampersand is a reserved character in XML --&gt;
+
+    &lt;parameter&gt;
+      &lt;name&gt;url&lt;/name&gt;
+      &lt;value&gt;jdbc:mysql://localhost:3306/test&lt;/value&gt;
+    &lt;/parameter&gt;
+
+  &lt;/ResourceParams&gt;
+&lt;/Context&gt;</pre><p>
+          In general, you should follow the installation instructions
+          that come with your version of Tomcat, as the way you
+          configure datasources in Tomcat changes from time-to-time, and
+          unfortunately if you use the wrong syntax in your XML file,
+          you will most likely end up with an exception similar to the
+          following:
+        </p><pre class="programlisting">Error: java.sql.SQLException: Cannot load JDBC driver class 'null ' SQL
+state: null </pre></div><div class="section" lang="en"><div class="titlepage"><div><div><h5 class="title"><a name="connector-j-usagenotes-jboss"></a>1.5.2.3. Using Connector/J with JBoss</h5></div></div></div><p>
+          These instructions cover JBoss-4.x. To make the JDBC driver
+          classes available to the application server, copy the .jar
+          file that comes with Connector/J to the
+          <code class="filename">lib</code> directory for your server
+          configuration (which is usually called
+          <code class="filename">default</code>). Then, in the same configuration
+          directory, in the subdirectory named deploy, create a
+          datasource configuration file that ends with "-ds.xml", which
+          tells JBoss to deploy this file as a JDBC Datasource. The file
+          should have the following contents:
+        </p><pre class="programlisting">&lt;datasources&gt;
+    &lt;local-tx-datasource&gt;
+        &lt;!-- This connection pool will be bound into JNDI with the name
+             "java:/MySQLDB" --&gt;
+
+        &lt;jndi-name&gt;MySQLDB&lt;/jndi-name&gt;
+        &lt;connection-url&gt;jdbc:mysql://localhost:3306/dbname&lt;/connection-url&gt;
+        &lt;driver-class&gt;com.mysql.jdbc.Driver&lt;/driver-class&gt;
+        &lt;user-name&gt;user&lt;/user-name&gt;
+        &lt;password&gt;pass&lt;/password&gt;
+
+        &lt;min-pool-size&gt;5&lt;/min-pool-size&gt;
+
+        &lt;!-- Don't set this any higher than max_connections on your
+         MySQL server, usually this should be a 10 or a few 10's
+         of connections, not hundreds or thousands --&gt;
+
+        &lt;max-pool-size&gt;20&lt;/max-pool-size&gt;
+
+        &lt;!-- Don't allow connections to hang out idle too long,
+         never longer than what wait_timeout is set to on the
+         server...A few minutes is usually okay here,
+         it depends on your application
+         and how much spikey load it will see --&gt;
+
+        &lt;idle-timeout-minutes&gt;5&lt;/idle-timeout-minutes&gt;
+
+        &lt;!-- If you're using Connector/J 3.1.8 or newer, you can use
+             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;/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>
+        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.
+      </p><p><span class="bold"><strong>Questions</strong></span></p><div class="itemizedlist"><ul type="disc"><li><p><a href="#qandaitem-1-5-3-1">1.5.3.1: </a>
+              When I try to connect to the database with MySQL
+              Connector/J, I get the following exception:
+            </p><pre class="programlisting">SQLException: Server configuration denies access to data source
+SQLState: 08001
+VendorError: 0</pre><p>
+              What's going on? I can connect just fine with the MySQL
+              command-line client.
+            </p></li><li><p><a href="#qandaitem-1-5-3-2">1.5.3.2: </a>
+              My application throws an SQLException 'No Suitable
+              Driver'. Why is this happening?
+            </p></li><li><p><a href="#qandaitem-1-5-3-3">1.5.3.3: </a>
+              I'm trying to use MySQL Connector/J in an applet or
+              application and I get an exception similar to:
+            </p><pre class="programlisting">SQLException: Cannot connect to MySQL server on host:3306.
+Is there a MySQL server running on the machine/port you
+are trying to connect to?
+
+(java.security.AccessControlException)
+SQLState: 08S01
+VendorError: 0 </pre></li><li><p><a href="#qandaitem-1-5-3-4">1.5.3.4: </a>
+              I have a servlet/application that works fine for a day,
+              and then stops working overnight
+            </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></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:
+            </strong></span></p><pre class="programlisting">SQLException: Server configuration denies access to data source
+SQLState: 08001
+VendorError: 0</pre><p><span class="bold"><strong>
+              What's going on? I can connect just fine with the MySQL
+              command-line client.
+            </strong></span></p><p>
+              MySQL Connector/J must use TCP/IP sockets to connect to
+              MySQL, as Java does not support Unix Domain Sockets.
+              Therefore, when MySQL Connector/J connects to MySQL, the
+              security manager in MySQL server will use its grant tables
+              to determine whether the connection should be allowed.
+            </p><p>
+              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>
+                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,
+                and use something other than
+                <code class="literal">localhost</code> for the host. The
+                <span><strong class="command">mysql</strong></span> command-line client will use
+                Unix domain sockets if you use the special hostname
+                <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>
+                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>
+              My application throws an SQLException 'No Suitable
+              Driver'. Why is this happening?
+            </strong></span></p><p>
+              There are three possible causes for this error:
+            </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>.
+                </p></li><li><p>
+                  The format of your connection URL is incorrect, or you
+                  are referencing the wrong JDBC driver.
+                </p></li><li><p>
+                  When using DriverManager, the
+                  <code class="literal">jdbc.drivers</code> system property has
+                  not been populated with the location of the
+                  Connector/J driver.
+                </p></li></ul></div><p><a name="qandaitem-1-5-3-3"></a><span class="bold"><strong>1.5.3.3: </strong></span><span class="bold"><strong>
+              I'm trying to use MySQL Connector/J in an applet or
+              application and I get an exception similar to:
+            </strong></span></p><pre class="programlisting">SQLException: Cannot connect to MySQL server on host:3306.
+Is there a MySQL server running on the machine/port you
+are trying to connect to?
+
+(java.security.AccessControlException)
+SQLState: 08S01
+VendorError: 0 </pre><p>
+              Either you're running an Applet, your MySQL server has
+              been installed with the "--skip-networking" option set, or
+              your MySQL server has a firewall sitting in front of it.
+            </p><p>
+              Applets can only make network connections back to the
+              machine that runs the web server that served the .class
+              files for the applet. This means that MySQL must run on
+              the same machine (or you must have some sort of port
+              re-direction) for this to work. This also means that you
+              will not be able to test applets from your local file
+              system, you must always deploy them to a web server.
+            </p><p>
+              MySQL Connector/J can only communicate with MySQL using
+              TCP/IP, as Java does not support Unix domain sockets.
+              TCP/IP communication with MySQL might be affected if MySQL
+              was started with the "--skip-networking" flag, or if it is
+              firewalled.
+            </p><p>
+              If MySQL has been started with the "--skip-networking"
+              option set (the Debian Linux package of MySQL server does
+              this for example), you need to comment it out in the file
+              /etc/mysql/my.cnf or /etc/my.cnf. Of course your my.cnf
+              file might also exist in the <code class="filename">data</code>
+              directory of your MySQL server, or anywhere else
+              (depending on how MySQL was compiled for your system).
+              Binaries created by MySQL AB always look in /etc/my.cnf
+              and [datadir]/my.cnf. If your MySQL server has been
+              firewalled, you will need to have the firewall configured
+              to allow TCP/IP connections from the host where your Java
+              code is running to the MySQL server on the port that MySQL
+              is listening to (by default, 3306).
+            </p><p><a name="qandaitem-1-5-3-4"></a><span class="bold"><strong>1.5.3.4: </strong></span><span class="bold"><strong>
+              I have a servlet/application that works fine for a day,
+              and then stops working overnight
+            </strong></span></p><p>
+              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
+              <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><p>
+              Also, you should be catching SQLExceptions in your
+              application and dealing with them, rather than propagating
+              them all the way until your application exits, this is
+              just good programming practice. MySQL Connector/J will set
+              the SQLState (see
+              <code class="literal">java.sql.SQLException.getSQLState()</code> in
+              your APIDOCS) to "08S01" when it encounters
+              network-connectivity issues during the processing of a
+              query. Your application code should then attempt to
+              re-connect to MySQL at this point.
+            </p><p>
+              The following (simplistic) example shows what code that
+              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;
+
+        //
+        // How many times do you want to retry the transaction
+        // (or at least _getting_ a connection)?
+        //
+        int retryCount = 5;
+
+        boolean transactionCompleted = false;
+
+        do {
+            try {
+                conn = getConnection(); // assume getting this from a
+                                        // javax.sql.DataSource, or the
+                                        // java.sql.DriverManager
+
+                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;
+
+                stmt = conn.createStatement();
+
+                String query = "SELECT foo FROM bar ORDER BY baz";
+
+                rs = stmt.executeQuery(query);
+
+                while (rs.next()) {
+                }
+
+                rs.close();
+                rs = null;
+
+                stmt.close();
+                stmt = null;
+
+                conn.commit();
+                conn.close();
+                conn = null;
+
+                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
+                //
+
+                String sqlState = sqlEx.getSQLState();
+
+                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 . . .
+                    }
+                }
+
+                if (stmt != null) {
+                    try {
+                        stmt.close();
+                    } catch (SQLException sqlEx) {
+                        // You'd probably want to log this as well . . .
+                    }
+                }
+
+                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;
+                    }
+                }
+            }
+        } while (!transactionCompleted &amp;&amp; (retryCount &gt; 0));
+    }</pre></div><p>
+            </p><p><b>Note. </b>
+                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
+                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 <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>
+              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>
+              Because MySQL does not have row identifiers, MySQL
+              Connector/J can only update result sets that have come
+              from queries on tables that have at least one primary key,
+              the query must select every primary key and the query can
+              only span one table (that is, no joins). This is outlined
+              in the JDBC specification.
+            </p><p>
+              Note that this issue only occurs when using updatable
+              result sets, and is caused because Connector/J is unable
+              to guarantee that it can identify the correct rows within
+              the result set to be updated without having a unique
+              reference to each row. There is no requirement to have a
+              unique field on a table if you are using
+              <code class="literal">UPDATE</code> or <code class="literal">DELETE</code>
+              statements on a table where you can individually specify
+              the criteria to be matched using a
+              <code class="literal">WHERE</code> clause.
+            </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
+        help from experienced users by using the MySQL and Java mailing
+        list. Archives and subscription information is available online
+        at <a href="http://lists.mysql.com/java" target="_top">http://lists.mysql.com/java</a>.
+      </p><p>
+        For information about subscribing to MySQL mailing lists or to
+        browse list archives, visit
+        <a href="http://lists.mysql.com/" target="_top">http://lists.mysql.com/</a>. See
+        <a href="http://dev.mysql.com/doc/refman/5.1/en/mailing-lists.html" target="_top">MySQL Mailing Lists</a>.
+      </p><p>
+        Community support from experienced users is also available
+        through the
+        <a href="http://forums.mysql.com/list.php?39" target="_top">JDBC
+        Forum</a>. You may also find help from other users in the
+        other MySQL Forums, located at
+        <a href="http://forums.mysql.com" target="_top">http://forums.mysql.com</a>. See
+        <a href="http://dev.mysql.com/doc/refman/5.1/en/forums.html" target="_top">MySQL Community Support at the MySQL Forums</a>.
+      </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-support-bug-report"></a>1.6.2. How to Report Connector/J Bugs or Problems</h4></div></div></div><p>
+        The normal place to report bugs is
+        <a href="http://bugs.mysql.com/" target="_top">http://bugs.mysql.com/</a>, which is the
+        address for our bugs database. This database is public, and can
+        be browsed and searched by anyone. If you log in to the system,
+        you will also be able to enter new reports.
+      </p><p>
+        If you have found a sensitive security bug in MySQL, you can
+        send email to
+        <a href="mailto:security_at_mysql.com" target="_top">security_at_mysql.com</a>.
+      </p><p>
+        Writing a good bug report takes patience, but doing it right the
+        first time saves time both for us and for yourself. A good bug
+        report, containing a full test case for the bug, makes it very
+        likely that we will fix the bug in the next release.
+      </p><p>
+        This section will help you write your report correctly so that
+        you don't waste your time doing things that may not help us much
+        or at all.
+      </p><p>
+        If you have a repeatable bug report, please report it to the
+        bugs database at <a href="http://bugs.mysql.com/" target="_top">http://bugs.mysql.com/</a>. Any bug
+        that we are able to repeat has a high chance of being fixed in
+        the next MySQL release.
+      </p><p>
+        To report other problems, you can use one of the MySQL mailing
+        lists.
+      </p><p>
+        Remember that it is possible for us to respond to a message
+        containing too much information, but not to one containing too
+        little. People often omit facts because they think they know the
+        cause of a problem and assume that some details don't matter.
+      </p><p>
+        A good principle is this: If you are in doubt about stating
+        something, state it. It is faster and less troublesome to write
+        a couple more lines in your report than to wait longer for the
+        answer if we must ask you to provide information that was
+        missing from the initial report.
+      </p><p>
+        The most common errors made in bug reports are (a) not including
+        the version number of Connector/J or MySQL used, and (b) not
+        fully describing the platform on which Connector/J is installed
+        (including the JVM version, and the platform type and version
+        number that MySQL itself is installed on).
+      </p><p>
+        This is highly relevant information, and in 99 cases out of 100,
+        the bug report is useless without it. Very often we get
+        questions like, “<span class="quote">Why doesn't this work for me?</span>”
+        Then we find that the feature requested wasn't implemented in
+        that MySQL version, or that a bug described in a report has
+        already been fixed in newer MySQL versions.
+      </p><p>
+        Sometimes the error is platform-dependent; in such cases, it is
+        next to impossible for us to fix anything without knowing the
+        operating system and the version number of the platform.
+      </p><p>
+        If at all possible, you should create a repeatable, stanalone
+        testcase that doesn't involve any third-party classes.
+      </p><p>
+        To streamline this process, we ship a base class for testcases
+        with Connector/J, named
+        '<code class="classname">com.mysql.jdbc.util.BaseBugReport</code>'. To
+        create a testcase for Connector/J using this class, create your
+        own class that inherits from
+        <code class="classname">com.mysql.jdbc.util.BaseBugReport</code> and
+        override the methods <code class="literal">setUp()</code>,
+        <code class="literal">tearDown()</code> and <code class="literal">runTest()</code>.
+      </p><p>
+        In the <code class="literal">setUp()</code> method, create code that
+        creates your tables, and populates them with any data needed to
+        demonstrate the bug.
+      </p><p>
+        In the <code class="literal">runTest()</code> method, create code that
+        demonstrates the bug using the tables and data you created in
+        the <code class="literal">setUp</code> method.
+      </p><p>
+        In the <code class="literal">tearDown()</code> method, drop any tables you
+        created in the <code class="literal">setUp()</code> method.
+      </p><p>
+        In any of the above three methods, you should use one of the
+        variants of the <code class="literal">getConnection()</code> method to
+        create a JDBC connection to MySQL:
+      </p><div class="itemizedlist"><ul type="disc"><li><p>
+            <code class="literal">getConnection()</code> - Provides a connection
+            to the JDBC URL specified in <code class="literal">getUrl()</code>. If
+            a connection already exists, that connection is returned,
+            otherwise a new connection is created.
+          </p></li><li><p>
+            <code class="literal">getNewConnection()</code> - Use this if you need
+            to get a new connection for your bug report (i.e. there's
+            more than one connection involved).
+          </p></li><li><p>
+            <code class="literal">getConnection(String url)</code> - Returns a
+            connection using the given URL.
+          </p></li><li><p>
+            <code class="literal">getConnection(String url, Properties
+            props)</code> - Returns a connection using the given URL
+            and properties.
+          </p></li></ul></div><p>
+        If you need to use a JDBC URL that is different from
+        'jdbc:mysql:///test', override the method
+        <code class="literal">getUrl()</code> as well.
+      </p><p>
+        Use the <code class="literal">assertTrue(boolean expression)</code> and
+        <code class="literal">assertTrue(String failureMessage, boolean
+        expression)</code> methods to create conditions that must be
+        met in your testcase demonstrating the behavior you are
+        expecting (vs. the behavior you are observing, which is why you
+        are most likely filing a bug report).
+      </p><p>
+        Finally, create a <code class="literal">main()</code> method that creates
+        a new instance of your testcase, and calls the
+        <code class="literal">run</code> method:
+      </p><pre class="programlisting">public static void main(String[] args) throws Exception {
+      new MyBugReport().run();
+ }</pre><p>
+        Once you have finished your testcase, and have verified that it
+        demonstrates the bug you are reporting, upload it with your bug
+        report to <a href="http://bugs.mysql.com/" target="_top">http://bugs.mysql.com/</a>.
+      </p></div><div class="section" lang="en"><div class="titlepage"><div><div><h4 class="title"><a name="connector-j-support-changelog"></a>1.6.3. Connector/J Change History</h4></div></div></div><p>
+        The Connector/J Change History (Changelog) is located with the
+        main Changelog for MySQL. See <a href="http://dev.mysql.com/doc/refman/5.1/en/cj-news.html" target="_top">MySQL Connector/J Change History</a>.
+      </p></div></div></div></body></html>

Added: branches/mysql-connector-java/upstream/5.0.4/docs/connector-j.pdf
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/docs/connector-j.pdf	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/docs/connector-j.pdf	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,2315 @@
+%PDF-1.3
+%ª«¬­
+4 0 obj
+<< /Type /Info
+/Producer (FOP 0.20.5) >>
+endobj
+5 0 obj
+<< /Length 2186 /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:~>
+endstream
+endobj
+6 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 5 0 R
+/Annots 7 0 R
+>>
+endobj
+7 0 obj
+[
+8 0 R
+9 0 R
+10 0 R
+11 0 R
+12 0 R
+13 0 R
+]
+endobj
+8 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 467.99 581.75 507.42 571.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://www.hibernate.org/)
+/S /URI >>
+/H /I
+>>
+endobj
+9 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 235.54 570.75 335.68 560.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://www.springframework.org/)
+/S /URI >>
+/H /I
+>>
+endobj
+10 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 493.71 570.75 537.32 560.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://ibatis.apache.org/)
+/S /URI >>
+/H /I
+>>
+endobj
+11 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 120.0 559.75 142.22 549.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://ibatis.apache.org/)
+/S /URI >>
+/H /I
+>>
+endobj
+12 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 474.75 188.06 464.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://java.sun.com/docs/books/tutorial/jdbc/basics/index.html)
+/S /URI >>
+/H /I
+>>
+endobj
+13 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 442.75 214.45 432.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://java.sun.com/developer/onlineTraining/Database/JDBCShortCourse/index.html)
+/S /URI >>
+/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
+>>
+endobj
+16 0 obj
+[
+17 0 R
+19 0 R
+20 0 R
+]
+endobj
+17 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 433.29 657.0 533.01 647.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 18 0 R
+/H /I
+>>
+endobj
+19 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 120.0 646.0 266.07 636.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 18 0 R
+/H /I
+>>
+endobj
+20 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 138.6 366.25 425.63 356.25 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 21 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
+>>
+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 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 26 0 R
+/H /I
+>>
+endobj
+27 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 120.0 442.224 194.7 432.224 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 26 0 R
+/H /I
+>>
+endobj
+28 0 obj
+<< /Length 3146 /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)~>
+endstream
+endobj
+29 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 28 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
+[
+33 0 R
+34 0 R
+]
+endobj
+33 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 428.28 180.75 537.18 170.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://subversion.tigris.org/)
+/S /URI >>
+/H /I
+>>
+endobj
+34 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 334.11 148.75 419.1 138.75 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://ant.apache.org/)
+/S /URI >>
+/H /I
+>>
+endobj
+35 0 obj
+<< /Length 2560 /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~>
+endstream
+endobj
+36 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
+>>
+endobj
+37 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 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 21 0 R
+/H /I
+>>
+endobj
+40 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 140.0 308.56 198.44 298.56 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 21 0 R
+/H /I
+>>
+endobj
+41 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 207.56 402.41 197.56 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 42 0 R
+/H /I
+>>
+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
+>>
+endobj
+45 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 165.56 264.97 155.56 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 46 0 R
+/H /I
+>>
+endobj
+47 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 144.56 362.32 134.56 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 48 0 R
+/H /I
+>>
+endobj
+49 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 123.56 312.47 113.56 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 50 0 R
+/H /I
+>>
+endobj
+51 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 102.56 394.2 92.56 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 52 0 R
+/H /I
+>>
+endobj
+53 0 obj
+<< /Length 2648 /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~>
+endstream
+endobj
+54 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
+>>
+endobj
+55 0 obj
+[
+56 0 R
+58 0 R
+60 0 R
+61 0 R
+63 0 R
+64 0 R
+66 0 R
+68 0 R
+]
+endobj
+56 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 709.0 378.29 699.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 57 0 R
+/H /I
+>>
+endobj
+58 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 688.0 439.43 678.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 59 0 R
+/H /I
+>>
+endobj
+60 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 677.0 277.44 667.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 59 0 R
+/H /I
+>>
+endobj
+61 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 656.0 439.43 646.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 62 0 R
+/H /I
+>>
+endobj
+63 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 645.0 235.44 635.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 62 0 R
+/H /I
+>>
+endobj
+64 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 624.0 518.98 614.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 65 0 R
+/H /I
+>>
+endobj
+66 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 603.0 419.12 593.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 67 0 R
+/H /I
+>>
+endobj
+68 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 582.0 353.85 572.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 69 0 R
+/H /I
+>>
+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
+>>
+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
+>>
+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
+>>
+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
+>>
+endobj
+78 0 obj
+<< /Length 3241 /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~>
+endstream
+endobj
+79 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 78 0 R
+>>
+endobj
+80 0 obj
+<< /Length 3226 /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~>
+endstream
+endobj
+81 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 80 0 R
+>>
+endobj
+82 0 obj
+<< /Length 3450 /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#~>
+endstream
+endobj
+83 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 82 0 R
+>>
+endobj
+84 0 obj
+<< /Length 3370 /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,~>
+endstream
+endobj
+85 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 84 0 R
+>>
+endobj
+86 0 obj
+<< /Length 3138 /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~>
+endstream
+endobj
+87 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 86 0 R
+>>
+endobj
+88 0 obj
+<< /Length 2950 /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~>
+endstream
+endobj
+89 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 88 0 R
+>>
+endobj
+90 0 obj
+<< /Length 2820 /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~>
+endstream
+endobj
+91 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 90 0 R
+>>
+endobj
+92 0 obj
+<< /Length 2568 /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^^~>
+endstream
+endobj
+93 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 92 0 R
+/Annots 94 0 R
+>>
+endobj
+94 0 obj
+[
+95 0 R
+]
+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
+>>
+endobj
+96 0 obj
+<< /Length 2460 /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%[/~>
+endstream
+endobj
+97 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 96 0 R
+>>
+endobj
+98 0 obj
+<< /Length 2395 /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~>
+endstream
+endobj
+99 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 98 0 R
+/Annots 100 0 R
+>>
+endobj
+100 0 obj
+[
+101 0 R
+]
+endobj
+101 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 194.18 149.25 480.27 139.25 ]
+/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
+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
+endobj
+103 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 102 0 R
+>>
+endobj
+104 0 obj
+<< /Length 3816 /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~>
+endstream
+endobj
+105 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 104 0 R
+>>
+endobj
+106 0 obj
+<< /Length 2205 /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~>
+endstream
+endobj
+107 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 106 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
+endobj
+109 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 108 0 R
+>>
+endobj
+110 0 obj
+<< /Length 2617 /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~>
+endstream
+endobj
+111 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 110 0 R
+>>
+endobj
+112 0 obj
+<< /Length 2186 /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~>
+endstream
+endobj
+113 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 112 0 R
+>>
+endobj
+114 0 obj
+<< /Length 1681 /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~>
+endstream
+endobj
+115 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 114 0 R
+>>
+endobj
+116 0 obj
+<< /Length 2575 /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~>
+endstream
+endobj
+117 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 116 0 R
+>>
+endobj
+118 0 obj
+<< /Length 1778 /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~>
+endstream
+endobj
+119 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 118 0 R
+>>
+endobj
+120 0 obj
+<< /Length 1881 /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,'~>
+endstream
+endobj
+121 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 120 0 R
+>>
+endobj
+122 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~>
+endstream
+endobj
+123 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 122 0 R
+>>
+endobj
+124 0 obj
+<< /Length 2476 /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~>
+endstream
+endobj
+125 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 124 0 R
+>>
+endobj
+126 0 obj
+<< /Length 2358 /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~>
+endstream
+endobj
+127 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 126 0 R
+>>
+endobj
+128 0 obj
+<< /Length 2630 /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~>
+endstream
+endobj
+129 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
+>>
+endobj
+130 0 obj
+[
+131 0 R
+132 0 R
+]
+endobj
+131 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 458.84 217.076 469.95 207.076 ]
+/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
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 120.0 206.076 457.72 196.076 ]
+/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
+133 0 obj
+<< /Length 2240 /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~>
+endstream
+endobj
+134 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 133 0 R
+>>
+endobj
+135 0 obj
+<< /Length 2498 /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~>
+endstream
+endobj
+136 0 obj
+<< /Type /Page
+/Parent 1 0 R
+/MediaBox [ 0 0 612 792 ]
+/Resources 3 0 R
+/Contents 135 0 R
+>>
+endobj
+137 0 obj
+<< /Length 2138 /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~>
+endstream
+endobj
+138 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
+>>
+endobj
+139 0 obj
+[
+140 0 R
+142 0 R
+144 0 R
+146 0 R
+148 0 R
+]
+endobj
+140 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 444.49 165.28 434.49 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 141 0 R
+/H /I
+>>
+endobj
+142 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 359.826 165.28 349.826 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 143 0 R
+/H /I
+>>
+endobj
+144 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 338.826 165.28 328.826 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 145 0 R
+/H /I
+>>
+endobj
+146 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 233.61 165.28 223.61 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 147 0 R
+/H /I
+>>
+endobj
+148 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 135.0 212.61 165.28 202.61 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 149 0 R
+/H /I
+>>
+endobj
+150 0 obj
+<< /Length 3025 /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-_('~>
+endstream
+endobj
+151 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
+>>
+endobj
+152 0 obj
+[
+153 0 R
+]
+endobj
+153 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 360.09 496.0 507.58 486.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 154 0 R
+/H /I
+>>
+endobj
+155 0 obj
+<< /Length 2455 /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;~>
+endstream
+endobj
+156 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
+>>
+endobj
+157 0 obj
+[
+158 0 R
+160 0 R
+]
+endobj
+158 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 396.7 698.0 532.5 688.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 159 0 R
+/H /I
+>>
+endobj
+160 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 120.0 687.0 415.25 677.0 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A 159 0 R
+/H /I
+>>
+endobj
+161 0 obj
+<< /Length 2273 /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~>
+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 2837 /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'~>
+endstream
+endobj
+164 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
+>>
+endobj
+165 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
+]
+endobj
+166 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 361.64 547.25 467.77 537.25 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://lists.mysql.com/java)
+/S /URI >>
+/H /I
+>>
+endobj
+167 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 481.95 526.25 493.06 516.25 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://lists.mysql.com/)
+/S /URI >>
+/H /I
+>>
+endobj
+168 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 120.0 515.25 201.69 505.25 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://lists.mysql.com/)
+/S /URI >>
+/H /I
+>>
+endobj
+169 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 223.63 515.25 312.53 505.25 ]
+/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
+170 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 413.3 483.25 466.92 473.25 ]
+/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
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 196.66 461.25 295.56 451.25 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://forums.mysql.com)
+/S /URI >>
+/H /I
+>>
+endobj
+172 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 317.5 461.25 527.52 451.25 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://dev.mysql.com/doc/refman/5.1/en/forums.html)
+/S /URI >>
+/H /I
+>>
+endobj
+173 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 259.7 403.5 351.94 393.5 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://bugs.mysql.com/)
+/S /URI >>
+/H /I
+>>
+endobj
+174 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 427.74 360.5 520.79 350.5 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (mailto:security_at_mysql.com)
+/S /URI >>
+/H /I
+>>
+endobj
+175 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 419.92 253.5 512.16 243.5 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://bugs.mysql.com/)
+/S /URI >>
+/H /I
+>>
+endobj
+176 0 obj
+<< /Length 2696 /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~>
+endstream
+endobj
+177 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
+>>
+endobj
+178 0 obj
+[
+179 0 R
+]
+endobj
+179 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 253.05 112.336 345.29 102.336 ]
+/C [ 0 0 0 ]
+/Border [ 0 0 0 ]
+/A << /URI (http://bugs.mysql.com/)
+/S /URI >>
+/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
+>>
+endobj
+182 0 obj
+[
+183 0 R
+]
+endobj
+183 0 obj
+<< /Type /Annot
+/Subtype /Link
+/Rect [ 120.0 698.0 269.16 688.0 ]
+/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
+186 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
+>> endobj
+188 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
+>> endobj
+190 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
+ /Count -1
+ /A 189 0 R
+>> endobj
+192 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
+>> endobj
+193 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
+ /Count -6
+ /A 154 0 R
+>> endobj
+195 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
+>> endobj
+196 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
+>> endobj
+198 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
+ /Count -2
+ /A 197 0 R
+>> endobj
+200 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
+>> endobj
+202 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
+>> endobj
+203 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
+>> endobj
+205 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
+>> endobj
+207 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
+>> endobj
+208 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
+>> endobj
+210 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
+>> endobj
+212 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
+>> endobj
+214 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
+>> endobj
+216 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
+>> endobj
+218 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
+>> endobj
+220 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
+>> endobj
+222 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
+ /Count -4
+ /A 221 0 R
+>> endobj
+224 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
+>> endobj
+226 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
+>> endobj
+228 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
+>> endobj
+230 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
+>> endobj
+231 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
+>> endobj
+233 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
+ /Count -1
+ /A 232 0 R
+>> endobj
+235 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
+>> endobj
+237 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
+>> endobj
+239 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
+>> endobj
+241 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
+>> endobj
+243 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
+ /Count -3
+ /A 242 0 R
+>> endobj
+245 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
+>> endobj
+247 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
+>> endobj
+249 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
+>> endobj
+250 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F3
+/BaseFont /Helvetica-Bold
+/Encoding /WinAnsiEncoding >>
+endobj
+251 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F5
+/BaseFont /Times-Roman
+/Encoding /WinAnsiEncoding >>
+endobj
+252 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F10
+/BaseFont /Courier-Oblique
+/Encoding /WinAnsiEncoding >>
+endobj
+253 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F6
+/BaseFont /Times-Italic
+/Encoding /WinAnsiEncoding >>
+endobj
+254 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F1
+/BaseFont /Helvetica
+/Encoding /WinAnsiEncoding >>
+endobj
+255 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F11
+/BaseFont /Courier-Bold
+/Encoding /WinAnsiEncoding >>
+endobj
+256 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F9
+/BaseFont /Courier
+/Encoding /WinAnsiEncoding >>
+endobj
+257 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F12
+/BaseFont /Courier-BoldOblique
+/Encoding /WinAnsiEncoding >>
+endobj
+258 0 obj
+<< /Type /Font
+/Subtype /Type1
+/Name /F7
+/BaseFont /Times-Bold
+/Encoding /WinAnsiEncoding >>
+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 ] >>
+endobj
+2 0 obj
+<< /Type /Catalog
+/Pages 1 0 R
+ /Outlines 184 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 ] ] >> >>
+ >>
+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 >> 
+/ProcSet [ /PDF /ImageC /Text ] >> 
+endobj
+18 0 obj
+<<
+/S /GoTo
+/D [31 0 R /XYZ 115.0 307.5 null]
+>>
+endobj
+21 0 obj
+<<
+/S /GoTo
+/D [15 0 R /XYZ 115.0 156.25 null]
+>>
+endobj
+26 0 obj
+<<
+/S /GoTo
+/D [123 0 R /XYZ 115.0 107.28 null]
+>>
+endobj
+42 0 obj
+<<
+/S /GoTo
+/D [109 0 R /XYZ 115.0 469.016 null]
+>>
+endobj
+44 0 obj
+<<
+/S /GoTo
+/D [111 0 R /XYZ 115.0 596.0 null]
+>>
+endobj
+46 0 obj
+<<
+/S /GoTo
+/D [113 0 R /XYZ 115.0 683.0 null]
+>>
+endobj
+48 0 obj
+<<
+/S /GoTo
+/D [113 0 R /XYZ 115.0 458.87 null]
+>>
+endobj
+50 0 obj
+<<
+/S /GoTo
+/D [113 0 R /XYZ 115.0 142.412 null]
+>>
+endobj
+52 0 obj
+<<
+/S /GoTo
+/D [115 0 R /XYZ 115.0 403.136 null]
+>>
+endobj
+57 0 obj
+<<
+/S /GoTo
+/D [117 0 R /XYZ 115.0 683.0 null]
+>>
+endobj
+59 0 obj
+<<
+/S /GoTo
+/D [117 0 R /XYZ 115.0 217.498 null]
+>>
+endobj
+62 0 obj
+<<
+/S /GoTo
+/D [119 0 R /XYZ 115.0 190.056 null]
+>>
+endobj
+65 0 obj
+<<
+/S /GoTo
+/D [121 0 R /XYZ 115.0 213.72 null]
+>>
+endobj
+67 0 obj
+<<
+/S /GoTo
+/D [127 0 R /XYZ 115.0 672.0 null]
+>>
+endobj
+69 0 obj
+<<
+/S /GoTo
+/D [156 0 R /XYZ 115.0 576.0 null]
+>>
+endobj
+141 0 obj
+<<
+/S /GoTo
+/D [138 0 R /XYZ 115.0 155.61 null]
+>>
+endobj
+143 0 obj
+<<
+/S /GoTo
+/D [151 0 R /XYZ 115.0 554.0 null]
+>>
+endobj
+145 0 obj
+<<
+/S /GoTo
+/D [151 0 R /XYZ 115.0 418.0 null]
+>>
+endobj
+147 0 obj
+<<
+/S /GoTo
+/D [151 0 R /XYZ 115.0 96.784 null]
+>>
+endobj
+149 0 obj
+<<
+/S /GoTo
+/D [162 0 R /XYZ 115.0 148.72 null]
+>>
+endobj
+154 0 obj
+<<
+/S /GoTo
+/D [15 0 R /XYZ 115.0 512.0 null]
+>>
+endobj
+159 0 obj
+<<
+/S /GoTo
+/D [54 0 R /XYZ 115.0 507.0 null]
+>>
+endobj
+184 0 obj
+<<
+ /First 186 0 R
+ /Last 243 0 R
+>> endobj
+185 0 obj
+<<
+/S /GoTo
+/D [null /XYZ 0.0 0.0 null]
+>>
+endobj
+187 0 obj
+<<
+/S /GoTo
+/D [null /XYZ 0.0 0.0 null]
+>>
+endobj
+189 0 obj
+<<
+/S /GoTo
+/D [6 0 R /XYZ 115.0 405.75 null]
+>>
+endobj
+191 0 obj
+<<
+/S /GoTo
+/D [6 0 R /XYZ 115.0 186.75 null]
+>>
+endobj
+194 0 obj
+<<
+/S /GoTo
+/D [15 0 R /XYZ 115.0 441.0 null]
+>>
+endobj
+197 0 obj
+<<
+/S /GoTo
+/D [23 0 R /XYZ 115.0 172.224 null]
+>>
+endobj
+199 0 obj
+<<
+/S /GoTo
+/D [29 0 R /XYZ 115.0 704.0 null]
+>>
+endobj
+201 0 obj
+<<
+/S /GoTo
+/D [31 0 R /XYZ 115.0 544.0 null]
+>>
+endobj
+204 0 obj
+<<
+/S /GoTo
+/D [36 0 R /XYZ 115.0 293.56 null]
+>>
+endobj
+206 0 obj
+<<
+/S /GoTo
+/D [54 0 R /XYZ 115.0 567.0 null]
+>>
+endobj
+209 0 obj
+<<
+/S /GoTo
+/D [87 0 R /XYZ 115.0 254.474 null]
+>>
+endobj
+211 0 obj
+<<
+/S /GoTo
+/D [91 0 R /XYZ 115.0 106.336 null]
+>>
+endobj
+213 0 obj
+<<
+/S /GoTo
+/D [97 0 R /XYZ 115.0 429.75 null]
+>>
+endobj
+215 0 obj
+<<
+/S /GoTo
+/D [99 0 R /XYZ 115.0 287.0 null]
+>>
+endobj
+217 0 obj
+<<
+/S /GoTo
+/D [105 0 R /XYZ 115.0 341.36 null]
+>>
+endobj
+219 0 obj
+<<
+/S /GoTo
+/D [107 0 R /XYZ 115.0 260.048 null]
+>>
+endobj
+221 0 obj
+<<
+/S /GoTo
+/D [107 0 R /XYZ 115.0 232.048 null]
+>>
+endobj
+223 0 obj
+<<
+/S /GoTo
+/D [107 0 R /XYZ 115.0 185.298 null]
+>>
+endobj
+225 0 obj
+<<
+/S /GoTo
+/D [109 0 R /XYZ 115.0 205.558 null]
+>>
+endobj
+227 0 obj
+<<
+/S /GoTo
+/D [111 0 R /XYZ 115.0 245.006 null]
+>>
+endobj
+229 0 obj
+<<
+/S /GoTo
+/D [117 0 R /XYZ 115.0 392.998 null]
+>>
+endobj
+232 0 obj
+<<
+/S /GoTo
+/D [125 0 R /XYZ 115.0 704.0 null]
+>>
+endobj
+234 0 obj
+<<
+/S /GoTo
+/D [125 0 R /XYZ 115.0 659.5 null]
+>>
+endobj
+236 0 obj
+<<
+/S /GoTo
+/D [129 0 R /XYZ 115.0 256.576 null]
+>>
+endobj
+238 0 obj
+<<
+/S /GoTo
+/D [136 0 R /XYZ 115.0 306.816 null]
+>>
+endobj
+240 0 obj
+<<
+/S /GoTo
+/D [138 0 R /XYZ 115.0 549.24 null]
+>>
+endobj
+242 0 obj
+<<
+/S /GoTo
+/D [164 0 R /XYZ 115.0 639.0 null]
+>>
+endobj
+244 0 obj
+<<
+/S /GoTo
+/D [164 0 R /XYZ 115.0 611.0 null]
+>>
+endobj
+246 0 obj
+<<
+/S /GoTo
+/D [164 0 R /XYZ 115.0 445.25 null]
+>>
+endobj
+248 0 obj
+<<
+/S /GoTo
+/D [177 0 R /XYZ 115.0 107.336 null]
+>>
+endobj
+xref
+0 259
+0000000000 65535 f 
+0000145968 00000 n 
+0000146351 00000 n 
+0000146869 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 
+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 
+trailer
+<<
+/Size 259
+/Root 2 0 R
+/Info 4 0 R
+>>
+startxref
+150587
+%%EOF

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/AssertionFailedException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/AssertionFailedException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/AssertionFailedException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,66 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+/**
+ * Assertions for empty code paths that should never be executed.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: AssertionFailedException.java,v 1.1.2.1 2005/05/13 18:58:37
+ *          mmatthews Exp $
+ */
+public class AssertionFailedException extends RuntimeException {
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Convenience method.
+	 * 
+	 * @param ex
+	 *            the exception that should never have been thrown.
+	 * @throws AssertionFailedException
+	 *             for the exception ex.
+	 */
+	public static void shouldNotHappen(Exception ex)
+			throws AssertionFailedException {
+		throw new AssertionFailedException(ex);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Creates an AssertionFailedException for the given exception that should
+	 * never have been thrown.
+	 * 
+	 * @param ex
+	 *            the exception that should never have been thrown.
+	 */
+	public AssertionFailedException(Exception ex) {
+		super(Messages.getString("AssertionFailedException.0") + ex.toString() //$NON-NLS-1$
+				+ Messages.getString("AssertionFailedException.1")); //$NON-NLS-1$
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Blob.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Blob.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Blob.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,247 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import java.sql.SQLException;
+
+/**
+ * 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
+ * as a column value in a row of a database table. The driver implements Blob
+ * using an SQL locator(BLOB), which means that a Blob object contains a logical
+ * pointer to the SQL BLOB data rather than the data itself. A Blob object is
+ * valid for the duration of the transaction in which is was created. Methods in
+ * the interfaces ResultSet, CallableStatement, and PreparedStatement, such as
+ * getBlob and setBlob allow a programmer to access an SQL BLOB value. The Blob
+ * interface provides methods for getting the length of an SQL BLOB (Binary
+ * Large Object) value, for materializing a BLOB value on the client, and for
+ * determining the position of a pattern of bytes within a BLOB value. This
+ * class is new in the JDBC 2.0 API.
+ * 
+ * @author Mark Matthews
+ * @version $Id: Blob.java 4489 2005-11-01 00:43:01Z mmatthews $
+ */
+public class Blob implements java.sql.Blob, OutputStreamWatcher {
+
+	//
+	// This is a real brain-dead implementation of BLOB. Once I add
+	// streamability to the I/O for MySQL this will be more efficiently
+	// implemented (except for the position() method, ugh).
+	//
+
+	/** The binary data that makes up this BLOB */
+	private byte[] binaryData = null;
+
+	/**
+	 * Creates a BLOB encapsulating the given binary data
+	 * 
+	 * @param data
+	 *            DOCUMENT ME!
+	 */
+	Blob(byte[] data) {
+		setBinaryData(data);
+	}
+
+	/**
+	 * Creates an updatable BLOB that can update in-place (not implemented yet).
+	 * 
+	 * @param data
+	 *            DOCUMENT ME!
+	 * @param creatorResultSetToSet
+	 *            DOCUMENT ME!
+	 * @param columnIndexToSet
+	 *            DOCUMENT ME!
+	 */
+	Blob(byte[] data, ResultSet creatorResultSetToSet, int columnIndexToSet) {
+		setBinaryData(data);
+	}
+
+	private byte[] getBinaryData() {
+		return this.binaryData;
+	}
+
+	/**
+	 * Retrieves the BLOB designated by this Blob instance as a stream.
+	 * 
+	 * @return this BLOB represented as a binary stream of bytes.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public java.io.InputStream getBinaryStream() throws SQLException {
+		return new ByteArrayInputStream(getBinaryData());
+	}
+
+	/**
+	 * Returns as an array of bytes, part or all of the BLOB value that this
+	 * Blob object designates.
+	 * 
+	 * @param pos
+	 *            where to start the part of the BLOB
+	 * @param length
+	 *            the length of the part of the BLOB you want returned.
+	 * 
+	 * @return the bytes stored in the blob starting at position
+	 *         <code>pos</code> and having a length of <code>length</code>.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public byte[] getBytes(long pos, int length) throws SQLException {
+		if (pos < 1) {
+			throw SQLError.createSQLException(Messages.getString("Blob.2"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		byte[] newData = new byte[length];
+		System.arraycopy(getBinaryData(), (int) (pos - 1), newData, 0, length);
+
+		return newData;
+	}
+
+	/**
+	 * Returns the number of bytes in the BLOB value designated by this Blob
+	 * object.
+	 * 
+	 * @return the length of this blob
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public long length() throws SQLException {
+		return getBinaryData().length;
+	}
+
+	/**
+	 * @see java.sql.Blob#position(byte[], long)
+	 */
+	public long position(byte[] pattern, long start) throws SQLException {
+		throw SQLError.createSQLException("Not implemented"); //$NON-NLS-1$
+	}
+
+	/**
+	 * Finds the position of the given pattern in this BLOB.
+	 * 
+	 * @param pattern
+	 *            the pattern to find
+	 * @param start
+	 *            where to start finding the pattern
+	 * 
+	 * @return the position where the pattern is found in the BLOB, -1 if not
+	 *         found
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public long position(java.sql.Blob pattern, long start) throws SQLException {
+		return position(pattern.getBytes(0, (int) pattern.length()), start);
+	}
+
+	private void setBinaryData(byte[] newBinaryData) {
+		this.binaryData = newBinaryData;
+	}
+
+	/**
+	 * @see Blob#setBinaryStream(long)
+	 */
+	public OutputStream setBinaryStream(long indexToWriteAt)
+			throws SQLException {
+		if (indexToWriteAt < 1) {
+			throw SQLError.createSQLException(Messages.getString("Blob.0"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		WatchableOutputStream bytesOut = new WatchableOutputStream();
+		bytesOut.setWatcher(this);
+
+		if (indexToWriteAt > 0) {
+			bytesOut.write(this.binaryData, 0, (int) (indexToWriteAt - 1));
+		}
+
+		return bytesOut;
+	}
+
+	/**
+	 * @see Blob#setBytes(long, byte[])
+	 */
+	public int setBytes(long writeAt, byte[] bytes) throws SQLException {
+		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)
+			throws SQLException {
+		OutputStream bytesOut = setBinaryStream(writeAt);
+
+		try {
+			bytesOut.write(bytes, offset, length);
+		} catch (IOException ioEx) {
+			throw SQLError.createSQLException(Messages.getString("Blob.1"), //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} finally {
+			try {
+				bytesOut.close();
+			} catch (IOException doNothing) {
+				; // do nothing
+			}
+		}
+
+		return length;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[])
+	 */
+	public void streamClosed(byte[] byteData) {
+		this.binaryData = byteData;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[])
+	 */
+	public void streamClosed(WatchableOutputStream out) {
+		int streamSize = out.size();
+
+		if (streamSize < this.binaryData.length) {
+			out.write(this.binaryData, streamSize, this.binaryData.length
+					- streamSize);
+		}
+
+		this.binaryData = out.toByteArray();
+	}
+
+	/**
+	 * @see Blob#truncate(long)
+	 */
+	public void truncate(long arg0) throws SQLException {
+		throw new NotImplemented();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/BlobFromLocator.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/BlobFromLocator.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/BlobFromLocator.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,671 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 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
+ * as a column value in a row of a database table. The driver implements Blob
+ * using an SQL locator(BLOB), which means that a Blob object contains a logical
+ * pointer to the SQL BLOB data rather than the data itself. A Blob object is
+ * valid for the duration of the transaction in which is was created. Methods in
+ * the interfaces ResultSet, CallableStatement, and PreparedStatement, such as
+ * getBlob and setBlob allow a programmer to access an SQL BLOB value. The Blob
+ * interface provides methods for getting the length of an SQL BLOB (Binary
+ * Large Object) value, for materializing a BLOB value on the client, and for
+ * determining the position of a pattern of bytes within a BLOB value. This
+ * class is new in the JDBC 2.0 API.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: BlobFromLocator.java,v 1.1.4.1 2005/05/19 18:31:49 mmatthews
+ *          Exp $
+ */
+public class BlobFromLocator implements java.sql.Blob {
+	private List primaryKeyColumns = null;
+
+	private List primaryKeyValues = null;
+
+	/** The ResultSet that created this BLOB */
+	private ResultSet creatorResultSet;
+
+	private String blobColumnName = null;
+
+	private String tableName = null;
+
+	private int numColsInResultSet = 0;
+
+	private int numPrimaryKeys = 0;
+
+	private String quotedId;
+
+	/**
+	 * Creates an updatable BLOB that can update in-place
+	 */
+	BlobFromLocator(ResultSet creatorResultSetToSet, int blobColumnIndex)
+			throws SQLException {
+		this.creatorResultSet = creatorResultSetToSet;
+
+		this.numColsInResultSet = this.creatorResultSet.fields.length;
+		this.quotedId = this.creatorResultSet.connection.getMetaData()
+				.getIdentifierQuoteString();
+
+		if (this.numColsInResultSet > 1) {
+			this.primaryKeyColumns = new ArrayList();
+			this.primaryKeyValues = new ArrayList();
+
+			for (int i = 0; i < this.numColsInResultSet; i++) {
+				if (this.creatorResultSet.fields[i].isPrimaryKey()) {
+					StringBuffer keyName = new StringBuffer();
+					keyName.append(quotedId);
+
+					String originalColumnName = this.creatorResultSet.fields[i]
+							.getOriginalName();
+
+					if ((originalColumnName != null)
+							&& (originalColumnName.length() > 0)) {
+						keyName.append(originalColumnName);
+					} else {
+						keyName.append(this.creatorResultSet.fields[i]
+								.getName());
+					}
+
+					keyName.append(quotedId);
+
+					this.primaryKeyColumns.add(keyName.toString());
+					this.primaryKeyValues.add(this.creatorResultSet
+							.getString(i + 1));
+				}
+			}
+		} else {
+			notEnoughInformationInQuery();
+		}
+
+		this.numPrimaryKeys = this.primaryKeyColumns.size();
+
+		if (this.numPrimaryKeys == 0) {
+			notEnoughInformationInQuery();
+		}
+
+		if (this.creatorResultSet.fields[0].getOriginalTableName() != null) {
+			StringBuffer tableNameBuffer = new StringBuffer();
+
+			String databaseName = this.creatorResultSet.fields[0]
+					.getDatabaseName();
+
+			if ((databaseName != null) && (databaseName.length() > 0)) {
+				tableNameBuffer.append(quotedId);
+				tableNameBuffer.append(databaseName);
+				tableNameBuffer.append(quotedId);
+				tableNameBuffer.append('.');
+			}
+
+			tableNameBuffer.append(quotedId);
+			tableNameBuffer.append(this.creatorResultSet.fields[0]
+					.getOriginalTableName());
+			tableNameBuffer.append(quotedId);
+
+			this.tableName = tableNameBuffer.toString();
+		} else {
+			StringBuffer tableNameBuffer = new StringBuffer();
+
+			tableNameBuffer.append(quotedId);
+			tableNameBuffer.append(this.creatorResultSet.fields[0]
+					.getTableName());
+			tableNameBuffer.append(quotedId);
+
+			this.tableName = tableNameBuffer.toString();
+		}
+
+		this.blobColumnName = quotedId
+				+ this.creatorResultSet.getString(blobColumnIndex) + quotedId;
+	}
+
+	private void notEnoughInformationInQuery() throws SQLException {
+		throw SQLError.createSQLException("Emulated BLOB locators must come from "
+				+ "a ResultSet with only one table selected, and all primary "
+				+ "keys selected", SQLError.SQL_STATE_GENERAL_ERROR);
+	}
+
+	/**
+	 * @see Blob#setBinaryStream(long)
+	 */
+	public OutputStream setBinaryStream(long indexToWriteAt)
+			throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * Retrieves the BLOB designated by this Blob instance as a stream.
+	 * 
+	 * @return this BLOB represented as a binary stream of bytes.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public java.io.InputStream getBinaryStream() throws SQLException {
+		// TODO: Make fetch size configurable
+		return new BufferedInputStream(new LocatorInputStream(),
+				this.creatorResultSet.connection.getLocatorFetchBufferSize());
+	}
+
+	/**
+	 * @see Blob#setBytes(long, byte[], int, int)
+	 */
+	public int setBytes(long writeAt, byte[] bytes, int offset, int length)
+			throws SQLException {
+		java.sql.PreparedStatement pStmt = null;
+
+		if ((offset + length) > bytes.length) {
+			length = bytes.length - offset;
+		}
+
+		byte[] bytesToWrite = new byte[length];
+		System.arraycopy(bytes, offset, bytesToWrite, 0, length);
+
+		// FIXME: Needs to use identifiers for column/table names
+		StringBuffer query = new StringBuffer("UPDATE ");
+		query.append(this.tableName);
+		query.append(" SET ");
+		query.append(this.blobColumnName);
+		query.append(" = INSERT(");
+		query.append(this.blobColumnName);
+		query.append(", ");
+		query.append(writeAt);
+		query.append(", ");
+		query.append(length);
+		query.append(", ?) WHERE ");
+
+		query.append((String) this.primaryKeyColumns.get(0));
+		query.append(" = ?");
+
+		for (int i = 1; i < this.numPrimaryKeys; i++) {
+			query.append(" AND ");
+			query.append((String) this.primaryKeyColumns.get(i));
+			query.append(" = ?");
+		}
+
+		try {
+			// FIXME: Have this passed in instead
+			pStmt = this.creatorResultSet.connection.prepareStatement(query
+					.toString());
+
+			pStmt.setBytes(1, bytesToWrite);
+
+			for (int i = 0; i < this.numPrimaryKeys; i++) {
+				pStmt.setString(i + 2, (String) this.primaryKeyValues.get(i));
+			}
+
+			int rowsUpdated = pStmt.executeUpdate();
+
+			if (rowsUpdated != 1) {
+				throw SQLError.createSQLException(
+						"BLOB data not found! Did primary keys change?",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} finally {
+			if (pStmt != null) {
+				try {
+					pStmt.close();
+				} catch (SQLException sqlEx) {
+					; // do nothing
+				}
+
+				pStmt = null;
+			}
+		}
+
+		return (int) length();
+	}
+
+	/**
+	 * @see Blob#setBytes(long, byte[])
+	 */
+	public int setBytes(long writeAt, byte[] bytes) throws SQLException {
+		return setBytes(writeAt, bytes, 0, bytes.length);
+	}
+
+	/**
+	 * Returns as an array of bytes, part or all of the BLOB value that this
+	 * Blob object designates.
+	 * 
+	 * @param pos
+	 *            where to start the part of the BLOB
+	 * @param length
+	 *            the length of the part of the BLOB you want returned.
+	 * 
+	 * @return the bytes stored in the blob starting at position
+	 *         <code>pos</code> and having a length of <code>length</code>.
+	 * 
+	 * @throws SQLException
+	 *             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 {
+
+			pStmt = createGetBytesStatement();
+
+			return getBytesInternal(pStmt, pos, length);
+		} finally {
+			if (blobRs != null) {
+				try {
+					blobRs.close();
+				} catch (SQLException sqlEx) {
+					; // do nothing
+				}
+
+				blobRs = null;
+			}
+		}
+	}
+
+	/**
+	 * Returns the number of bytes in the BLOB value designated by this Blob
+	 * object.
+	 * 
+	 * @return the length of this blob
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public long length() throws SQLException {
+		java.sql.ResultSet blobRs = null;
+		java.sql.PreparedStatement pStmt = null;
+
+		// FIXME: Needs to use identifiers for column/table names
+		StringBuffer query = new StringBuffer("SELECT LENGTH(");
+		query.append(this.blobColumnName);
+		query.append(") FROM ");
+		query.append(this.tableName);
+		query.append(" WHERE ");
+
+		query.append((String) this.primaryKeyColumns.get(0));
+		query.append(" = ?");
+
+		for (int i = 1; i < this.numPrimaryKeys; i++) {
+			query.append(" AND ");
+			query.append((String) this.primaryKeyColumns.get(i));
+			query.append(" = ?");
+		}
+
+		try {
+			// FIXME: Have this passed in instead
+			pStmt = this.creatorResultSet.connection.prepareStatement(query
+					.toString());
+
+			for (int i = 0; i < this.numPrimaryKeys; i++) {
+				pStmt.setString(i + 1, (String) this.primaryKeyValues.get(i));
+			}
+
+			blobRs = pStmt.executeQuery();
+
+			if (blobRs.next()) {
+				return blobRs.getLong(1);
+			}
+
+			throw SQLError.createSQLException(
+					"BLOB data not found! Did primary keys change?",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} finally {
+			if (blobRs != null) {
+				try {
+					blobRs.close();
+				} catch (SQLException sqlEx) {
+					; // do nothing
+				}
+
+				blobRs = null;
+			}
+
+			if (pStmt != null) {
+				try {
+					pStmt.close();
+				} catch (SQLException sqlEx) {
+					; // do nothing
+				}
+
+				pStmt = null;
+			}
+		}
+	}
+
+	/**
+	 * Finds the position of the given pattern in this BLOB.
+	 * 
+	 * @param pattern
+	 *            the pattern to find
+	 * @param start
+	 *            where to start finding the pattern
+	 * 
+	 * @return the position where the pattern is found in the BLOB, -1 if not
+	 *         found
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public long position(java.sql.Blob pattern, long start) throws SQLException {
+		return position(pattern.getBytes(0, (int) pattern.length()), start);
+	}
+
+	/**
+	 * @see java.sql.Blob#position(byte[], long)
+	 */
+	public long position(byte[] pattern, long start) throws SQLException {
+		java.sql.ResultSet blobRs = null;
+		java.sql.PreparedStatement pStmt = null;
+
+		// FIXME: Needs to use identifiers for column/table names
+		StringBuffer query = new StringBuffer("SELECT LOCATE(");
+		query.append("?, ");
+		query.append(this.blobColumnName);
+		query.append(", ");
+		query.append(start);
+		query.append(") FROM ");
+		query.append(this.tableName);
+		query.append(" WHERE ");
+
+		query.append((String) this.primaryKeyColumns.get(0));
+		query.append(" = ?");
+
+		for (int i = 1; i < this.numPrimaryKeys; i++) {
+			query.append(" AND ");
+			query.append((String) this.primaryKeyColumns.get(i));
+			query.append(" = ?");
+		}
+
+		try {
+			// FIXME: Have this passed in instead
+			pStmt = this.creatorResultSet.connection.prepareStatement(query
+					.toString());
+			pStmt.setBytes(1, pattern);
+
+			for (int i = 0; i < this.numPrimaryKeys; i++) {
+				pStmt.setString(i + 2, (String) this.primaryKeyValues.get(i));
+			}
+
+			blobRs = pStmt.executeQuery();
+
+			if (blobRs.next()) {
+				return blobRs.getLong(1);
+			}
+
+			throw SQLError.createSQLException(
+					"BLOB data not found! Did primary keys change?",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} finally {
+			if (blobRs != null) {
+				try {
+					blobRs.close();
+				} catch (SQLException sqlEx) {
+					; // do nothing
+				}
+
+				blobRs = null;
+			}
+
+			if (pStmt != null) {
+				try {
+					pStmt.close();
+				} catch (SQLException sqlEx) {
+					; // do nothing
+				}
+
+				pStmt = null;
+			}
+		}
+	}
+
+	/**
+	 * @see Blob#truncate(long)
+	 */
+	public void truncate(long length) throws SQLException {
+		java.sql.PreparedStatement pStmt = null;
+
+		// FIXME: Needs to use identifiers for column/table names
+		StringBuffer query = new StringBuffer("UPDATE ");
+		query.append(this.tableName);
+		query.append(" SET ");
+		query.append(this.blobColumnName);
+		query.append(" = LEFT(");
+		query.append(this.blobColumnName);
+		query.append(", ");
+		query.append(length);
+		query.append(") WHERE ");
+
+		query.append((String) this.primaryKeyColumns.get(0));
+		query.append(" = ?");
+
+		for (int i = 1; i < this.numPrimaryKeys; i++) {
+			query.append(" AND ");
+			query.append((String) this.primaryKeyColumns.get(i));
+			query.append(" = ?");
+		}
+
+		try {
+			// FIXME: Have this passed in instead
+			pStmt = this.creatorResultSet.connection.prepareStatement(query
+					.toString());
+
+			for (int i = 0; i < this.numPrimaryKeys; i++) {
+				pStmt.setString(i + 1, (String) this.primaryKeyValues.get(i));
+			}
+
+			int rowsUpdated = pStmt.executeUpdate();
+
+			if (rowsUpdated != 1) {
+				throw SQLError.createSQLException(
+						"BLOB data not found! Did primary keys change?",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} finally {
+			if (pStmt != null) {
+				try {
+					pStmt.close();
+				} catch (SQLException sqlEx) {
+					; // do nothing
+				}
+
+				pStmt = null;
+			}
+		}
+	}
+
+	java.sql.PreparedStatement createGetBytesStatement() throws SQLException {
+		StringBuffer query = new StringBuffer("SELECT SUBSTRING(");
+
+		query.append(this.blobColumnName);
+		query.append(", ");
+		query.append("?");
+		query.append(", ");
+		query.append("?");
+		query.append(") FROM ");
+		query.append(this.tableName);
+		query.append(" WHERE ");
+
+		query.append((String) this.primaryKeyColumns.get(0));
+		query.append(" = ?");
+
+		for (int i = 1; i < this.numPrimaryKeys; i++) {
+			query.append(" AND ");
+			query.append((String) this.primaryKeyColumns.get(i));
+			query.append(" = ?");
+		}
+
+		return this.creatorResultSet.connection.prepareStatement(query
+				.toString());
+	}
+
+	byte[] getBytesInternal(java.sql.PreparedStatement pStmt, long pos,
+			int length) throws SQLException {
+
+		java.sql.ResultSet blobRs = null;
+
+		try {
+
+			pStmt.setLong(1, pos);
+			pStmt.setInt(2, length);
+
+			for (int i = 0; i < this.numPrimaryKeys; i++) {
+				pStmt.setString(i + 3, (String) this.primaryKeyValues.get(i));
+			}
+
+			blobRs = pStmt.executeQuery();
+
+			if (blobRs.next()) {
+				return ((com.mysql.jdbc.ResultSet) blobRs).getBytes(1, true);
+			}
+
+			throw SQLError.createSQLException(
+					"BLOB data not found! Did primary keys change?",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} finally {
+			if (blobRs != null) {
+				try {
+					blobRs.close();
+				} catch (SQLException sqlEx) {
+					; // do nothing
+				}
+
+				blobRs = null;
+			}
+		}
+	}
+
+	class LocatorInputStream extends InputStream {
+		long currentPositionInBlob = 0;
+
+		long length = 0;
+
+		java.sql.PreparedStatement pStmt = null;
+
+		LocatorInputStream() throws SQLException {
+			length = length();
+			pStmt = createGetBytesStatement();
+		}
+
+		public int read() throws IOException {
+			if (currentPositionInBlob + 1 > length) {
+				return -1;
+			}
+
+			try {
+				byte[] asBytes = getBytesInternal(pStmt,
+						(currentPositionInBlob++) + 1, 1);
+
+				if (asBytes == null) {
+					return -1;
+				}
+
+				return asBytes[0];
+			} catch (SQLException sqlEx) {
+				throw new IOException(sqlEx.toString());
+			}
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.io.InputStream#read(byte[], int, int)
+		 */
+		public int read(byte[] b, int off, int len) throws IOException {
+			if (currentPositionInBlob + 1 > length) {
+				return -1;
+			}
+
+			try {
+				byte[] asBytes = getBytesInternal(pStmt,
+						(currentPositionInBlob) + 1, len);
+
+				if (asBytes == null) {
+					return -1;
+				}
+
+				System.arraycopy(asBytes, 0, b, off, asBytes.length);
+
+				currentPositionInBlob += asBytes.length;
+
+				return asBytes.length;
+			} catch (SQLException sqlEx) {
+				throw new IOException(sqlEx.toString());
+			}
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.io.InputStream#read(byte[])
+		 */
+		public int read(byte[] b) throws IOException {
+			if (currentPositionInBlob + 1 > length) {
+				return -1;
+			}
+
+			try {
+				byte[] asBytes = getBytesInternal(pStmt,
+						(currentPositionInBlob) + 1, b.length);
+
+				if (asBytes == null) {
+					return -1;
+				}
+
+				System.arraycopy(asBytes, 0, b, 0, asBytes.length);
+
+				currentPositionInBlob += asBytes.length;
+
+				return asBytes.length;
+			} catch (SQLException sqlEx) {
+				throw new IOException(sqlEx.toString());
+			}
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.io.InputStream#close()
+		 */
+		public void close() throws IOException {
+			if (pStmt != null) {
+				try {
+					pStmt.close();
+				} catch (SQLException sqlEx) {
+					throw new IOException(sqlEx.toString());
+				}
+			}
+
+			super.close();
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Buffer.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Buffer.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Buffer.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,663 @@
+/*
+ Copyright (C) 2002-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;
+
+import java.io.UnsupportedEncodingException;
+
+import java.nio.ByteBuffer;
+
+import java.sql.SQLException;
+
+/**
+ * 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 $
+ * @author Mark Matthews
+ */
+class Buffer {
+	static final int MAX_BYTES_TO_DUMP = 512;
+
+	static final int NO_LENGTH_LIMIT = -1;
+
+	static final long NULL_LENGTH = -1;
+
+	private int bufLength = 0;
+
+	private byte[] byteBuffer;
+
+	private int position = 0;
+
+	protected boolean wasMultiPacket = false;
+
+	Buffer(byte[] buf) {
+		this.byteBuffer = buf;
+		setBufLength(buf.length);
+	}
+
+	Buffer(int size) {
+		this.byteBuffer = new byte[size];
+		setBufLength(this.byteBuffer.length);
+		this.position = MysqlIO.HEADER_LENGTH;
+	}
+
+	final void clear() {
+		this.position = MysqlIO.HEADER_LENGTH;
+	}
+
+	final void dump() {
+		dump(getBufLength());
+	}
+
+	final String dump(int numBytes) {
+		return StringUtils.dumpAsHex(getBytes(0,
+				numBytes > getBufLength() ? getBufLength() : numBytes),
+				numBytes > getBufLength() ? getBufLength() : numBytes);
+	}
+
+	final String dumpClampedBytes(int numBytes) {
+		int numBytesToDump = numBytes < MAX_BYTES_TO_DUMP ? numBytes
+				: MAX_BYTES_TO_DUMP;
+
+		String dumped = StringUtils.dumpAsHex(getBytes(0,
+				numBytesToDump > getBufLength() ? getBufLength()
+						: numBytesToDump),
+				numBytesToDump > getBufLength() ? getBufLength()
+						: numBytesToDump);
+
+		if (numBytesToDump < numBytes) {
+			return dumped + " ....(packet exceeds max. dump length)";
+		}
+
+		return dumped;
+	}
+
+	final void dumpHeader() {
+		for (int i = 0; i < MysqlIO.HEADER_LENGTH; i++) {
+			String hexVal = Integer.toHexString(readByte(i) & 0xff);
+
+			if (hexVal.length() == 1) {
+				hexVal = "0" + hexVal; //$NON-NLS-1$
+			}
+
+			System.out.print(hexVal + " "); //$NON-NLS-1$
+		}
+	}
+
+	final void dumpNBytes(int start, int nBytes) {
+		StringBuffer asciiBuf = new StringBuffer();
+
+		for (int i = start; (i < (start + nBytes)) && (i < getBufLength()); i++) {
+			String hexVal = Integer.toHexString(readByte(i) & 0xff);
+
+			if (hexVal.length() == 1) {
+				hexVal = "0" + hexVal; //$NON-NLS-1$
+			}
+
+			System.out.print(hexVal + " "); //$NON-NLS-1$
+
+			if ((readByte(i) > 32) && (readByte(i) < 127)) {
+				asciiBuf.append((char) readByte(i));
+			} else {
+				asciiBuf.append("."); //$NON-NLS-1$
+			}
+
+			asciiBuf.append(" "); //$NON-NLS-1$
+		}
+
+		System.out.println("    " + asciiBuf.toString()); //$NON-NLS-1$
+	}
+
+	final void ensureCapacity(int additionalData) throws SQLException {
+		if ((this.position + additionalData) > getBufLength()) {
+			if ((this.position + additionalData) < this.byteBuffer.length) {
+				// byteBuffer.length is != getBufLength() all of the time
+				// due to re-using of packets (we don't shrink them)
+				//
+				// If we can, don't re-alloc, just set buffer length
+				// to size of current buffer
+				setBufLength(this.byteBuffer.length);
+			} else {
+				//
+				// Otherwise, re-size, and pad so we can avoid
+				// allocing again in the near future
+				//
+				int newLength = (int) (this.byteBuffer.length * 1.25);
+
+				if (newLength < (this.byteBuffer.length + additionalData)) {
+					newLength = this.byteBuffer.length
+							+ (int) (additionalData * 1.25);
+				}
+
+				if (newLength < this.byteBuffer.length) {
+					newLength = this.byteBuffer.length + additionalData;
+				}
+
+				byte[] newBytes = new byte[newLength];
+
+				System.arraycopy(this.byteBuffer, 0, newBytes, 0,
+						this.byteBuffer.length);
+				this.byteBuffer = newBytes;
+				setBufLength(this.byteBuffer.length);
+			}
+		}
+	}
+
+	/**
+	 * Skip over a length-encoded string
+	 * 
+	 * @return The position past the end of the string
+	 */
+	public int fastSkipLenString() {
+		long len = this.readFieldLength();
+
+		this.position += len;
+
+		return (int) len; // this is safe, as this is only
+	}
+
+	protected final byte[] getBufferSource() {
+		return this.byteBuffer;
+	}
+
+	int getBufLength() {
+		return this.bufLength;
+	}
+
+	/**
+	 * Returns the array of bytes this Buffer is using to read from.
+	 * 
+	 * @return byte array being read from
+	 */
+	public byte[] getByteBuffer() {
+		return this.byteBuffer;
+	}
+
+	final byte[] getBytes(int len) {
+		byte[] b = new byte[len];
+		System.arraycopy(this.byteBuffer, this.position, b, 0, len);
+		this.position += len; // update cursor
+
+		return b;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.Buffer#getBytes(int, int)
+	 */
+	byte[] getBytes(int offset, int len) {
+		byte[] dest = new byte[len];
+		System.arraycopy(this.byteBuffer, offset, dest, 0, len);
+
+		return dest;
+	}
+
+	int getCapacity() {
+		return this.byteBuffer.length;
+	}
+
+	public ByteBuffer getNioBuffer() {
+		throw new IllegalArgumentException(Messages
+				.getString("ByteArrayBuffer.0")); //$NON-NLS-1$
+	}
+
+	/**
+	 * Returns the current position to write to/ read from
+	 * 
+	 * @return the current position to write to/ read from
+	 */
+	public int getPosition() {
+		return this.position;
+	}
+
+	// 2000-06-05 Changed
+	final boolean isLastDataPacket() {
+		return ((getBufLength() < 9) && ((this.byteBuffer[0] & 0xff) == 254));
+	}
+
+	final long newReadLength() {
+		int sw = this.byteBuffer[this.position++] & 0xff;
+
+		switch (sw) {
+		case 251:
+			return 0;
+
+		case 252:
+			return readInt();
+
+		case 253:
+			return readLongInt();
+
+		case 254: // changed for 64 bit lengths
+			return readLongLong();
+
+		default:
+			return sw;
+		}
+	}
+
+	final byte readByte() {
+		return this.byteBuffer[this.position++];
+	}
+
+	final byte readByte(int readAt) {
+		return this.byteBuffer[readAt];
+	}
+
+	final long readFieldLength() {
+		int sw = this.byteBuffer[this.position++] & 0xff;
+
+		switch (sw) {
+		case 251:
+			return NULL_LENGTH;
+
+		case 252:
+			return readInt();
+
+		case 253:
+			return readLongInt();
+
+		case 254:
+			return readLongLong();
+
+		default:
+			return sw;
+		}
+	}
+
+	// 2000-06-05 Changed
+	final int readInt() {
+		byte[] b = this.byteBuffer; // a little bit optimization
+
+		return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8);
+	}
+
+	final int readIntAsLong() {
+		byte[] b = this.byteBuffer;
+
+		return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8)
+				| ((b[this.position++] & 0xff) << 16)
+				| ((b[this.position++] & 0xff) << 24);
+	}
+
+	final byte[] readLenByteArray(int offset) {
+		long len = this.readFieldLength();
+
+		if (len == NULL_LENGTH) {
+			return null;
+		}
+
+		if (len == 0) {
+			return Constants.EMPTY_BYTE_ARRAY;
+		}
+
+		this.position += offset;
+
+		return getBytes((int) len);
+	}
+
+	final long readLength() {
+		int sw = this.byteBuffer[this.position++] & 0xff;
+
+		switch (sw) {
+		case 251:
+			return 0;
+
+		case 252:
+			return readInt();
+
+		case 253:
+			return readLongInt();
+
+		case 254:
+			return readLong();
+
+		default:
+			return sw;
+		}
+	}
+
+	// 2000-06-05 Fixed
+	final long readLong() {
+		byte[] b = this.byteBuffer;
+
+		return ((long) b[this.position++] & 0xff)
+				| (((long) b[this.position++] & 0xff) << 8)
+				| ((long) (b[this.position++] & 0xff) << 16)
+				| ((long) (b[this.position++] & 0xff) << 24);
+	}
+
+	// 2000-06-05 Changed
+	final int readLongInt() {
+		byte[] b = this.byteBuffer;
+
+		return (b[this.position++] & 0xff) | ((b[this.position++] & 0xff) << 8)
+				| ((b[this.position++] & 0xff) << 16);
+	}
+
+	// 2000-06-05 Fixed
+	final long readLongLong() {
+		byte[] b = this.byteBuffer;
+
+		return (b[this.position++] & 0xff)
+				| ((long) (b[this.position++] & 0xff) << 8)
+				| ((long) (b[this.position++] & 0xff) << 16)
+				| ((long) (b[this.position++] & 0xff) << 24)
+				| ((long) (b[this.position++] & 0xff) << 32)
+				| ((long) (b[this.position++] & 0xff) << 40)
+				| ((long) (b[this.position++] & 0xff) << 48)
+				| ((long) (b[this.position++] & 0xff) << 56);
+	}
+
+	final int readnBytes() {
+		int sw = this.byteBuffer[this.position++] & 0xff;
+
+		switch (sw) {
+		case 1:
+			return this.byteBuffer[this.position++] & 0xff;
+
+		case 2:
+			return this.readInt();
+
+		case 3:
+			return this.readLongInt();
+
+		case 4:
+			return (int) this.readLong();
+
+		default:
+			return 255;
+		}
+	}
+
+	//
+	// Read a null-terminated string
+	//
+	// To avoid alloc'ing a new byte array, we
+	// do this by hand, rather than calling getNullTerminatedBytes()
+	//
+	final String readString() {
+		int i = this.position;
+		int len = 0;
+		int maxLen = getBufLength();
+
+		while ((i < maxLen) && (this.byteBuffer[i] != 0)) {
+			len++;
+			i++;
+		}
+
+		String s = new String(this.byteBuffer, this.position, len);
+		this.position += (len + 1); // update cursor
+
+		return s;
+	}
+
+	final String readString(String encoding) throws SQLException {
+		int i = this.position;
+		int len = 0;
+		int maxLen = getBufLength();
+
+		while ((i < maxLen) && (this.byteBuffer[i] != 0)) {
+			len++;
+			i++;
+		}
+
+		try {
+			return new String(this.byteBuffer, this.position, len, encoding);
+		} catch (UnsupportedEncodingException uEE) {
+			throw SQLError.createSQLException(Messages.getString("ByteArrayBuffer.1") //$NON-NLS-1$
+					+ encoding + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		} finally {
+			this.position += (len + 1); // update cursor
+		}
+	}
+
+	void setBufLength(int bufLengthToSet) {
+		this.bufLength = bufLengthToSet;
+	}
+
+	/**
+	 * Sets the array of bytes to use as a buffer to read from.
+	 * 
+	 * @param byteBuffer
+	 *            the array of bytes to use as a buffer
+	 */
+	public void setByteBuffer(byte[] byteBufferToSet) {
+		this.byteBuffer = byteBufferToSet;
+	}
+
+	/**
+	 * Set the current position to write to/ read from
+	 * 
+	 * @param position
+	 *            the position (0-based index)
+	 */
+	public void setPosition(int positionToSet) {
+		this.position = positionToSet;
+	}
+
+	/**
+	 * Sets whether this packet was part of a multipacket
+	 * 
+	 * @param flag
+	 *            was this packet part of a multipacket?
+	 */
+	public void setWasMultiPacket(boolean flag) {
+		this.wasMultiPacket = flag;
+	}
+
+	public String toString() {
+		return dumpClampedBytes(getPosition());
+	}
+
+	public String toSuperString() {
+		return super.toString();
+	}
+
+	/**
+	 * Was this packet part of a multipacket?
+	 * 
+	 * @return was this packet part of a multipacket?
+	 */
+	public boolean wasMultiPacket() {
+		return this.wasMultiPacket;
+	}
+
+	final void writeByte(byte b) throws SQLException {
+		ensureCapacity(1);
+
+		this.byteBuffer[this.position++] = b;
+	}
+
+	// Write a byte array
+	final void writeBytesNoNull(byte[] bytes) throws SQLException {
+		int len = bytes.length;
+		ensureCapacity(len);
+		System.arraycopy(bytes, 0, this.byteBuffer, this.position, len);
+		this.position += len;
+	}
+
+	// Write a byte array with the given offset and length
+	final void writeBytesNoNull(byte[] bytes, int offset, int length)
+			throws SQLException {
+		ensureCapacity(length);
+		System.arraycopy(bytes, offset, this.byteBuffer, this.position, length);
+		this.position += length;
+	}
+
+	final void writeDouble(double d) throws SQLException {
+		long l = Double.doubleToLongBits(d);
+		writeLongLong(l);
+	}
+
+	final void writeFieldLength(long length) throws SQLException {
+		if (length < 251) {
+			writeByte((byte) length);
+		} else if (length < 65536L) {
+			ensureCapacity(3);
+			writeByte((byte) 252);
+			writeInt((int) length);
+		} else if (length < 16777216L) {
+			ensureCapacity(4);
+			writeByte((byte) 253);
+			writeLongInt((int) length);
+		} else {
+			ensureCapacity(9);
+			writeByte((byte) 254);
+			writeLongLong(length);
+		}
+	}
+
+	final void writeFloat(float f) throws SQLException {
+		ensureCapacity(4);
+
+		int i = Float.floatToIntBits(f);
+		byte[] b = this.byteBuffer;
+		b[this.position++] = (byte) (i & 0xff);
+		b[this.position++] = (byte) (i >>> 8);
+		b[this.position++] = (byte) (i >>> 16);
+		b[this.position++] = (byte) (i >>> 24);
+	}
+
+	// 2000-06-05 Changed
+	final void writeInt(int i) throws SQLException {
+		ensureCapacity(2);
+
+		byte[] b = this.byteBuffer;
+		b[this.position++] = (byte) (i & 0xff);
+		b[this.position++] = (byte) (i >>> 8);
+	}
+
+	// Write a String using the specified character
+	// encoding
+	final void writeLenBytes(byte[] b) throws SQLException {
+		int len = b.length;
+		ensureCapacity(len + 9);
+		writeFieldLength(len);
+		System.arraycopy(b, 0, this.byteBuffer, this.position, len);
+		this.position += len;
+	}
+
+	// Write a String using the specified character
+	// encoding
+	final void writeLenString(String s, String encoding, String serverEncoding,
+			SingleByteCharsetConverter converter, boolean parserKnowsUnicode,
+			Connection conn)
+			throws UnsupportedEncodingException, SQLException {
+		byte[] b = null;
+
+		if (converter != null) {
+			b = converter.toBytes(s);
+		} else {
+			b = StringUtils.getBytes(s, encoding, serverEncoding,
+					parserKnowsUnicode, conn);
+		}
+
+		int len = b.length;
+		ensureCapacity(len + 9);
+		writeFieldLength(len);
+		System.arraycopy(b, 0, this.byteBuffer, this.position, len);
+		this.position += len;
+	}
+
+	// 2000-06-05 Changed
+	final void writeLong(long i) throws SQLException {
+		ensureCapacity(4);
+
+		byte[] b = this.byteBuffer;
+		b[this.position++] = (byte) (i & 0xff);
+		b[this.position++] = (byte) (i >>> 8);
+		b[this.position++] = (byte) (i >>> 16);
+		b[this.position++] = (byte) (i >>> 24);
+	}
+
+	// 2000-06-05 Changed
+	final void writeLongInt(int i) throws SQLException {
+		ensureCapacity(3);
+		byte[] b = this.byteBuffer;
+		b[this.position++] = (byte) (i & 0xff);
+		b[this.position++] = (byte) (i >>> 8);
+		b[this.position++] = (byte) (i >>> 16);
+	}
+
+	final void writeLongLong(long i) throws SQLException {
+		ensureCapacity(8);
+		byte[] b = this.byteBuffer;
+		b[this.position++] = (byte) (i & 0xff);
+		b[this.position++] = (byte) (i >>> 8);
+		b[this.position++] = (byte) (i >>> 16);
+		b[this.position++] = (byte) (i >>> 24);
+		b[this.position++] = (byte) (i >>> 32);
+		b[this.position++] = (byte) (i >>> 40);
+		b[this.position++] = (byte) (i >>> 48);
+		b[this.position++] = (byte) (i >>> 56);
+	}
+
+	// Write null-terminated string
+	final void writeString(String s) throws SQLException {
+		ensureCapacity((s.length() * 2) + 1);
+		writeStringNoNull(s);
+		this.byteBuffer[this.position++] = 0;
+	}
+	
+	//	 Write null-terminated string in the given encoding
+	final void writeString(String s, String encoding, Connection conn) throws SQLException {
+		ensureCapacity((s.length() * 2) + 1);
+		try {
+			writeStringNoNull(s, encoding, encoding, false, conn);
+		} catch (UnsupportedEncodingException ue) {
+			throw new SQLException(ue.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+		
+		this.byteBuffer[this.position++] = 0;
+	}
+
+	// Write string, with no termination
+	final void writeStringNoNull(String s) throws SQLException {
+		int len = s.length();
+		ensureCapacity(len * 2);
+		System.arraycopy(s.getBytes(), 0, this.byteBuffer, this.position, len);
+		this.position += len;
+
+		// for (int i = 0; i < len; i++)
+		// {
+		// this.byteBuffer[this.position++] = (byte)s.charAt(i);
+		// }
+	}
+
+	// Write a String using the specified character
+	// encoding
+	final void writeStringNoNull(String s, String encoding,
+			String serverEncoding, boolean parserKnowsUnicode, Connection conn)
+			throws UnsupportedEncodingException, SQLException {
+		byte[] b = StringUtils.getBytes(s, encoding, serverEncoding,
+				parserKnowsUnicode, conn);
+
+		int len = b.length;
+		ensureCapacity(len);
+		System.arraycopy(b, 0, this.byteBuffer, this.position, len);
+		this.position += len;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CallableStatement.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CallableStatement.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CallableStatement.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,2156 @@
+/*
+ Copyright (C) 2002-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;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+import java.math.BigDecimal;
+
+import java.net.URL;
+
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.ParameterMetaData;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+
+/**
+ * Representation of stored procedures for JDBC
+ * 
+ * @author Mark Matthews
+ * @version $Id: CallableStatement.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
+ *          Exp $
+ */
+public class CallableStatement extends PreparedStatement implements
+		java.sql.CallableStatement {
+	class CallableStatementParam {
+		int desiredJdbcType;
+
+		int index;
+
+		int inOutModifier;
+
+		boolean isIn;
+
+		boolean isOut;
+
+		int jdbcType;
+
+		short nullability;
+
+		String paramName;
+
+		int precision;
+
+		int scale;
+
+		String typeName;
+
+		CallableStatementParam(String name, int idx, boolean in, boolean out,
+				int jdbcType, String typeName, int precision, int scale,
+				short nullability, int inOutModifier) {
+			this.paramName = name;
+			this.isIn = in;
+			this.isOut = out;
+			this.index = idx;
+
+			this.jdbcType = jdbcType;
+			this.typeName = typeName;
+			this.precision = precision;
+			this.scale = scale;
+			this.nullability = nullability;
+			this.inOutModifier = inOutModifier;
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.Object#clone()
+		 */
+		protected Object clone() throws CloneNotSupportedException {
+			return super.clone();
+		}
+	}
+
+	class CallableStatementParamInfo {
+		String catalogInUse;
+
+		boolean isFunctionCall;
+
+		String nativeSql;
+
+		int numParameters;
+
+		List parameterList;
+
+		Map parameterMap;
+
+		/**
+		 * Constructor that converts a full list of parameter metadata into one
+		 * that only represents the placeholders present in the {CALL ()}.
+		 * 
+		 * @param fullParamInfo the metadata for all parameters for this stored 
+		 * procedure or function.
+		 */
+		CallableStatementParamInfo(CallableStatementParamInfo fullParamInfo) {
+			this.nativeSql = originalSql;
+			this.catalogInUse = currentCatalog;
+			isFunctionCall = fullParamInfo.isFunctionCall;
+			int[] localParameterMap = placeholderToParameterIndexMap;
+			int parameterMapLength = localParameterMap.length;
+			
+			parameterList = new ArrayList(fullParamInfo.numParameters);
+			parameterMap = new HashMap(fullParamInfo.numParameters);
+			
+			if (isFunctionCall) {
+				// Take the return value
+				parameterList.add(fullParamInfo.parameterList.get(0));
+			}
+			
+			int offset = isFunctionCall ? 1 : 0;
+			
+			for (int i = 0; i < parameterMapLength; i++) {
+				if (localParameterMap[i] != 0) {
+					CallableStatementParam param = (CallableStatementParam)fullParamInfo.parameterList.get(localParameterMap[i] + offset);
+					
+					parameterList.add(param);
+					parameterMap.put(param.paramName, param);
+				}
+			}
+			
+			this.numParameters = parameterList.size();
+		}
+		
+		CallableStatementParamInfo(java.sql.ResultSet paramTypesRs)
+				throws SQLException {
+			boolean hadRows = paramTypesRs.last();
+
+			this.nativeSql = originalSql;
+			this.catalogInUse = currentCatalog;
+			isFunctionCall = callingStoredFunction;
+
+			if (hadRows) {
+				this.numParameters = paramTypesRs.getRow();
+
+				this.parameterList = new ArrayList(this.numParameters);
+				this.parameterMap = new HashMap(this.numParameters);
+
+				paramTypesRs.beforeFirst();
+
+				addParametersFromDBMD(paramTypesRs);
+			} else {
+				this.numParameters = 0;
+			}
+			
+			if (isFunctionCall) {
+				this.numParameters += 1;
+			}
+		}
+
+		private void addParametersFromDBMD(java.sql.ResultSet paramTypesRs)
+				throws SQLException {
+			int i = 0;
+
+			while (paramTypesRs.next()) {
+				String paramName = paramTypesRs.getString(4);
+				int inOutModifier = paramTypesRs.getInt(5);
+
+				boolean isOutParameter = false;
+				boolean isInParameter = false;
+
+				if (i == 0 && isFunctionCall) {
+					isOutParameter = true;
+					isInParameter = false;
+				} else if (inOutModifier == DatabaseMetaData.procedureColumnInOut) {
+					isOutParameter = true;
+					isInParameter = true;
+				} else if (inOutModifier == DatabaseMetaData.procedureColumnIn) {
+					isOutParameter = false;
+					isInParameter = true;
+				} else if (inOutModifier == DatabaseMetaData.procedureColumnOut) {
+					isOutParameter = true;
+					isInParameter = false;
+				}
+
+				int jdbcType = paramTypesRs.getInt(6);
+				String typeName = paramTypesRs.getString(7);
+				int precision = paramTypesRs.getInt(8);
+				int scale = paramTypesRs.getInt(10);
+				short nullability = paramTypesRs.getShort(12);
+
+				CallableStatementParam paramInfoToAdd = new CallableStatementParam(
+						paramName, i++, isInParameter, isOutParameter,
+						jdbcType, typeName, precision, scale, nullability,
+						inOutModifier);
+
+				this.parameterList.add(paramInfoToAdd);
+				this.parameterMap.put(paramName, paramInfoToAdd);
+			}
+		}
+
+		protected void checkBounds(int paramIndex) throws SQLException {
+			int localParamIndex = paramIndex - 1;
+
+			if ((paramIndex < 0) || (localParamIndex >= this.numParameters)) {
+				throw SQLError.createSQLException(
+						Messages.getString("CallableStatement.11") + paramIndex //$NON-NLS-1$
+								+ Messages.getString("CallableStatement.12") + numParameters //$NON-NLS-1$
+								+ Messages.getString("CallableStatement.13"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+			}
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.Object#clone()
+		 */
+		protected Object clone() throws CloneNotSupportedException {
+			// TODO Auto-generated method stub
+			return super.clone();
+		}
+
+		CallableStatementParam getParameter(int index) {
+			return (CallableStatementParam) this.parameterList.get(index);
+		}
+
+		CallableStatementParam getParameter(String name) {
+			return (CallableStatementParam) this.parameterMap.get(name);
+		}
+
+		public String getParameterClassName(int arg0) throws SQLException {
+			String mysqlTypeName = getParameterTypeName(arg0);
+			
+			boolean isBinaryOrBlob = StringUtils.indexOfIgnoreCase(mysqlTypeName, "BLOB") != -1 || 
+				StringUtils.indexOfIgnoreCase(mysqlTypeName, "BINARY") != -1;
+			
+			boolean isUnsigned = StringUtils.indexOfIgnoreCase(mysqlTypeName, "UNSIGNED") != -1;
+			
+			int mysqlTypeIfKnown = 0;
+			
+			if (StringUtils.startsWithIgnoreCase(mysqlTypeName, "MEDIUMINT")) {
+				mysqlTypeIfKnown = MysqlDefs.FIELD_TYPE_INT24;
+			}
+			
+			return ResultSetMetaData.getClassNameForJavaType(getParameterType(arg0), 
+					isUnsigned, mysqlTypeIfKnown, isBinaryOrBlob, false);
+		}
+
+		public int getParameterCount() throws SQLException {
+			if (this.parameterList == null) {
+				return 0;
+			}
+
+			return this.parameterList.size();
+		}
+
+		public int getParameterMode(int arg0) throws SQLException {
+			checkBounds(arg0);
+
+			return getParameter(arg0 - 1).inOutModifier;
+		}
+
+		public int getParameterType(int arg0) throws SQLException {
+			checkBounds(arg0);
+
+			return getParameter(arg0 - 1).jdbcType;
+		}
+
+		public String getParameterTypeName(int arg0) throws SQLException {
+			checkBounds(arg0);
+
+			return getParameter(arg0 - 1).typeName;
+		}
+
+		public int getPrecision(int arg0) throws SQLException {
+			checkBounds(arg0);
+
+			return getParameter(arg0 - 1).precision;
+		}
+
+		public int getScale(int arg0) throws SQLException {
+			checkBounds(arg0);
+
+			return getParameter(arg0 - 1).scale;
+		}
+
+		public int isNullable(int arg0) throws SQLException {
+			checkBounds(arg0);
+
+			return getParameter(arg0 - 1).nullability;
+		}
+
+		public boolean isSigned(int arg0) throws SQLException {
+			checkBounds(arg0);
+
+			return false;
+		}
+
+		Iterator iterator() {
+			return this.parameterList.iterator();
+		}
+
+		int numberOfParameters() {
+			return this.numParameters;
+		}
+	}
+
+	/**
+	 * Can't implement this directly, as then you can't use callable statements
+	 * on JDK-1.3.1, which unfortunately isn't EOL'd yet, and still present
+	 * quite a bit out there in the wild (Websphere, FreeBSD, anyone?)
+	 */
+
+	class CallableStatementParamInfoJDBC3 extends CallableStatementParamInfo
+			implements ParameterMetaData {
+
+		CallableStatementParamInfoJDBC3(java.sql.ResultSet paramTypesRs)
+				throws SQLException {
+			super(paramTypesRs);
+		}
+
+		public CallableStatementParamInfoJDBC3(CallableStatementParamInfo paramInfo) {
+			super(paramInfo);
+		}
+	}
+
+	private final static int NOT_OUTPUT_PARAMETER_INDICATOR = Integer.MIN_VALUE;
+
+	private final static String PARAMETER_NAMESPACE_PREFIX = "@com_mysql_jdbc_outparam_"; //$NON-NLS-1$
+
+	private static String mangleParameterName(String origParameterName) {
+		if (origParameterName == null) {
+			return null;
+		}
+
+		int offset = 0;
+
+		if (origParameterName.length() > 0
+				&& origParameterName.charAt(0) == '@') {
+			offset = 1;
+		}
+
+		StringBuffer paramNameBuf = new StringBuffer(PARAMETER_NAMESPACE_PREFIX
+				.length()
+				+ origParameterName.length());
+		paramNameBuf.append(PARAMETER_NAMESPACE_PREFIX);
+		paramNameBuf.append(origParameterName.substring(offset));
+
+		return paramNameBuf.toString();
+	}
+
+	private boolean callingStoredFunction = false;
+
+	private ResultSet functionReturnValueResults;
+
+	private boolean hasOutputParams = false;
+
+	// private List parameterList;
+	// private Map parameterMap;
+	private ResultSet outputParameterResults;
+
+	private boolean outputParamWasNull = false;
+
+	private int[] parameterIndexToRsIndex;
+
+	protected CallableStatementParamInfo paramInfo;
+
+	private CallableStatementParam returnValueParam;
+
+	/**
+	 * Creates a new CallableStatement
+	 * 
+	 * @param conn
+	 *            the connection creating this statement
+	 * @param paramInfo
+	 *            the SQL to prepare
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public CallableStatement(Connection conn,
+			CallableStatementParamInfo paramInfo) throws SQLException {
+		super(conn, paramInfo.nativeSql, paramInfo.catalogInUse);
+
+		this.paramInfo = paramInfo;
+		this.callingStoredFunction = this.paramInfo.isFunctionCall;
+		
+		if (this.callingStoredFunction) {
+			this.parameterCount += 1;
+		}
+	}
+
+	/**
+	 * Creates a new CallableStatement
+	 * 
+	 * @param conn
+	 *            the connection creating this statement
+	 * @param catalog
+	 *            catalog the current catalog
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public CallableStatement(Connection conn, String catalog)
+			throws SQLException {
+		super(conn, catalog, null);
+
+		determineParameterTypes();
+		generateParameterMap();
+		
+		if (this.callingStoredFunction) {
+			this.parameterCount += 1;
+		}
+	}
+
+	private int[] placeholderToParameterIndexMap;
+	
+	
+	private void generateParameterMap() throws SQLException {
+		// if the user specified some parameters as literals, we need to
+		// provide a map from the specified placeholders to the actual
+		// parameter numbers
+		
+		int parameterCountFromMetaData = this.paramInfo.getParameterCount();
+		
+		// Ignore the first ? if this is a stored function, it doesn't count
+		
+		if (this.callingStoredFunction) {
+			parameterCountFromMetaData--;
+		}
+		
+		if (this.paramInfo != null &&
+				this.parameterCount != parameterCountFromMetaData) {
+			this.placeholderToParameterIndexMap = new int[this.parameterCount];
+			
+			int startPos = this.callingStoredFunction ? StringUtils.indexOfIgnoreCase(this.originalSql, 
+			"SELECT") : StringUtils.indexOfIgnoreCase(this.originalSql, "CALL");
+			
+			if (startPos != -1) {
+				int parenOpenPos = this.originalSql.indexOf('(', startPos + 4);
+				
+				if (parenOpenPos != -1) {
+					int parenClosePos = StringUtils.indexOfIgnoreCaseRespectQuotes(parenOpenPos, 
+							this.originalSql, ")", '\'', true);
+					
+					if (parenClosePos != -1) {
+						List parsedParameters = StringUtils.split(this.originalSql.substring(parenOpenPos + 1, parenClosePos), ",", "'\"", "'\"", true);
+						
+						int numParsedParameters = parsedParameters.size();
+						
+						// sanity check
+						
+						if (numParsedParameters != this.parameterCount) {
+							// bail?
+						}
+						
+						int placeholderCount = 0;
+						
+						for (int i = 0; i < numParsedParameters; i++) {
+							if (((String)parsedParameters.get(i)).equals("?")) {
+								this.placeholderToParameterIndexMap[placeholderCount++] = i;
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Creates a new CallableStatement
+	 * 
+	 * @param conn
+	 *            the connection creating this statement
+	 * @param sql
+	 *            the SQL to prepare
+	 * @param catalog
+	 *            the current catalog
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public CallableStatement(Connection conn, String sql, String catalog,
+			boolean isFunctionCall) throws SQLException {
+		super(conn, sql, catalog);
+
+		this.callingStoredFunction = isFunctionCall;
+
+		determineParameterTypes();
+		generateParameterMap();
+		
+		if (this.callingStoredFunction) {
+			this.parameterCount += 1;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#addBatch()
+	 */
+	public void addBatch() throws SQLException {
+		setOutParams();
+
+		super.addBatch();
+	}
+
+	private CallableStatementParam checkIsOutputParam(int paramIndex)
+			throws SQLException {
+
+		if (this.callingStoredFunction) {
+			if (paramIndex == 1) {
+
+				if (this.returnValueParam == null) {
+					this.returnValueParam = new CallableStatementParam("", 0,
+							false, true, Types.VARCHAR, "VARCHAR", 0, 0,
+							DatabaseMetaData.attributeNullableUnknown,
+							DatabaseMetaData.procedureColumnReturn);
+				}
+
+				return this.returnValueParam;
+			}
+
+			// Move to position in output result set
+			paramIndex--;
+		}
+
+		checkParameterIndexBounds(paramIndex);
+
+		int localParamIndex = paramIndex - 1;
+
+		if (this.placeholderToParameterIndexMap != null) {
+			localParamIndex = this.placeholderToParameterIndexMap[localParamIndex];
+		}
+		
+		CallableStatementParam paramDescriptor = this.paramInfo
+				.getParameter(localParamIndex);
+
+		if (!paramDescriptor.isOut) {
+			throw SQLError.createSQLException(
+					Messages.getString("CallableStatement.9") + paramIndex //$NON-NLS-1$
+							+ Messages.getString("CallableStatement.10"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		this.hasOutputParams = true;
+
+		return paramDescriptor;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param paramIndex
+	 * 
+	 * @throws SQLException
+	 */
+	private void checkParameterIndexBounds(int paramIndex) throws SQLException {
+		this.paramInfo.checkBounds(paramIndex);
+	}
+
+	/**
+	 * Checks whether or not this statement is supposed to be providing
+	 * streamable result sets...If output parameters are registered, the driver
+	 * can not stream the results.
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void checkStreamability() throws SQLException {
+		if (this.hasOutputParams && createStreamingResultSet()) {
+			throw SQLError.createSQLException(Messages.getString("CallableStatement.14"), //$NON-NLS-1$
+					SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+		}
+	}
+
+	public synchronized void clearParameters() throws SQLException {
+		super.clearParameters();
+
+		try {
+			if (this.outputParameterResults != null) {
+				this.outputParameterResults.close();
+			}
+		} finally {
+			this.outputParameterResults = null;
+		}
+	}
+
+	/**
+	 * Used to fake up some metadata when we don't have access to 
+	 * SHOW CREATE PROCEDURE or mysql.proc.
+	 * 
+	 * @throws SQLException if we can't build the metadata.
+	 */
+	private void fakeParameterTypes() throws SQLException {
+		Field[] fields = new Field[13];
+
+		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("", "COLUMN_NAME", Types.CHAR, 0);
+		fields[4] = new Field("", "COLUMN_TYPE", Types.CHAR, 0);
+		fields[5] = new Field("", "DATA_TYPE", Types.SMALLINT, 0);
+		fields[6] = new Field("", "TYPE_NAME", Types.CHAR, 0);
+		fields[7] = new Field("", "PRECISION", Types.INTEGER, 0);
+		fields[8] = new Field("", "LENGTH", Types.INTEGER, 0);
+		fields[9] = new Field("", "SCALE", Types.SMALLINT, 0);
+		fields[10] = new Field("", "RADIX", Types.SMALLINT, 0);
+		fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 0);
+		fields[12] = new Field("", "REMARKS", Types.CHAR, 0);
+
+		String procName = extractProcedureName();
+
+		byte[] procNameAsBytes = null;
+
+		try {
+			procNameAsBytes = procName.getBytes("UTF-8");
+		} catch (UnsupportedEncodingException ueEx) {
+			procNameAsBytes = StringUtils.s2b(procName, this.connection);
+		}
+
+		ArrayList resultRows = new ArrayList();
+
+		for (int i = 0; i < this.parameterCount; i++) {
+			byte[][] row = new byte[13][];
+			row[0] = null; // PROCEDURE_CAT
+			row[1] = null; // PROCEDURE_SCHEM
+			row[2] = procNameAsBytes; // PROCEDURE/NAME
+			row[3] = StringUtils.s2b(String.valueOf(i), this.connection); // COLUMN_NAME
+
+			row[4] = StringUtils.s2b(String
+					.valueOf(DatabaseMetaData.procedureColumnInOut),
+					this.connection);
+
+			row[5] = StringUtils.s2b(String.valueOf(Types.VARCHAR),
+					this.connection); // DATA_TYPE
+			row[6] = StringUtils.s2b("VARCHAR", this.connection); // TYPE_NAME
+			row[7] = StringUtils.s2b(Integer.toString(65535), this.connection); // PRECISION
+			row[8] = StringUtils.s2b(Integer.toString(65535), this.connection); // LENGTH
+			row[9] = StringUtils.s2b(Integer.toString(0), this.connection); // SCALE
+			row[10] = StringUtils.s2b(Integer.toString(10), this.connection); // RADIX
+
+			row[11] = StringUtils.s2b(Integer
+					.toString(DatabaseMetaData.procedureNullableUnknown),
+					this.connection); // nullable
+
+			row[12] = null;
+
+			resultRows.add(row);
+		}
+
+		java.sql.ResultSet paramTypesRs = DatabaseMetaData.buildResultSet(
+				fields, resultRows, this.connection);
+
+		convertGetProcedureColumnsToInternalDescriptors(paramTypesRs);
+	}
+	
+	private void determineParameterTypes() throws SQLException {
+		if (this.connection.getNoAccessToProcedureBodies()) {
+			fakeParameterTypes();
+			
+			return;
+		}
+		
+		java.sql.ResultSet paramTypesRs = null;
+
+		try {
+			String procName = extractProcedureName();
+
+			java.sql.DatabaseMetaData dbmd = this.connection.getMetaData();
+
+			boolean useCatalog = false;
+
+			if (procName.indexOf(".") == -1) {
+				useCatalog = true;
+			}
+
+			paramTypesRs = dbmd.getProcedureColumns(this.connection
+					.versionMeetsMinimum(5, 0, 2)
+					& useCatalog ? this.currentCatalog : null, null, procName,
+					"%"); //$NON-NLS-1$
+
+			convertGetProcedureColumnsToInternalDescriptors(paramTypesRs);
+		} finally {
+			SQLException sqlExRethrow = null;
+
+			if (paramTypesRs != null) {
+				try {
+					paramTypesRs.close();
+				} catch (SQLException sqlEx) {
+					sqlExRethrow = sqlEx;
+				}
+
+				paramTypesRs = null;
+			}
+
+			if (sqlExRethrow != null) {
+				throw sqlExRethrow;
+			}
+		}
+	}
+
+	private void convertGetProcedureColumnsToInternalDescriptors(java.sql.ResultSet paramTypesRs) throws SQLException {
+		if (!this.connection.isRunningOnJDK13()) {
+			this.paramInfo = new CallableStatementParamInfoJDBC3(
+					paramTypesRs);
+		} else {
+			this.paramInfo = new CallableStatementParamInfo(paramTypesRs);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#execute()
+	 */
+	public boolean execute() throws SQLException {
+		boolean returnVal = false;
+
+		checkClosed();
+
+		checkStreamability();
+
+		synchronized (this.connection.getMutex()) {
+			setInOutParamsOnServer();
+			setOutParams();
+
+			returnVal = super.execute();
+
+			if (this.callingStoredFunction) {
+				this.functionReturnValueResults = this.results;
+				this.functionReturnValueResults.next();
+				this.results = null;
+			}
+
+			retrieveOutParams();
+		}
+
+		if (!this.callingStoredFunction) {
+			return returnVal;
+		}
+
+		// Functions can't return results
+		return false;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#executeQuery()
+	 */
+	public java.sql.ResultSet executeQuery() throws SQLException {
+		checkClosed();
+
+		checkStreamability();
+
+		java.sql.ResultSet execResults = null;
+
+		synchronized (this.connection.getMutex()) {
+			setInOutParamsOnServer();
+			setOutParams();
+
+			execResults = super.executeQuery();
+
+			retrieveOutParams();
+		}
+
+		return execResults;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#executeUpdate()
+	 */
+	public int executeUpdate() throws SQLException {
+		int returnVal = -1;
+
+		checkClosed();
+
+		checkStreamability();
+
+		if (this.callingStoredFunction) {
+			execute();
+
+			return -1;
+		}
+
+		synchronized (this.connection.getMutex()) {
+			setInOutParamsOnServer();
+			setOutParams();
+
+			returnVal = super.executeUpdate();
+
+			retrieveOutParams();
+		}
+
+		return returnVal;
+	}
+
+	private String extractProcedureName() throws SQLException {
+		// TODO: Do this with less memory allocation
+		int endCallIndex = StringUtils.indexOfIgnoreCase(this.originalSql,
+				"CALL "); //$NON-NLS-1$
+		int offset = 5;
+
+		if (endCallIndex == -1) {
+			endCallIndex = StringUtils.indexOfIgnoreCase(this.originalSql,
+					"SELECT ");
+			offset = 7;
+		}
+
+		if (endCallIndex != -1) {
+			StringBuffer nameBuf = new StringBuffer();
+
+			String trimmedStatement = this.originalSql.substring(
+					endCallIndex + offset).trim();
+
+			int statementLength = trimmedStatement.length();
+
+			for (int i = 0; i < statementLength; i++) {
+				char c = trimmedStatement.charAt(i);
+
+				if (Character.isWhitespace(c) || (c == '(') || (c == '?')) {
+					break;
+				}
+				nameBuf.append(c);
+
+			}
+
+			return nameBuf.toString();
+		}
+		throw SQLError.createSQLException(Messages.getString("CallableStatement.1"), //$NON-NLS-1$
+				SQLError.SQL_STATE_GENERAL_ERROR);
+
+	}
+
+	/**
+	 * Adds 'at' symbol to beginning of parameter names if needed.
+	 * 
+	 * @param paramNameIn
+	 *            the parameter name to 'fix'
+	 * 
+	 * @return the parameter name with an 'a' prepended, if needed
+	 * 
+	 * @throws SQLException
+	 *             if the parameter name is null or empty.
+	 */
+	private String fixParameterName(String paramNameIn) throws SQLException {
+		if ((paramNameIn == null) || (paramNameIn.length() == 0)) {
+			throw SQLError.createSQLException(
+					((Messages.getString("CallableStatement.0") + paramNameIn) == null) //$NON-NLS-1$
+							? Messages.getString("CallableStatement.15") : Messages.getString("CallableStatement.16"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		if (this.connection.getNoAccessToProcedureBodies()) {
+			throw SQLError.createSQLException("No access to parameters by name when connection has been configured not to access procedure bodies",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		return mangleParameterName(paramNameIn);
+
+		/*
+		 * if (paramNameIn.startsWith("@")) { return paramNameIn; } else {
+		 * StringBuffer paramNameBuf = new StringBuffer("@");
+		 * paramNameBuf.append(paramNameIn);
+		 * 
+		 * return paramNameBuf.toString(); }
+		 */
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getArray(int)
+	 */
+	public synchronized Array getArray(int i) throws SQLException {
+		ResultSet rs = getOutputParameters(i);
+
+		Array retValue = rs.getArray(mapOutputParameterIndexToRsIndex(i));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getArray(java.lang.String)
+	 */
+	public synchronized Array getArray(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		Array retValue = rs.getArray(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getBigDecimal(int)
+	 */
+	public synchronized BigDecimal getBigDecimal(int parameterIndex)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		BigDecimal retValue = rs
+				.getBigDecimal(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param parameterIndex
+	 *            DOCUMENT ME!
+	 * @param scale
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 * 
+	 * @see java.sql.CallableStatement#getBigDecimal(int, int)
+	 * @deprecated
+	 */
+	public synchronized BigDecimal getBigDecimal(int parameterIndex, int scale)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		BigDecimal retValue = rs.getBigDecimal(
+				mapOutputParameterIndexToRsIndex(parameterIndex), scale);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getBigDecimal(java.lang.String)
+	 */
+	public synchronized BigDecimal getBigDecimal(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		BigDecimal retValue = rs.getBigDecimal(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getBlob(int)
+	 */
+	public synchronized Blob getBlob(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Blob retValue = rs
+				.getBlob(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		Blob retValue = rs.getBlob(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getBoolean(int)
+	 */
+	public synchronized boolean getBoolean(int parameterIndex)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		boolean retValue = rs
+				.getBoolean(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getBoolean(java.lang.String)
+	 */
+	public synchronized boolean getBoolean(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		boolean retValue = rs.getBoolean(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getByte(int)
+	 */
+	public synchronized byte getByte(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		byte retValue = rs
+				.getByte(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		byte retValue = rs.getByte(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getBytes(int)
+	 */
+	public synchronized byte[] getBytes(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		byte[] retValue = rs
+				.getBytes(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getBytes(java.lang.String)
+	 */
+	public synchronized byte[] getBytes(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		byte[] retValue = rs.getBytes(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getClob(int)
+	 */
+	public synchronized Clob getClob(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Clob retValue = rs
+				.getClob(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		Clob retValue = rs.getClob(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getDate(int)
+	 */
+	public synchronized Date getDate(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Date retValue = rs
+				.getDate(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getDate(int, java.util.Calendar)
+	 */
+	public synchronized Date getDate(int parameterIndex, Calendar cal)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Date retValue = rs.getDate(
+				mapOutputParameterIndexToRsIndex(parameterIndex), cal);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		Date retValue = rs.getDate(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getDate(java.lang.String,
+	 *      java.util.Calendar)
+	 */
+	public synchronized Date getDate(String parameterName, Calendar cal)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		Date retValue = rs.getDate(fixParameterName(parameterName), cal);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getDouble(int)
+	 */
+	public synchronized double getDouble(int parameterIndex)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		double retValue = rs
+				.getDouble(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getDouble(java.lang.String)
+	 */
+	public synchronized double getDouble(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		double retValue = rs.getDouble(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getFloat(int)
+	 */
+	public synchronized float getFloat(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		float retValue = rs
+				.getFloat(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getFloat(java.lang.String)
+	 */
+	public synchronized float getFloat(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		float retValue = rs.getFloat(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getInt(int)
+	 */
+	public synchronized int getInt(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		int retValue = rs
+				.getInt(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		int retValue = rs.getInt(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getLong(int)
+	 */
+	public synchronized long getLong(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		long retValue = rs
+				.getLong(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		long retValue = rs.getLong(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	private 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",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		if ((paramName == null) || (paramName.length() == 0)) {
+			throw SQLError.createSQLException(Messages.getString("CallableStatement.2"), //$NON-NLS-1$
+					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);
+		}
+
+		if (forOut && !namedParamInfo.isOut) {
+			throw SQLError.createSQLException(
+					Messages.getString("CallableStatement.5") + paramName //$NON-NLS-1$
+					+ Messages.getString("CallableStatement.6"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+
+		if (this.placeholderToParameterIndexMap == null) {
+			return namedParamInfo.index + 1; // JDBC indices are 1-based
+		} 
+
+		for (int i = 0; i < this.placeholderToParameterIndexMap.length; i++) {
+			if (this.placeholderToParameterIndexMap[i] == namedParamInfo.index) {
+				return i + 1;
+			}
+		}
+
+		throw SQLError.createSQLException("Can't find local placeholder mapping for parameter named \"" + 
+				paramName + "\".", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getObject(int)
+	 */
+	public synchronized Object getObject(int parameterIndex)
+			throws SQLException {
+		CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex);
+
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Object retVal = rs.getObjectStoredProc(
+				mapOutputParameterIndexToRsIndex(parameterIndex),
+				paramDescriptor.desiredJdbcType);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retVal;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getObject(int, java.util.Map)
+	 */
+	public synchronized Object getObject(int parameterIndex, Map map)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Object retVal = rs.getObject(
+				mapOutputParameterIndexToRsIndex(parameterIndex), map);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retVal;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getObject(java.lang.String)
+	 */
+	public synchronized Object getObject(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		Object retValue = rs.getObject(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getObject(java.lang.String,
+	 *      java.util.Map)
+	 */
+	public synchronized Object getObject(String parameterName, Map map)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		Object retValue = rs.getObject(fixParameterName(parameterName), map);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * Returns the ResultSet that holds the output parameters, or throws an
+	 * appropriate exception if none exist, or they weren't returned.
+	 * 
+	 * @return the ResultSet that holds the output parameters
+	 * 
+	 * @throws SQLException
+	 *             if no output parameters were defined, or if no output
+	 *             parameters were returned.
+	 */
+	private ResultSet getOutputParameters(int paramIndex) throws SQLException {
+		this.outputParamWasNull = false;
+
+		if (paramIndex == 1 && this.callingStoredFunction
+				&& this.returnValueParam != null) {
+			return this.functionReturnValueResults;
+		}
+
+		if (this.outputParameterResults == null) {
+			if (this.paramInfo.numberOfParameters() == 0) {
+				throw SQLError.createSQLException(Messages
+						.getString("CallableStatement.7"), //$NON-NLS-1$
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+			throw SQLError.createSQLException(Messages.getString("CallableStatement.8"), //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		return this.outputParameterResults;
+
+	}
+
+	public synchronized ParameterMetaData getParameterMetaData()
+			throws SQLException {
+		if (this.placeholderToParameterIndexMap == null) {
+			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);
+
+		Ref retValue = rs
+				.getRef(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		Ref retValue = rs.getRef(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getShort(int)
+	 */
+	public synchronized short getShort(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		short retValue = rs
+				.getShort(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getShort(java.lang.String)
+	 */
+	public synchronized short getShort(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		short retValue = rs.getShort(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getString(int)
+	 */
+	public synchronized String getString(int parameterIndex)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		String retValue = rs
+				.getString(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getString(java.lang.String)
+	 */
+	public synchronized String getString(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		String retValue = rs.getString(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getTime(int)
+	 */
+	public synchronized Time getTime(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Time retValue = rs
+				.getTime(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getTime(int, java.util.Calendar)
+	 */
+	public synchronized Time getTime(int parameterIndex, Calendar cal)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Time retValue = rs.getTime(
+				mapOutputParameterIndexToRsIndex(parameterIndex), cal);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		Time retValue = rs.getTime(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getTime(java.lang.String,
+	 *      java.util.Calendar)
+	 */
+	public synchronized Time getTime(String parameterName, Calendar cal)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		Time retValue = rs.getTime(fixParameterName(parameterName), cal);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getTimestamp(int)
+	 */
+	public synchronized Timestamp getTimestamp(int parameterIndex)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Timestamp retValue = rs
+				.getTimestamp(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getTimestamp(int, java.util.Calendar)
+	 */
+	public synchronized Timestamp getTimestamp(int parameterIndex, Calendar cal)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		Timestamp retValue = rs.getTimestamp(
+				mapOutputParameterIndexToRsIndex(parameterIndex), cal);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getTimestamp(java.lang.String)
+	 */
+	public synchronized Timestamp getTimestamp(String parameterName)
+			throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getTimestamp(java.lang.String,
+	 *      java.util.Calendar)
+	 */
+	public synchronized Timestamp getTimestamp(String parameterName,
+			Calendar cal) throws SQLException {
+		ResultSet rs = getOutputParameters(0); // definitely not going to be
+		// from ?=
+
+		Timestamp retValue = rs.getTimestamp(fixParameterName(parameterName),
+				cal);
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#getURL(int)
+	 */
+	public synchronized URL getURL(int parameterIndex) throws SQLException {
+		ResultSet rs = getOutputParameters(parameterIndex);
+
+		URL retValue = rs
+				.getURL(mapOutputParameterIndexToRsIndex(parameterIndex));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	/**
+	 * @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
+		// from ?=
+
+		URL retValue = rs.getURL(fixParameterName(parameterName));
+
+		this.outputParamWasNull = rs.wasNull();
+
+		return retValue;
+	}
+
+	private int mapOutputParameterIndexToRsIndex(int paramIndex)
+			throws SQLException {
+
+		if (this.returnValueParam != null && paramIndex == 1) {
+			return 1;
+		}
+
+		checkParameterIndexBounds(paramIndex);
+
+		int localParamIndex = paramIndex - 1;
+
+		if (this.placeholderToParameterIndexMap != null) {
+			localParamIndex = this.placeholderToParameterIndexMap[localParamIndex];
+		}
+
+		int rsIndex = this.parameterIndexToRsIndex[localParamIndex];
+
+		if (rsIndex == NOT_OUTPUT_PARAMETER_INDICATOR) {
+			throw SQLError.createSQLException(
+					Messages.getString("CallableStatement.21") + paramIndex //$NON-NLS-1$
+							+ Messages.getString("CallableStatement.22"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		return rsIndex + 1;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#registerOutParameter(int, int)
+	 */
+	public void registerOutParameter(int parameterIndex, int sqlType)
+			throws SQLException {
+		CallableStatementParam paramDescriptor = checkIsOutputParam(parameterIndex);
+		paramDescriptor.desiredJdbcType = sqlType;
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#registerOutParameter(int, int, int)
+	 */
+	public void registerOutParameter(int parameterIndex, int sqlType, int scale)
+			throws SQLException {
+		registerOutParameter(parameterIndex, sqlType);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#registerOutParameter(int, int,
+	 *      java.lang.String)
+	 */
+	public void registerOutParameter(int parameterIndex, int sqlType,
+			String typeName) throws SQLException {
+		checkIsOutputParam(parameterIndex);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#registerOutParameter(java.lang.String,
+	 *      int)
+	 */
+	public synchronized void registerOutParameter(String parameterName,
+			int sqlType) throws SQLException {
+		registerOutParameter(getNamedParamIndex(parameterName, true), sqlType);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#registerOutParameter(java.lang.String,
+	 *      int, int)
+	 */
+	public void registerOutParameter(String parameterName, int sqlType,
+			int scale) throws SQLException {
+		registerOutParameter(getNamedParamIndex(parameterName, true), sqlType);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#registerOutParameter(java.lang.String,
+	 *      int, java.lang.String)
+	 */
+	public void registerOutParameter(String parameterName, int sqlType,
+			String typeName) throws SQLException {
+		registerOutParameter(getNamedParamIndex(parameterName, true), sqlType,
+				typeName);
+	}
+
+	/**
+	 * Issues a second query to retrieve all output parameters.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	private void retrieveOutParams() throws SQLException {
+		int numParameters = this.paramInfo.numberOfParameters();
+
+		this.parameterIndexToRsIndex = new int[numParameters];
+
+		for (int i = 0; i < numParameters; i++) {
+			this.parameterIndexToRsIndex[i] = NOT_OUTPUT_PARAMETER_INDICATOR;
+		}
+
+		int localParamIndex = 0;
+
+		if (numParameters > 0) {
+			StringBuffer outParameterQuery = new StringBuffer("SELECT "); //$NON-NLS-1$
+
+			boolean firstParam = true;
+			boolean hadOutputParams = false;
+
+			for (Iterator paramIter = this.paramInfo.iterator(); paramIter
+					.hasNext();) {
+				CallableStatementParam retrParamInfo = (CallableStatementParam) paramIter
+						.next();
+
+				if (retrParamInfo.isOut) {
+					hadOutputParams = true;
+
+					this.parameterIndexToRsIndex[retrParamInfo.index] = localParamIndex++;
+
+					String outParameterName = mangleParameterName(retrParamInfo.paramName);
+
+					if (!firstParam) {
+						outParameterQuery.append(","); //$NON-NLS-1$
+					} else {
+						firstParam = false;
+					}
+
+					if (!outParameterName.startsWith("@")) { //$NON-NLS-1$
+						outParameterQuery.append('@');
+					}
+
+					outParameterQuery.append(outParameterName);
+				}
+			}
+
+			if (hadOutputParams) {
+				// We can't use 'ourself' to execute this query, or any
+				// pending result sets would be overwritten
+				java.sql.Statement outParameterStmt = null;
+				java.sql.ResultSet outParamRs = null;
+
+				try {
+					outParameterStmt = this.connection.createStatement();
+					outParamRs = outParameterStmt
+							.executeQuery(outParameterQuery.toString());
+					this.outputParameterResults = ((com.mysql.jdbc.ResultSet) outParamRs)
+							.copy();
+
+					if (!this.outputParameterResults.next()) {
+						this.outputParameterResults.close();
+						this.outputParameterResults = null;
+					}
+				} finally {
+					if (outParameterStmt != null) {
+						outParameterStmt.close();
+					}
+				}
+			} else {
+				this.outputParameterResults = null;
+			}
+		} else {
+			this.outputParameterResults = null;
+		}
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setAsciiStream(java.lang.String,
+	 *      java.io.InputStream, int)
+	 */
+	public void setAsciiStream(String parameterName, InputStream x, int length)
+			throws SQLException {
+		setAsciiStream(getNamedParamIndex(parameterName, false), x, length);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setBigDecimal(java.lang.String,
+	 *      java.math.BigDecimal)
+	 */
+	public void setBigDecimal(String parameterName, BigDecimal x)
+			throws SQLException {
+		setBigDecimal(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setBinaryStream(java.lang.String,
+	 *      java.io.InputStream, int)
+	 */
+	public void setBinaryStream(String parameterName, InputStream x, int length)
+			throws SQLException {
+		setBinaryStream(getNamedParamIndex(parameterName, false), x, length);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setBoolean(java.lang.String, boolean)
+	 */
+	public void setBoolean(String parameterName, boolean x) throws SQLException {
+		setBoolean(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setByte(java.lang.String, byte)
+	 */
+	public void setByte(String parameterName, byte x) throws SQLException {
+		setByte(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setBytes(java.lang.String, byte[])
+	 */
+	public void setBytes(String parameterName, byte[] x) throws SQLException {
+		setBytes(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setCharacterStream(java.lang.String,
+	 *      java.io.Reader, int)
+	 */
+	public void setCharacterStream(String parameterName, Reader reader,
+			int length) throws SQLException {
+		setCharacterStream(getNamedParamIndex(parameterName, false), reader,
+				length);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date)
+	 */
+	public void setDate(String parameterName, Date x) throws SQLException {
+		setDate(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date,
+	 *      java.util.Calendar)
+	 */
+	public void setDate(String parameterName, Date x, Calendar cal)
+			throws SQLException {
+		setDate(getNamedParamIndex(parameterName, false), x, cal);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setDouble(java.lang.String, double)
+	 */
+	public void setDouble(String parameterName, double x) throws SQLException {
+		setDouble(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setFloat(java.lang.String, float)
+	 */
+	public void setFloat(String parameterName, float x) throws SQLException {
+		setFloat(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * 
+	 */
+	private void setInOutParamsOnServer() throws SQLException {
+		if (this.paramInfo.numParameters > 0) {
+			int parameterIndex = 0;
+
+			for (Iterator paramIter = this.paramInfo.iterator(); paramIter
+					.hasNext();) {
+
+				CallableStatementParam inParamInfo = (CallableStatementParam) paramIter
+						.next();
+
+				if (inParamInfo.isOut && inParamInfo.isIn) {
+					String inOutParameterName = mangleParameterName(inParamInfo.paramName);
+					StringBuffer queryBuf = new StringBuffer(
+							4 + inOutParameterName.length() + 1 + 1);
+					queryBuf.append("SET "); //$NON-NLS-1$
+					queryBuf.append(inOutParameterName);
+					queryBuf.append("=?"); //$NON-NLS-1$
+
+					PreparedStatement setPstmt = null;
+
+					try {
+						setPstmt = this.connection
+								.clientPrepareStatement(queryBuf.toString());
+
+						byte[] parameterAsBytes = this
+								.getBytesRepresentation(inParamInfo.index);
+
+						if (parameterAsBytes != null) {
+							if (parameterAsBytes.length > 8
+									&& parameterAsBytes[0] == '_'
+									&& parameterAsBytes[1] == 'b'
+									&& parameterAsBytes[2] == 'i'
+									&& parameterAsBytes[3] == 'n'
+									&& parameterAsBytes[4] == 'a'
+									&& parameterAsBytes[5] == 'r'
+									&& parameterAsBytes[6] == 'y'
+									&& parameterAsBytes[7] == '\'') {
+								setPstmt.setBytesNoEscapeNoQuotes(1,
+										parameterAsBytes);
+							} else {
+								setPstmt.setBytes(1, parameterAsBytes);
+							}
+						} else {
+							setPstmt.setNull(1, Types.NULL);
+						}
+
+						setPstmt.executeUpdate();
+					} finally {
+						if (setPstmt != null) {
+							setPstmt.close();
+						}
+					}
+				}
+
+				parameterIndex++;
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setInt(java.lang.String, int)
+	 */
+	public void setInt(String parameterName, int x) throws SQLException {
+		setInt(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setLong(java.lang.String, long)
+	 */
+	public void setLong(String parameterName, long x) throws SQLException {
+		setLong(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setNull(java.lang.String, int)
+	 */
+	public void setNull(String parameterName, int sqlType) throws SQLException {
+		setNull(getNamedParamIndex(parameterName, false), sqlType);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setNull(java.lang.String, int,
+	 *      java.lang.String)
+	 */
+	public void setNull(String parameterName, int sqlType, String typeName)
+			throws SQLException {
+		setNull(getNamedParamIndex(parameterName, false), sqlType, typeName);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setObject(java.lang.String,
+	 *      java.lang.Object)
+	 */
+	public void setObject(String parameterName, Object x) throws SQLException {
+		setObject(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setObject(java.lang.String,
+	 *      java.lang.Object, int)
+	 */
+	public void setObject(String parameterName, Object x, int targetSqlType)
+			throws SQLException {
+		setObject(getNamedParamIndex(parameterName, false), x, targetSqlType);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setObject(java.lang.String,
+	 *      java.lang.Object, int, int)
+	 */
+	public void setObject(String parameterName, Object x, int targetSqlType,
+			int scale) throws SQLException {
+	}
+
+	private void setOutParams() throws SQLException {
+		if (this.paramInfo.numParameters > 0) {
+			for (Iterator paramIter = this.paramInfo.iterator(); paramIter
+					.hasNext();) {
+				CallableStatementParam outParamInfo = (CallableStatementParam) paramIter
+						.next();
+
+				if (!this.callingStoredFunction && outParamInfo.isOut) {
+					String outParameterName = mangleParameterName(outParamInfo.paramName);
+
+					int outParamIndex;
+					
+					if (this.placeholderToParameterIndexMap == null) { 
+							outParamIndex = outParamInfo.index + 1;
+					} else {
+							outParamIndex = this.placeholderToParameterIndexMap[outParamInfo.index - 1 /* JDBC is 1-based */];
+					}
+					
+					this.setBytesNoEscapeNoQuotes(outParamIndex,
+							StringUtils.getBytes(outParameterName,
+									this.charConverter, this.charEncoding,
+									this.connection
+											.getServerCharacterEncoding(),
+									this.connection.parserKnowsUnicode()));
+				}
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setShort(java.lang.String, short)
+	 */
+	public void setShort(String parameterName, short x) throws SQLException {
+		setShort(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setString(java.lang.String,
+	 *      java.lang.String)
+	 */
+	public void setString(String parameterName, String x) throws SQLException {
+		setString(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time)
+	 */
+	public void setTime(String parameterName, Time x) throws SQLException {
+		setTime(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time,
+	 *      java.util.Calendar)
+	 */
+	public void setTime(String parameterName, Time x, Calendar cal)
+			throws SQLException {
+		setTime(getNamedParamIndex(parameterName, false), x, cal);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setTimestamp(java.lang.String,
+	 *      java.sql.Timestamp)
+	 */
+	public void setTimestamp(String parameterName, Timestamp x)
+			throws SQLException {
+		setTimestamp(getNamedParamIndex(parameterName, false), x);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setTimestamp(java.lang.String,
+	 *      java.sql.Timestamp, java.util.Calendar)
+	 */
+	public void setTimestamp(String parameterName, Timestamp x, Calendar cal)
+			throws SQLException {
+		setTimestamp(getNamedParamIndex(parameterName, false), x, cal);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#setURL(java.lang.String, java.net.URL)
+	 */
+	public void setURL(String parameterName, URL val) throws SQLException {
+		setURL(getNamedParamIndex(parameterName, false), val);
+	}
+
+	/**
+	 * @see java.sql.CallableStatement#wasNull()
+	 */
+	public synchronized boolean wasNull() throws SQLException {
+		return this.outputParamWasNull;
+	}
+
+	public int[] executeBatch() throws SQLException {
+		if (this.hasOutputParams) {
+			throw SQLError.createSQLException("Can't call executeBatch() on CallableStatement with OUTPUT parameters",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		return super.executeBatch();
+	}
+
+	protected int getParameterIndexOffset() {
+		if (this.callingStoredFunction) {
+			return -1;
+		}
+		
+		return super.getParameterIndexOffset();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CharsetMapping.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CharsetMapping.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CharsetMapping.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,797 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+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 java.util.Set;
+import java.util.SortedSet;
+
+/**
+ * Mapping between MySQL charset names and Java charset names. I've investigated
+ * placing these in a .properties file, but unfortunately under most appservers
+ * this complicates configuration because the security policy needs to be
+ * changed by the user to allow the driver to read them :(
+ * 
+ * @author Mark Matthews
+ */
+public class CharsetMapping {
+	private static final Properties CHARSET_CONFIG = new Properties();
+
+	/**
+	 * Map of MySQL-4.1 charset indexes to Java encoding names
+	 */
+	public static final String[] INDEX_TO_CHARSET;
+
+	/** Mapping of Java charset names to MySQL charset names */
+	private static final Map JAVA_TO_MYSQL_CHARSET_MAP;
+
+	private static final Map JAVA_UC_TO_MYSQL_CHARSET_MAP;
+	
+	private static final Map ERROR_MESSAGE_FILE_TO_MYSQL_CHARSET_MAP;
+
+	/** Map/List of multibyte character sets (using MySQL names) */
+	private static final Map MULTIBYTE_CHARSETS;
+
+	private static final Map MYSQL_TO_JAVA_CHARSET_MAP;
+
+	static {	
+		
+		CHARSET_CONFIG.setProperty("javaToMysqlMappings",
+			//
+			// Note: This used to be stored in Charsets.properties,
+			// but turned out to be problematic when dealing with
+			// Tomcat classloaders when the security manager was
+			// enabled
+			//
+			// Java Encoding		MySQL Name (and version, '*' 
+			//                           denotes preferred value)      
+			//
+			"US-ASCII =			usa7,"
+	 		+ "US-ASCII =			ascii,"
+	 		+ "Big5 = 				big5,"
+	 		+ "GBK = 				gbk,"
+	 		+ "SJIS = 				sjis,"
+	 		+ "EUC_CN = 			gb2312,"
+	 		+ "EUC_JP = 			ujis,"
+	 		+ "EUC_JP_Solaris = 	>5.0.3 eucjpms,"
+	 		+ "EUC_KR = 			euc_kr,"
+	 		+ "EUC_KR = 			>4.1.0 euckr,"
+	 		+ "ISO8859_1 =			*latin1,"
+	 		+ "ISO8859_1 =			latin1_de,"
+	 		+ "ISO8859_1 =			german1,"
+	 		+ "ISO8859_1 =			danish,"
+	 		+ "ISO8859_2 =			latin2,"
+			+ "ISO8859_2 =			czech,"
+			+ "ISO8859_2 =			hungarian,"
+			+ "ISO8859_2  =		croat,"
+			+ "ISO8859_7  =		greek,"
+			+ "ISO8859_7  =		latin7,"
+			+ "ISO8859_8  = 		hebrew,"
+			+ "ISO8859_9  =		latin5,"
+	 		+ "ISO8859_13 =		latvian,"
+			+ "ISO8859_13 =		latvian1,"
+			+ "ISO8859_13 =		estonia,"
+			+ "Cp437 =             *>4.1.0 cp850,"
+	 		+ "Cp437 =				dos,"
+	 		+ "Cp850 =				Cp850,"
+			+ "Cp852 = 			Cp852,"
+	 		+ "Cp866 = 			cp866,"
+	 		+ "KOI8_R = 			koi8_ru,"
+			+ "KOI8_R = 			>4.1.0 koi8r,"
+	 		+ "TIS620 = 			tis620,"
+			+ "Cp1250 = 			cp1250,"
+			+ "Cp1250 = 			win1250,"
+			+ "Cp1251 = 			*>4.1.0 cp1251,"
+			+ "Cp1251 = 			win1251," 
+	 		+ "Cp1251 = 			cp1251cias,"
+			+ "Cp1251 = 			cp1251csas,"
+			+ "Cp1256 = 			cp1256,"
+	 		+ "Cp1251 = 			win1251ukr,"
+	 		+ "Cp1252 =             latin1,"
+			+ "Cp1257 = 			cp1257,"
+			+ "MacRoman = 			macroman,"
+			+ "MacCentralEurope = 	macce,"
+			+ "UTF-8 = 		utf8,"
+			+ "UnicodeBig = 	ucs2,"
+			+ "US-ASCII =		binary,"
+			+ "Cp943 =        	sjis,"
+			+ "MS932 =			sjis,"
+			+ "MS932 =        	>4.1.11 cp932,"
+			+ "WINDOWS-31J =	sjis,"
+			+ "WINDOWS-31J = 	>4.1.11 cp932,"
+			+ "CP932 =			sjis,"
+			+ "CP932 =			*>4.1.11 cp932,"
+			+ "SHIFT_JIS = 	sjis,"
+			+ "ASCII =			ascii,"
+	        + "LATIN5 =		latin5,"
+	        + "LATIN7 =		latin7,"
+	        + "HEBREW =		hebrew,"
+	        + "GREEK =			greek,"
+	        + "EUCKR =			euckr,"
+	        + "GB2312 =		gb2312,"
+	        + "LATIN2 =		latin2");
+
+		HashMap javaToMysqlMap = new HashMap();
+
+		populateMapWithKeyValuePairs("javaToMysqlMappings", javaToMysqlMap,
+				true, false);
+		JAVA_TO_MYSQL_CHARSET_MAP = Collections.unmodifiableMap(javaToMysqlMap);
+
+		HashMap mysqlToJavaMap = new HashMap();
+
+		Set keySet = JAVA_TO_MYSQL_CHARSET_MAP.keySet();
+
+		Iterator javaCharsets = keySet.iterator();
+
+		while (javaCharsets.hasNext()) {
+			Object javaEncodingName = javaCharsets.next();
+			List mysqlEncodingList = (List) JAVA_TO_MYSQL_CHARSET_MAP
+					.get(javaEncodingName);
+
+			Iterator mysqlEncodings = mysqlEncodingList.iterator();
+
+			String mysqlEncodingName = null;
+
+			while (mysqlEncodings.hasNext()) {
+				VersionedStringProperty mysqlProp = (VersionedStringProperty) mysqlEncodings
+						.next();
+				mysqlEncodingName = mysqlProp.toString();
+
+				mysqlToJavaMap.put(mysqlEncodingName, javaEncodingName);
+				mysqlToJavaMap.put(mysqlEncodingName
+						.toUpperCase(Locale.ENGLISH), javaEncodingName);
+			}
+		}
+
+		// we don't want CP932 to map to CP932
+		mysqlToJavaMap.put("cp932", "Windows-31J");
+		mysqlToJavaMap.put("CP932", "Windows-31J");
+
+		MYSQL_TO_JAVA_CHARSET_MAP = Collections.unmodifiableMap(mysqlToJavaMap);
+
+		HashMap ucMap = new HashMap(JAVA_TO_MYSQL_CHARSET_MAP.size());
+
+		Iterator javaNamesKeys = JAVA_TO_MYSQL_CHARSET_MAP.keySet().iterator();
+
+		while (javaNamesKeys.hasNext()) {
+			String key = (String) javaNamesKeys.next();
+
+			ucMap.put(key.toUpperCase(Locale.ENGLISH),
+					JAVA_TO_MYSQL_CHARSET_MAP.get(key));
+		}
+
+		JAVA_UC_TO_MYSQL_CHARSET_MAP = Collections.unmodifiableMap(ucMap);
+
+		//
+		// Character sets that we can't convert
+		// ourselves.
+		//
+		HashMap tempMapMulti = new HashMap();
+
+		CHARSET_CONFIG.setProperty("multibyteCharsets", 
+			//
+			// Note: This used to be stored in Charsets.properties,
+			// but turned out to be problematic when dealing with
+			// Tomcat classloaders when the security manager was
+			// enabled
+			//
+			//   Java Name			MySQL Name (not currently used)
+			//
+				
+	        "Big5 = 			big5,"
+	 		+ "GBK = 			gbk,"
+	 		+ "SJIS = 			sjis,"
+	 		+ "EUC_CN = 		gb2312,"
+	 		+ "EUC_JP = 		ujis,"
+	 		+ "EUC_JP_Solaris = eucjpms,"
+	 		+ "EUC_KR = 		euc_kr,"
+	 		+ "EUC_KR = 		>4.1.0 euckr,"
+	 		+ "Cp943 =        	sjis,"
+	 		+ "Cp943 = 		cp943,"
+	 		+ "WINDOWS-31J =	sjis,"
+	 		+ "WINDOWS-31J = 	cp932,"
+	 		+ "CP932 =			cp932,"
+	 		+ "MS932 =			sjis,"
+	 		+ "MS932 =        	cp932,"
+	 		+ "SHIFT_JIS = 	sjis,"
+	 		+ "EUCKR =			euckr,"
+	 		+ "GB2312 =		gb2312,"
+	 		+ "UTF-8 = 		utf8,"
+	 		+ "utf8 =          utf8,"
+	 		+ "UnicodeBig = 	ucs2");
+		
+		populateMapWithKeyValuePairs("multibyteCharsets", tempMapMulti, false,
+				true);
+
+		MULTIBYTE_CHARSETS = Collections.unmodifiableMap(tempMapMulti);
+
+		INDEX_TO_CHARSET = new String[211];
+
+		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[5] = getJavaEncodingForMysqlEncoding("german1",
+					null);
+			INDEX_TO_CHARSET[6] = getJavaEncodingForMysqlEncoding("hp8", null);
+			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[11] = getJavaEncodingForMysqlEncoding("usa7", null);
+			INDEX_TO_CHARSET[12] = getJavaEncodingForMysqlEncoding("ujis", null);
+			INDEX_TO_CHARSET[13] = getJavaEncodingForMysqlEncoding("sjis", null);
+			INDEX_TO_CHARSET[14] = getJavaEncodingForMysqlEncoding("cp1251",
+					null);
+			INDEX_TO_CHARSET[15] = getJavaEncodingForMysqlEncoding("danish",
+					null);
+			INDEX_TO_CHARSET[16] = getJavaEncodingForMysqlEncoding("hebrew",
+					null);
+			INDEX_TO_CHARSET[18] = getJavaEncodingForMysqlEncoding("tis620",
+					null);
+			INDEX_TO_CHARSET[19] = getJavaEncodingForMysqlEncoding("euc_kr",
+					null);
+			INDEX_TO_CHARSET[20] = getJavaEncodingForMysqlEncoding("estonia",
+					null);
+			INDEX_TO_CHARSET[21] = getJavaEncodingForMysqlEncoding("hungarian",
+					null);
+			INDEX_TO_CHARSET[22] = getJavaEncodingForMysqlEncoding("koi8_ukr",
+					null);
+			INDEX_TO_CHARSET[23] = getJavaEncodingForMysqlEncoding(
+					"win1251ukr", null);
+			INDEX_TO_CHARSET[24] = getJavaEncodingForMysqlEncoding("gb2312",
+					null);
+			INDEX_TO_CHARSET[25] = getJavaEncodingForMysqlEncoding("greek",
+					null);
+			INDEX_TO_CHARSET[26] = getJavaEncodingForMysqlEncoding("win1250",
+					null);
+			INDEX_TO_CHARSET[27] = getJavaEncodingForMysqlEncoding("croat",
+					null);
+			INDEX_TO_CHARSET[28] = getJavaEncodingForMysqlEncoding("gbk", null);
+			INDEX_TO_CHARSET[29] = getJavaEncodingForMysqlEncoding("cp1257",
+					null);
+			INDEX_TO_CHARSET[30] = getJavaEncodingForMysqlEncoding("latin5",
+					null);
+			INDEX_TO_CHARSET[31] = getJavaEncodingForMysqlEncoding("latin1_de",
+					null);
+			INDEX_TO_CHARSET[32] = getJavaEncodingForMysqlEncoding("armscii8",
+					null);
+			INDEX_TO_CHARSET[33] = getJavaEncodingForMysqlEncoding("utf8", null);
+			INDEX_TO_CHARSET[34] = getJavaEncodingForMysqlEncoding("win1250ch",
+					null);
+			INDEX_TO_CHARSET[35] = getJavaEncodingForMysqlEncoding("ucs2", null);
+			INDEX_TO_CHARSET[36] = getJavaEncodingForMysqlEncoding("cp866",
+					null);
+			INDEX_TO_CHARSET[37] = getJavaEncodingForMysqlEncoding("keybcs2",
+					null);
+			INDEX_TO_CHARSET[38] = getJavaEncodingForMysqlEncoding("macce",
+					null);
+			INDEX_TO_CHARSET[39] = getJavaEncodingForMysqlEncoding("macroman",
+					null);
+			INDEX_TO_CHARSET[40] = getJavaEncodingForMysqlEncoding("pclatin2",
+					null);
+			INDEX_TO_CHARSET[41] = getJavaEncodingForMysqlEncoding("latvian",
+					null);
+			INDEX_TO_CHARSET[42] = getJavaEncodingForMysqlEncoding("latvian1",
+					null);
+			INDEX_TO_CHARSET[43] = getJavaEncodingForMysqlEncoding("maccebin",
+					null);
+			INDEX_TO_CHARSET[44] = getJavaEncodingForMysqlEncoding("macceciai",
+					null);
+			INDEX_TO_CHARSET[45] = getJavaEncodingForMysqlEncoding("maccecias",
+					null);
+			INDEX_TO_CHARSET[46] = getJavaEncodingForMysqlEncoding("maccecsas",
+					null);
+			INDEX_TO_CHARSET[47] = getJavaEncodingForMysqlEncoding("latin1bin",
+					null);
+			INDEX_TO_CHARSET[48] = getJavaEncodingForMysqlEncoding(
+					"latin1cias", null);
+			INDEX_TO_CHARSET[49] = getJavaEncodingForMysqlEncoding(
+					"latin1csas", null);
+			INDEX_TO_CHARSET[50] = getJavaEncodingForMysqlEncoding("cp1251bin",
+					null);
+			INDEX_TO_CHARSET[51] = getJavaEncodingForMysqlEncoding(
+					"cp1251cias", null);
+			INDEX_TO_CHARSET[52] = getJavaEncodingForMysqlEncoding(
+					"cp1251csas", null);
+			INDEX_TO_CHARSET[53] = getJavaEncodingForMysqlEncoding(
+					"macromanbin", null);
+			INDEX_TO_CHARSET[54] = getJavaEncodingForMysqlEncoding(
+					"macromancias", null);
+			INDEX_TO_CHARSET[55] = getJavaEncodingForMysqlEncoding(
+					"macromanciai", null);
+			INDEX_TO_CHARSET[56] = getJavaEncodingForMysqlEncoding(
+					"macromancsas", null);
+			INDEX_TO_CHARSET[57] = getJavaEncodingForMysqlEncoding("cp1256",
+					null);
+			INDEX_TO_CHARSET[63] = getJavaEncodingForMysqlEncoding("binary",
+					null);
+			INDEX_TO_CHARSET[64] = getJavaEncodingForMysqlEncoding("armscii",
+					null);
+			INDEX_TO_CHARSET[65] = getJavaEncodingForMysqlEncoding("ascii",
+					null);
+			INDEX_TO_CHARSET[66] = getJavaEncodingForMysqlEncoding("cp1250",
+					null);
+			INDEX_TO_CHARSET[67] = getJavaEncodingForMysqlEncoding("cp1256",
+					null);
+			INDEX_TO_CHARSET[68] = getJavaEncodingForMysqlEncoding("cp866",
+					null);
+			INDEX_TO_CHARSET[69] = getJavaEncodingForMysqlEncoding("dec8", null);
+			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[74] = getJavaEncodingForMysqlEncoding("koi8r",
+					null);
+			INDEX_TO_CHARSET[75] = getJavaEncodingForMysqlEncoding("koi8ukr",
+					null);
+			INDEX_TO_CHARSET[77] = getJavaEncodingForMysqlEncoding("latin2",
+					null);
+			INDEX_TO_CHARSET[78] = getJavaEncodingForMysqlEncoding("latin5",
+					null);
+			INDEX_TO_CHARSET[79] = getJavaEncodingForMysqlEncoding("latin7",
+					null);
+			INDEX_TO_CHARSET[80] = getJavaEncodingForMysqlEncoding("cp850",
+					null);
+			INDEX_TO_CHARSET[81] = getJavaEncodingForMysqlEncoding("cp852",
+					null);
+			INDEX_TO_CHARSET[82] = getJavaEncodingForMysqlEncoding("swe7", null);
+			INDEX_TO_CHARSET[83] = getJavaEncodingForMysqlEncoding("utf8", null);
+			INDEX_TO_CHARSET[84] = getJavaEncodingForMysqlEncoding("big5", null);
+			INDEX_TO_CHARSET[85] = getJavaEncodingForMysqlEncoding("euckr",
+					null);
+			INDEX_TO_CHARSET[86] = getJavaEncodingForMysqlEncoding("gb2312",
+					null);
+			INDEX_TO_CHARSET[87] = getJavaEncodingForMysqlEncoding("gbk", null);
+			INDEX_TO_CHARSET[88] = getJavaEncodingForMysqlEncoding("sjis", null);
+			INDEX_TO_CHARSET[89] = getJavaEncodingForMysqlEncoding("tis620",
+					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[94] = getJavaEncodingForMysqlEncoding("latin1",
+					null);
+			INDEX_TO_CHARSET[95] = getJavaEncodingForMysqlEncoding("cp932",
+					null);
+			INDEX_TO_CHARSET[96] = getJavaEncodingForMysqlEncoding("cp932",
+					null);
+			INDEX_TO_CHARSET[97] = getJavaEncodingForMysqlEncoding("eucjpms",
+					null);
+			INDEX_TO_CHARSET[98] = getJavaEncodingForMysqlEncoding("eucjpms",
+					null);
+			
+			INDEX_TO_CHARSET[128] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[129] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[130] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[131] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[132] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[133] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[134] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[135] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[136] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[137] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[138] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[139] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[140] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[141] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[142] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[143] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[144] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[145] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+			INDEX_TO_CHARSET[146] = getJavaEncodingForMysqlEncoding("ucs2",
+					null);
+
+			INDEX_TO_CHARSET[192] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[193] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[194] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[195] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[196] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[197] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[198] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[199] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[200] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[201] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[202] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[203] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[204] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[205] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[206] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[207] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[208] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[209] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+			INDEX_TO_CHARSET[210] = getJavaEncodingForMysqlEncoding("utf8",
+					null);
+
+		} catch (SQLException sqlEx) {
+			// ignore, it won't happen in this case
+		}
+		
+		Map tempMap = new HashMap();
+		
+		tempMap.put("czech", "latin2");
+		tempMap.put("danish", "latin1");
+		tempMap.put("dutch", "latin1");
+		tempMap.put("english", "latin1");
+		tempMap.put("estonian", "latin7");
+		tempMap.put("french", "latin1");
+		tempMap.put("german", "latin1");
+		tempMap.put("greek", "greek");
+		tempMap.put("hungarian", "latin2");
+		tempMap.put("italian", "latin1");
+		tempMap.put("japanese", "ujis");
+		tempMap.put("japanese-sjis", "sjis");
+		tempMap.put("korean", "euckr");
+		tempMap.put("norwegian", "latin1");
+		tempMap.put("norwegian-ny", "latin1");
+		tempMap.put("polish", "latin2");
+		tempMap.put("portuguese", "latin1");
+		tempMap.put("romanian", "latin2");
+		tempMap.put("russian", "koi8r");
+		tempMap.put("serbian", "cp1250");
+		tempMap.put("slovak", "latin2");
+		tempMap.put("spanish", "latin1");
+		tempMap.put("swedish", "latin1");
+		tempMap.put("ukrainian", "koi8u");
+		
+		ERROR_MESSAGE_FILE_TO_MYSQL_CHARSET_MAP = 
+			Collections.unmodifiableMap(tempMap);
+	}
+
+	final static String getJavaEncodingForMysqlEncoding(String mysqlEncoding,
+			Connection conn) throws SQLException {
+		
+		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,
+			Connection conn) throws SQLException {
+		List mysqlEncodings = (List) CharsetMapping.JAVA_UC_TO_MYSQL_CHARSET_MAP
+				.get(javaEncodingUC);
+		;
+
+		if (mysqlEncodings != null) {
+			Iterator iter = mysqlEncodings.iterator();
+
+			VersionedStringProperty versionedProp = null;
+
+			while (iter.hasNext()) {
+				VersionedStringProperty propToCheck = (VersionedStringProperty) iter
+						.next();
+
+				if (conn == null) {
+					// Take the first one we get
+
+					return propToCheck.toString();
+				}
+
+				if (versionedProp != null && !versionedProp.preferredValue) {
+					if (versionedProp.majorVersion == propToCheck.majorVersion
+							&& versionedProp.minorVersion == propToCheck.minorVersion
+							&& versionedProp.subminorVersion == propToCheck.subminorVersion) {
+						return versionedProp.toString();
+					}
+				}
+
+				if (propToCheck.isOkayForVersion(conn)) {
+					if (propToCheck.preferredValue) {
+						return propToCheck.toString();
+					}
+
+					versionedProp = propToCheck;
+				} else {
+					break;
+				}
+			}
+
+			if (versionedProp != null) {
+				return versionedProp.toString();
+			}
+		}
+
+		return null;
+	}
+
+	final static int getNumberOfCharsetsConfigured() {
+		return MYSQL_TO_JAVA_CHARSET_MAP.size() / 2; // because we UC every
+														// key
+	}
+
+	/**
+	 * Returns the character encoding for error messages returned from the
+	 * server. Doesn't return useful values other than Cp1252 until the driver
+	 * has gone through initialization phase and determined server configuration,
+	 * as not enough information is available to make an intelligent decision
+	 * until then.
+	 * 
+	 * @param conn the connection to the MySQL server
+	 * @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 {
+		String errorMessageFile = conn.getServerVariable("language");
+		
+		if (errorMessageFile == null || errorMessageFile.length() == 0) {
+			// punt
+			return "Cp1252";
+		}
+		
+		int endWithoutSlash = errorMessageFile.length();
+		
+		if (errorMessageFile.endsWith("/") || errorMessageFile.endsWith("\\")) {
+			endWithoutSlash--;
+		}
+			
+		int lastSlashIndex = errorMessageFile.lastIndexOf('/', endWithoutSlash - 1);
+		
+		if (lastSlashIndex == -1) {
+			lastSlashIndex = errorMessageFile.lastIndexOf('\\', endWithoutSlash - 1);
+		}
+		
+		if (lastSlashIndex == -1) {
+			lastSlashIndex = 0;
+		}
+		
+		if (lastSlashIndex == endWithoutSlash || endWithoutSlash < lastSlashIndex) {
+			// punt
+			return "Cp1252";
+		}
+		
+		errorMessageFile = errorMessageFile.substring(lastSlashIndex + 1, endWithoutSlash);
+		
+		String errorMessageEncodingMysql = (String)ERROR_MESSAGE_FILE_TO_MYSQL_CHARSET_MAP.get(errorMessageFile);
+		
+		if (errorMessageEncodingMysql == null) {
+			// punt
+			return "Cp1252";
+		}
+		
+		String javaEncoding = getJavaEncodingForMysqlEncoding(errorMessageEncodingMysql, conn);
+		
+		if (javaEncoding == null) {
+			// punt
+			return "Cp1252";
+		}
+		
+		return javaEncoding;
+	}
+	
+	final static boolean isAliasForSjis(String encoding) {
+		return ("SJIS".equalsIgnoreCase(encoding)
+				|| "WINDOWS-31J".equalsIgnoreCase(encoding)
+				|| "MS932".equalsIgnoreCase(encoding)
+				|| "SHIFT_JIS".equalsIgnoreCase(encoding) || "CP943"
+				.equalsIgnoreCase(encoding));
+
+	}
+
+	final static boolean isMultibyteCharset(String javaEncodingName) {
+		String javaEncodingNameUC = javaEncodingName
+				.toUpperCase(Locale.ENGLISH);
+
+		return MULTIBYTE_CHARSETS.containsKey(javaEncodingNameUC);
+	}
+
+	private static void populateMapWithKeyValuePairs(String configKey,
+			Map mapToPopulate, boolean addVersionedProperties,
+			boolean addUppercaseKeys) {
+		String javaToMysqlConfig = CHARSET_CONFIG.getProperty(configKey);
+
+		if (javaToMysqlConfig != null) {
+			List mappings = StringUtils.split(javaToMysqlConfig, ",", true);
+
+			if (mappings != null) {
+				Iterator mappingsIter = mappings.iterator();
+
+				while (mappingsIter.hasNext()) {
+					String aMapping = (String) mappingsIter.next();
+
+					List parsedPair = StringUtils.split(aMapping, "=", true);
+
+					if (parsedPair.size() == 2) {
+						String key = parsedPair.get(0).toString();
+						String value = parsedPair.get(1).toString();
+
+						if (addVersionedProperties) {
+							List versionedProperties = (List) mapToPopulate
+									.get(key);
+
+							if (versionedProperties == null) {
+								versionedProperties = new ArrayList();
+								mapToPopulate.put(key, versionedProperties);
+							}
+
+							VersionedStringProperty verProp = new VersionedStringProperty(
+									value);
+							versionedProperties.add(verProp);
+
+							if (addUppercaseKeys) {
+								String keyUc = key.toUpperCase(Locale.ENGLISH);
+
+								versionedProperties = (List) mapToPopulate
+										.get(keyUc);
+
+								if (versionedProperties == null) {
+									versionedProperties = new ArrayList();
+									mapToPopulate.put(keyUc,
+											versionedProperties);
+								}
+
+								versionedProperties.add(verProp);
+							}
+						} else {
+							mapToPopulate.put(key, value);
+
+							if (addUppercaseKeys) {
+								mapToPopulate.put(key
+										.toUpperCase(Locale.ENGLISH), value);
+							}
+						}
+					} else {
+						throw new RuntimeException(
+								"Syntax error in Charsets.properties "
+										+ "resource for token \"" + aMapping
+										+ "\".");
+					}
+				}
+			} else {
+				throw new RuntimeException("Missing/corrupt entry for \""
+						+ configKey + "\" in Charsets.properties.");
+			}
+		} else {
+			throw new RuntimeException("Could not find configuration value "
+					+ "\"" + configKey + "\" in Charsets.properties resource");
+		}
+	}
+}
+
+class VersionedStringProperty {
+	int majorVersion, minorVersion, subminorVersion;
+
+	boolean preferredValue = false;
+
+	String propertyInfo;
+
+	VersionedStringProperty(String property) {
+		property = property.trim();
+
+		if (property.startsWith("*")) {
+			property = property.substring(1);
+			preferredValue = true;
+		}
+
+		if (property.startsWith(">")) {
+			property = property.substring(1);
+
+			int charPos = 0;
+
+			for (charPos = 0; charPos < property.length(); charPos++) {
+				char c = property.charAt(charPos);
+
+				if (!Character.isWhitespace(c) && !Character.isDigit(c)
+						&& c != '.') {
+					break;
+				}
+			}
+
+			String versionInfo = property.substring(0, charPos);
+			List versionParts = StringUtils.split(versionInfo, ".", true);
+
+			majorVersion = Integer.parseInt(versionParts.get(0).toString());
+
+			if (versionParts.size() > 1) {
+				minorVersion = Integer.parseInt(versionParts.get(1).toString());
+			} else {
+				minorVersion = 0;
+			}
+
+			if (versionParts.size() > 2) {
+				subminorVersion = Integer.parseInt(versionParts.get(2)
+						.toString());
+			} else {
+				subminorVersion = 0;
+			}
+
+			propertyInfo = property.substring(charPos);
+		} else {
+			majorVersion = minorVersion = subminorVersion = 0;
+			propertyInfo = property;
+		}
+	}
+
+	VersionedStringProperty(String property, int major, int minor, int subminor) {
+		propertyInfo = property;
+		majorVersion = major;
+		minorVersion = minor;
+		subminorVersion = subminor;
+	}
+
+	boolean isOkayForVersion(Connection conn) throws SQLException {
+		return conn.versionMeetsMinimum(majorVersion, minorVersion,
+				subminorVersion);
+	}
+
+	public String toString() {
+		return propertyInfo;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Charsets.properties
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Charsets.properties	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Charsets.properties	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,102 @@
+#
+# Charset Mappings 
+#
+#      Java Encoding		MySQL Name (and version, '*' 
+#                           denotes preferred value)      
+#
+
+javaToMysqlMappings=\
+		US-ASCII =			usa7,\
+ 		US-ASCII =			ascii,\
+ 		Big5 = 				big5,\
+ 		GBK = 				gbk,\
+ 		SJIS = 				sjis,\
+ 		EUC_CN = 			gb2312,\
+ 		EUC_JP = 			ujis,\
+ 		EUC_JP_Solaris = 	>5.0.3 eucjpms,\
+ 		EUC_KR = 			euc_kr,\
+ 		EUC_KR = 			>4.1.0 euckr,\
+ 		ISO8859_1 =			*latin1,\
+ 		ISO8859_1 =			latin1_de,\
+ 		ISO8859_1 =			german1,\
+ 		ISO8859_1 =			danish,\
+ 		ISO8859_2 =			latin2,\
+		ISO8859_2 =			czech,\
+		ISO8859_2 =			hungarian,\
+		ISO8859_2  =		croat,\
+		ISO8859_7  =		greek,\
+		ISO8859_7  =		latin7,\
+		ISO8859_8  = 		hebrew,\
+		ISO8859_9  =		latin5,\
+ 		ISO8859_13 =		latvian,\
+		ISO8859_13 =		latvian1,\
+		ISO8859_13 =		estonia,\
+		Cp437 =             *>4.1.0 cp850,\
+ 		Cp437 =				dos,\
+ 		Cp850 =				Cp850,\
+		Cp852 = 			Cp852,\
+ 		Cp866 = 			cp866,\
+ 		KOI8_R = 			koi8_ru,\
+		KOI8_R = 			>4.1.0 koi8r,\
+ 		TIS620 = 			tis620,\
+		Cp1250 = 			cp1250,\
+		Cp1250 = 			win1250,\
+		Cp1251 = 			*>4.1.0 cp1251,\
+		Cp1251 = 			win1251,\
+ 		Cp1251 = 			cp1251cias,\
+		Cp1251 = 			cp1251csas,\
+		Cp1256 = 			cp1256,\
+ 		Cp1251 = 			win1251ukr,\
+		Cp1257 = 			cp1257,\
+		MacRoman = 			macroman,\
+		MacCentralEurope = 	macce,\
+		UTF-8 = 		utf8,\
+		UnicodeBig = 	ucs2,\
+		US-ASCII =		binary,\
+		Cp943 =        	sjis,\
+		MS932 =			sjis,\
+		MS932 =        	>4.1.11 cp932,\
+		WINDOWS-31J =	sjis,\
+		WINDOWS-31J = 	>4.1.11 cp932,\
+		CP932 =			sjis,\
+		CP932 =			*>4.1.11 cp932,\
+		SHIFT_JIS = 	sjis,\
+		ASCII =			ascii,\
+        LATIN5 =		latin5,\
+        LATIN7 =		latin7,\
+        HEBREW =		hebrew,\
+        GREEK =			greek,\
+        EUCKR =			euckr,\
+        GB2312 =		gb2312,\
+        LATIN2 =		latin2
+ 
+#       
+# List of multibyte character sets that can not
+# use efficient charset conversion or escaping
+#
+# This map is made case-insensitive inside CharsetMapping
+#
+#   Java Name			MySQL Name (not currently used)
+
+multibyteCharsets=\
+        Big5 = 			big5,\
+ 		GBK = 			gbk,\
+ 		SJIS = 			sjis,\
+ 		EUC_CN = 		gb2312,\
+ 		EUC_JP = 		ujis,\
+ 		EUC_JP_Solaris = eucjpms,\
+ 		EUC_KR = 		euc_kr,\
+ 		EUC_KR = 		>4.1.0 euckr,\
+ 		Cp943 =        	sjis,\
+		Cp943 = 		cp943,\
+		WINDOWS-31J =	sjis,\
+		WINDOWS-31J = 	cp932,\
+		CP932 =			cp932,\
+		MS932 =			sjis,\
+		MS932 =        	cp932,\
+		SHIFT_JIS = 	sjis,\
+		EUCKR =			euckr,\
+        GB2312 =		gb2312,\
+		UTF-8 = 		utf8,\
+		utf8 =          utf8,\
+		UnicodeBig = 	ucs2
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Clob.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Clob.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Clob.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,290 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.Writer;
+
+import java.sql.SQLException;
+
+/**
+ * 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 $
+ */
+public class Clob implements java.sql.Clob, OutputStreamWatcher, WriterWatcher {
+	private String charData;
+
+	Clob(String charDataInit) {
+		this.charData = charDataInit;
+	}
+
+	/**
+	 * @see java.sql.Clob#getAsciiStream()
+	 */
+	public InputStream getAsciiStream() throws SQLException {
+		if (this.charData != null) {
+			return new ByteArrayInputStream(this.charData.getBytes());
+		}
+
+		return null;
+	}
+
+	/**
+	 * @see java.sql.Clob#getCharacterStream()
+	 */
+	public Reader getCharacterStream() throws SQLException {
+		if (this.charData != null) {
+			return new StringReader(this.charData);
+		}
+
+		return null;
+	}
+
+	/**
+	 * @see java.sql.Clob#getSubString(long, int)
+	 */
+	public String getSubString(long startPos, int length) throws SQLException {
+		if (startPos < 1) {
+			throw SQLError.createSQLException(Messages.getString("Clob.6"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		int adjustedStartPos = (int)startPos - 1;
+		int adjustedEndIndex = adjustedStartPos + length;
+		
+		if (this.charData != null) {
+			if (adjustedEndIndex > this.charData.length()) {
+				throw SQLError.createSQLException(Messages.getString("Clob.7"), //$NON-NLS-1$
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			return this.charData.substring(adjustedStartPos, 
+					adjustedEndIndex);
+		}
+
+		return null;
+	}
+
+	/**
+	 * @see java.sql.Clob#length()
+	 */
+	public long length() throws SQLException {
+		if (this.charData != null) {
+			return this.charData.length();
+		}
+
+		return 0;
+	}
+
+	/**
+	 * @see java.sql.Clob#position(Clob, long)
+	 */
+	public long position(java.sql.Clob arg0, long arg1) throws SQLException {
+		return position(arg0.getSubString(0L, (int) arg0.length()), arg1);
+	}
+
+	/**
+	 * @see java.sql.Clob#position(String, long)
+	 */
+	public long position(String stringToFind, long startPos)
+			throws SQLException {
+		if (startPos < 1) {
+			throw SQLError.createSQLException(
+					Messages.getString("Clob.8") //$NON-NLS-1$
+							+ startPos + Messages.getString("Clob.9"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		if (this.charData != null) {
+			if ((startPos - 1) > this.charData.length()) {
+				throw SQLError.createSQLException(Messages.getString("Clob.10"), //$NON-NLS-1$
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			int pos = this.charData.indexOf(stringToFind, (int) (startPos - 1));
+
+			return (pos == -1) ? (-1) : (pos + 1);
+		}
+
+		return -1;
+	}
+
+	/**
+	 * @see java.sql.Clob#setAsciiStream(long)
+	 */
+	public OutputStream setAsciiStream(long indexToWriteAt) throws SQLException {
+		if (indexToWriteAt < 1) {
+			throw SQLError.createSQLException(Messages.getString("Clob.0"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		WatchableOutputStream bytesOut = new WatchableOutputStream();
+		bytesOut.setWatcher(this);
+
+		if (indexToWriteAt > 0) {
+			bytesOut.write(this.charData.getBytes(), 0,
+					(int) (indexToWriteAt - 1));
+		}
+
+		return bytesOut;
+	}
+
+	/**
+	 * @see java.sql.Clob#setCharacterStream(long)
+	 */
+	public Writer setCharacterStream(long indexToWriteAt) throws SQLException {
+		if (indexToWriteAt < 1) {
+			throw SQLError.createSQLException(Messages.getString("Clob.1"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		WatchableWriter writer = new WatchableWriter();
+		writer.setWatcher(this);
+
+		//
+		// Don't call write() if nothing to write...
+		//
+		if (indexToWriteAt > 1) {
+			writer.write(this.charData, 0, (int) (indexToWriteAt - 1));
+		}
+
+		return writer;
+	}
+
+	/**
+	 * @see java.sql.Clob#setString(long, String)
+	 */
+	public int setString(long pos, String str) throws SQLException {
+		if (pos < 1) {
+			throw SQLError.createSQLException(Messages.getString("Clob.2"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if (str == null) {
+			throw SQLError.createSQLException(Messages.getString("Clob.3"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		StringBuffer charBuf = new StringBuffer(this.charData);
+
+		pos--;
+
+		int strLength = str.length();
+
+		charBuf.replace((int) pos, (int) (pos + strLength), str);
+
+		this.charData = charBuf.toString();
+
+		return strLength;
+	}
+
+	/**
+	 * @see java.sql.Clob#setString(long, String, int, int)
+	 */
+	public int setString(long pos, String str, int offset, int len)
+			throws SQLException {
+		if (pos < 1) {
+			throw SQLError.createSQLException(Messages.getString("Clob.4"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if (str == null) {
+			throw SQLError.createSQLException(Messages.getString("Clob.5"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		StringBuffer charBuf = new StringBuffer(this.charData);
+
+		pos--;
+
+		String replaceString = str.substring(offset, len);
+
+		charBuf.replace((int) pos, (int) (pos + replaceString.length()),
+				replaceString);
+
+		this.charData = charBuf.toString();
+
+		return len;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.OutputStreamWatcher#streamClosed(byte[])
+	 */
+	public void streamClosed(WatchableOutputStream out) {
+		int streamSize = out.size();
+
+		if (streamSize < this.charData.length()) {
+			try {
+				out.write(StringUtils
+						.getBytes(this.charData, null, null, false, null),
+						streamSize, this.charData.length() - streamSize);
+			} catch (SQLException ex) {
+				//
+			}
+		}
+
+		this.charData = StringUtils.toAsciiString(out.toByteArray());
+	}
+
+	/**
+	 * @see java.sql.Clob#truncate(long)
+	 */
+	public void truncate(long length) throws SQLException {
+		if (length > this.charData.length()) {
+			throw SQLError.createSQLException(
+					Messages.getString("Clob.11") //$NON-NLS-1$
+							+ this.charData.length()
+							+ Messages.getString("Clob.12") + length + Messages.getString("Clob.13")); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+
+		this.charData = this.charData.substring(0, (int) length);
+	}
+
+	/**
+	 * @see com.mysql.jdbc.WriterWatcher#writerClosed(char[])
+	 */
+	public void writerClosed(char[] charDataBeingWritten) {
+		this.charData = new String(charDataBeingWritten);
+	}
+
+	/**
+	 * @see com.mysql.jdbc.WriterWatcher#writerClosed(char[])
+	 */
+	public void writerClosed(WatchableWriter out) {
+		int dataLength = out.size();
+
+		if (dataLength < this.charData.length()) {
+			out.write(this.charData, dataLength, this.charData.length()
+					- dataLength);
+		}
+
+		this.charData = out.toString();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CommunicationsException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CommunicationsException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CommunicationsException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,213 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.net.BindException;
+
+import java.sql.SQLException;
+
+/**
+ * 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 SQLException {
+
+	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,
+			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();
+	}
+
+	/*
+	 * (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;
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CompressedInputStream.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CompressedInputStream.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CompressedInputStream.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,325 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import java.sql.SQLException;
+
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+
+/**
+ * Used to de-compress packets from the MySQL server when protocol-level
+ * compression is turned on.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: CompressedInputStream.java,v 1.1.2.1 2005/05/13 18:58:37
+ *          mmatthews Exp $
+ */
+class CompressedInputStream extends InputStream {
+	/** The packet data after it has been un-compressed */
+	private byte[] buffer;
+
+	/** The connection that is using us (used to read config values) */
+	private Connection connection;
+
+	/** The stream we are reading from the server */
+	private InputStream in;
+
+	/** The ZIP inflater used to un-compress packets */
+	private Inflater inflater;
+
+	/**
+	 * The buffer to read packet headers into
+	 */
+	private byte[] packetHeaderBuffer = new byte[7];
+
+	/** The position we are reading from */
+	private int pos = 0;
+
+	/**
+	 * Creates a new CompressedInputStream that reads the given stream from the
+	 * server.
+	 * 
+	 * @param conn
+	 *            DOCUMENT ME!
+	 * @param streamFromServer
+	 */
+	public CompressedInputStream(Connection conn, InputStream streamFromServer) {
+		this.connection = conn;
+		this.in = streamFromServer;
+		this.inflater = new Inflater();
+	}
+
+	/**
+	 * @see java.io.InputStream#available()
+	 */
+	public int available() throws IOException {
+		if (this.buffer == null) {
+			return this.in.available();
+		}
+
+		return this.buffer.length - this.pos + this.in.available();
+	}
+
+	/**
+	 * @see java.io.InputStream#close()
+	 */
+	public void close() throws IOException {
+		this.in.close();
+		this.buffer = null;
+		this.inflater = null;
+	}
+
+	/**
+	 * Retrieves and un-compressed (if necessary) the next packet from the
+	 * server.
+	 * 
+	 * @throws IOException
+	 *             if an I/O error occurs
+	 */
+	private void getNextPacketFromServer() throws IOException {
+		byte[] uncompressedData = null;
+
+		int lengthRead = readFully(this.packetHeaderBuffer, 0, 7);
+
+		if (lengthRead < 7) {
+			throw new IOException("Unexpected end of input stream");
+		}
+
+		int compressedPacketLength = ((this.packetHeaderBuffer[0] & 0xff))
+				+ (((this.packetHeaderBuffer[1] & 0xff)) << 8)
+				+ (((this.packetHeaderBuffer[2] & 0xff)) << 16);
+
+		int uncompressedLength = ((this.packetHeaderBuffer[4] & 0xff))
+				+ (((this.packetHeaderBuffer[5] & 0xff)) << 8)
+				+ (((this.packetHeaderBuffer[6] & 0xff)) << 16);
+
+		if (this.connection.getTraceProtocol()) {
+			try {
+				this.connection.getLog().logTrace(
+						"Reading compressed packet of length "
+								+ compressedPacketLength + " uncompressed to "
+								+ uncompressedLength);
+			} catch (SQLException sqlEx) {
+				throw new IOException(sqlEx.toString()); // should never
+															// happen
+			}
+		}
+
+		if (uncompressedLength > 0) {
+			uncompressedData = new byte[uncompressedLength];
+
+			byte[] compressedBuffer = new byte[compressedPacketLength];
+
+			readFully(compressedBuffer, 0, compressedPacketLength);
+
+			try {
+				this.inflater.reset();
+			} catch (NullPointerException npe) {
+				this.inflater = new Inflater();
+			}
+
+			this.inflater.setInput(compressedBuffer);
+
+			try {
+				this.inflater.inflate(uncompressedData);
+			} catch (DataFormatException dfe) {
+				throw new IOException(
+						"Error while uncompressing packet from server.");
+			}
+
+			this.inflater.end();
+		} else {
+			if (this.connection.getTraceProtocol()) {
+				try {
+					this.connection
+							.getLog()
+							.logTrace(
+									"Packet didn't meet compression threshold, not uncompressing...");
+				} catch (SQLException sqlEx) {
+					throw new IOException(sqlEx.toString()); // should never
+																// happen
+				}
+			}
+
+			//	
+			// Read data, note this this code is reached when using
+			// compressed packets that have not been compressed, as well
+			//
+			uncompressedData = new byte[compressedPacketLength];
+			readFully(uncompressedData, 0, compressedPacketLength);
+		}
+
+		if (this.connection.getTraceProtocol()) {
+			try {
+				this.connection.getLog().logTrace(
+						"Uncompressed packet: \n"
+								+ StringUtils.dumpAsHex(uncompressedData,
+										compressedPacketLength));
+			} catch (SQLException sqlEx) {
+				throw new IOException(sqlEx.toString()); // should never
+															// happen
+			}
+		}
+
+		if ((this.buffer != null) && (this.pos < this.buffer.length)) {
+			if (this.connection.getTraceProtocol()) {
+				try {
+					this.connection.getLog().logTrace(
+							"Combining remaining packet with new: ");
+				} catch (SQLException sqlEx) {
+					throw new IOException(sqlEx.toString()); // should never
+																// happen
+				}
+			}
+
+			int remaining = this.buffer.length - this.pos;
+			byte[] newBuffer = new byte[remaining + uncompressedData.length];
+
+			int newIndex = 0;
+
+			for (int i = this.pos; i < this.buffer.length; i++)
+				newBuffer[newIndex++] = this.buffer[i];
+
+			System.arraycopy(uncompressedData, 0, newBuffer, newIndex,
+					uncompressedData.length);
+
+			uncompressedData = newBuffer;
+		}
+
+		this.pos = 0;
+		this.buffer = uncompressedData;
+
+		return;
+	}
+
+	/**
+	 * Determines if another packet needs to be read from the server to be able
+	 * to read numBytes from the stream.
+	 * 
+	 * @param numBytes
+	 *            the number of bytes to be read
+	 * 
+	 * @throws IOException
+	 *             if an I/O error occors.
+	 */
+	private void getNextPacketIfRequired(int numBytes) throws IOException {
+		if ((this.buffer == null)
+				|| ((this.pos + numBytes) > this.buffer.length)) {
+			getNextPacketFromServer();
+		}
+	}
+
+	/**
+	 * @see java.io.InputStream#read()
+	 */
+	public int read() throws IOException {
+		try {
+			getNextPacketIfRequired(1);
+		} catch (IOException ioEx) {
+			return -1;
+		}
+
+		return this.buffer[this.pos++] & 0xff;
+	}
+
+	/**
+	 * @see java.io.InputStream#read(byte)
+	 */
+	public int read(byte[] b) throws IOException {
+		return read(b, 0, b.length);
+	}
+
+	/**
+	 * @see java.io.InputStream#read(byte, int, int)
+	 */
+	public int read(byte[] b, int off, int len) throws IOException {
+		if (b == null) {
+			throw new NullPointerException();
+		} else if ((off < 0) || (off > b.length) || (len < 0)
+				|| ((off + len) > b.length) || ((off + len) < 0)) {
+			throw new IndexOutOfBoundsException();
+		}
+
+		if (len <= 0) {
+			return 0;
+		}
+
+		try {
+			getNextPacketIfRequired(len);
+		} catch (IOException ioEx) {
+			return -1;
+		}
+
+		System.arraycopy(this.buffer, this.pos, b, off, len);
+		this.pos += len;
+
+		return len;
+	}
+
+	private final int readFully(byte[] b, int off, int len) throws IOException {
+		if (len < 0) {
+			throw new IndexOutOfBoundsException();
+		}
+
+		int n = 0;
+
+		while (n < len) {
+			int count = this.in.read(b, off + n, len - n);
+
+			if (count < 0) {
+				throw new EOFException();
+			}
+
+			n += count;
+		}
+
+		return n;
+	}
+
+	/**
+	 * @see java.io.InputStream#skip(long)
+	 */
+	public long skip(long n) throws IOException {
+		long count = 0;
+
+		for (long i = 0; i < n; i++) {
+			int bytesRead = read();
+
+			if (bytesRead == -1) {
+				break;
+			}
+
+			count++;
+		}
+
+		return count;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Connection.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Connection.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Connection.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,5736 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.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;
+
+/**
+ * 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: Connection.java 5907 2006-10-19 15:40:51Z mmatthews $
+ * @see java.sql.Connection
+ */
+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;
+
+		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.
+	 * 
+	 * @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();
+		}
+		
+		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.
+	 * 
+	 * @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();
+			}
+		}
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * 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 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);
+	}
+	
+	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.
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void close() throws SQLException {
+		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++) {
+				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>
+	 * 
+	 * @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;
+		}
+
+		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)) {
+			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");
+	}
+
+	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;
+
+				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
+	 */
+	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(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;
+	}
+
+	/**
+	 * 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 = 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;
+	}
+
+	/**
+	 * Has the maxRows value changed?
+	 * 
+	 * @param stmt
+	 *            DOCUMENT ME!
+	 */
+	void maxRowsChanged(Statement 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 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);
+	}
+
+	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.
+	 * 
+	 * @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 {
+				// 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(Savepoint savepoint) 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!
+	 * 
+	 * @param sql
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	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);
+	}
+
+	/**
+	 * 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(boolean autoCommitFlag) throws SQLException {
+		synchronized (getMutex()) {
+			checkClosed();
+	
+			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>
+	 * 
+	 * @param catalog
+	 *            the database for this connection to use
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public void setCatalog(String catalog) throws SQLException {
+		synchronized (getMutex()) {
+			checkClosed();
+	
+			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
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void setFailedOverState() throws SQLException {
+		if (getFailOverReadOnly()) {
+			setReadOnly(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();
+		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();
+				}
+			}
+		}
+
+	}
+
+	/**
+     * 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);
+		}
+	}
+	
+	/**
+	 * 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(Statement 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(Statement 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, true, 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);
+	}
+
+	protected String getErrorMessageEncoding() {
+		return errorMessageEncoding;
+	}
+	
+	/*
+	 * For testing failover scenarios
+	 */
+	private boolean hasTriedMasterFlag = false;
+	
+	public void clearHasTriedMaster() {
+		this.hasTriedMasterFlag = false;
+	}
+	
+	public boolean hasTriedMaster() {
+		return this.hasTriedMasterFlag;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,67 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+/**
+ * Thrown when a client requests a connection-level feature that isn't available
+ * for this particular distribution of Connector/J (currently only used by code
+ * that is export-controlled).
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: ConnectionFeatureNotAvailableException.java,v 1.1.2.1
+ *          2005/05/13 18:58:38 mmatthews Exp $
+ */
+public class ConnectionFeatureNotAvailableException extends
+		CommunicationsException {
+
+	/**
+	 * @param conn
+	 * @param lastPacketSentTimeMs
+	 * @param underlyingException
+	 */
+	public ConnectionFeatureNotAvailableException(Connection conn,
+			long lastPacketSentTimeMs, Exception underlyingException) {
+		super(conn, lastPacketSentTimeMs, underlyingException);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Throwable#getMessage()
+	 */
+	public String getMessage() {
+		return "Feature not available in this distribution of Connector/J";
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.SQLException#getSQLState()
+	 */
+	public String getSQLState() {
+		return SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionProperties.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionProperties.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionProperties.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,3720 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.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;
+
+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>");
+
+		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();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getAllowMultiQueries() {
+		return this.allowMultiQueries.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the allowNanAndInf.
+	 */
+	public boolean getAllowNanAndInf() {
+		return allowNanAndInf.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the allowUrlInLocalInfile.
+	 */
+	public boolean getAllowUrlInLocalInfile() {
+		return this.allowUrlInLocalInfile.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the alwaysSendSetIsolation.
+	 */
+	public boolean getAlwaysSendSetIsolation() {
+		return this.alwaysSendSetIsolation.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the autoDeserialize.
+	 */
+	public boolean getAutoDeserialize() {
+		return autoDeserialize.getValueAsBoolean();
+	}
+
+	public boolean getAutoGenerateTestcaseScript() {
+		return this.autoGenerateTestcaseScriptAsBoolean;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getAutoReconnectForPools() {
+		return this.autoReconnectForPoolsAsBoolean;
+	}
+
+	/**
+	 * @return Returns the blobSendChunkSize.
+	 */
+	public int getBlobSendChunkSize() {
+		return blobSendChunkSize.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns if cacheCallableStatements is enabled
+	 */
+	public boolean getCacheCallableStatements() {
+		return this.cacheCallableStatements.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the cachePreparedStatements.
+	 */
+	public boolean getCachePreparedStatements() {
+		return ((Boolean) this.cachePreparedStatements.getValueAsObject())
+				.booleanValue();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean getCacheResultSetMetadata() {
+		return this.cacheResultSetMetaDataAsBoolean;
+	}
+
+	/**
+	 * @return Returns the cacheServerConfiguration.
+	 */
+	public boolean getCacheServerConfiguration() {
+		return cacheServerConfiguration.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the callableStatementCacheSize.
+	 */
+	public int getCallableStatementCacheSize() {
+		return this.callableStatementCacheSize.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getCapitalizeTypeNames() {
+		return this.capitalizeTypeNames.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the characterSetResults.
+	 */
+	public String getCharacterSetResults() {
+		return this.characterSetResults.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the clobberStreamingResults.
+	 */
+	public boolean getClobberStreamingResults() {
+		return this.clobberStreamingResults.getValueAsBoolean();
+	}
+
+	public String getClobCharacterEncoding() {
+		return this.clobCharacterEncoding.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the connectionCollation.
+	 */
+	public String getConnectionCollation() {
+		return this.connectionCollation.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public int getConnectTimeout() {
+		return this.connectTimeout.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getContinueBatchOnError() {
+		return this.continueBatchOnError.getValueAsBoolean();
+	}
+
+	public boolean getCreateDatabaseIfNotExist() {
+		return this.createDatabaseIfNotExist.getValueAsBoolean();
+	}
+
+	public int getDefaultFetchSize() {
+		return this.defaultFetchSize.getValueAsInt();
+	}
+
+	/**
+	 * @return Returns the dontTrackOpenResources.
+	 */
+	public boolean getDontTrackOpenResources() {
+		return this.dontTrackOpenResources.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the dumpQueriesOnException.
+	 */
+	public boolean getDumpQueriesOnException() {
+		return this.dumpQueriesOnException.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the dynamicCalendars.
+	 */
+	public boolean getDynamicCalendars() {
+		return this.dynamicCalendars.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the elideSetAutoCommits.
+	 */
+	public boolean getElideSetAutoCommits() {
+		return this.elideSetAutoCommits.getValueAsBoolean();
+	}
+
+	public boolean getEmptyStringsConvertToZero() {
+		return this.emptyStringsConvertToZero.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getEmulateLocators() {
+		return this.emulateLocators.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the emulateUnsupportedPstmts.
+	 */
+	public boolean getEmulateUnsupportedPstmts() {
+		return this.emulateUnsupportedPstmts.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the enablePacketDebug.
+	 */
+	public boolean getEnablePacketDebug() {
+		return this.enablePacketDebug.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public String getEncoding() {
+		return this.characterEncodingAsString;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the explainSlowQueries.
+	 */
+	public boolean getExplainSlowQueries() {
+		return this.explainSlowQueries.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the failOverReadOnly.
+	 */
+	public boolean getFailOverReadOnly() {
+		return this.failOverReadOnly.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the gatherPerformanceMetrics.
+	 */
+	public boolean getGatherPerformanceMetrics() {
+		return this.gatherPerformanceMetrics.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	protected boolean getHighAvailability() {
+		return this.highAvailabilityAsBoolean;
+	}
+
+	/**
+	 * @return Returns the holdResultsOpenOverStatementClose.
+	 */
+	public boolean getHoldResultsOpenOverStatementClose() {
+		return holdResultsOpenOverStatementClose.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getIgnoreNonTxTables() {
+		return this.ignoreNonTxTables.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public int getInitialTimeout() {
+		return this.initialTimeout.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getInteractiveClient() {
+		return this.isInteractiveClient.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the isInteractiveClient.
+	 */
+	public boolean getIsInteractiveClient() {
+		return this.isInteractiveClient.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the jdbcCompliantTruncation.
+	 */
+	public boolean getJdbcCompliantTruncation() {
+		return this.jdbcCompliantTruncation.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the dontTrackOpenResources.
+	 */
+	public int getLocatorFetchBufferSize() {
+		return this.locatorFetchBufferSize.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public String getLogger() {
+		return this.loggerClassName.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the loggerClassName.
+	 */
+	public String getLoggerClassName() {
+		return this.loggerClassName.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the logSlowQueries.
+	 */
+	public boolean getLogSlowQueries() {
+		return this.logSlowQueries.getValueAsBoolean();
+	}
+
+	public boolean getMaintainTimeStats() {
+		return maintainTimeStatsAsBoolean;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the maxQuerySizeToLog.
+	 */
+	public int getMaxQuerySizeToLog() {
+		return this.maxQuerySizeToLog.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public int getMaxReconnects() {
+		return this.maxReconnects.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public int getMaxRows() {
+		return this.maxRowsAsInt;
+	}
+
+	/**
+	 * Returns the number of queries that metadata can be cached if caching is
+	 * enabled.
+	 * 
+	 * @return the number of queries to cache metadata for.
+	 */
+	public int getMetadataCacheSize() {
+		return this.metadataCacheSize.getValueAsInt();
+	}
+
+	/**
+	 * @return Returns the noDatetimeStringSync.
+	 */
+	public boolean getNoDatetimeStringSync() {
+		return this.noDatetimeStringSync.getValueAsBoolean();
+	}
+
+	public boolean getNullCatalogMeansCurrent() {
+		return this.nullCatalogMeansCurrent.getValueAsBoolean();
+	}
+
+	public boolean getNullNamePatternMatchesAll() {
+		return this.nullNamePatternMatchesAll.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the packetDebugBufferSize.
+	 */
+	public int getPacketDebugBufferSize() {
+		return this.packetDebugBufferSize.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getParanoid() {
+		return this.paranoid.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getPedantic() {
+		return this.pedantic.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the preparedStatementCacheSize.
+	 */
+	public int getPreparedStatementCacheSize() {
+		return ((Integer) this.preparedStatementCacheSize.getValueAsObject())
+				.intValue();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the preparedStatementCacheSqlLimit.
+	 */
+	public int getPreparedStatementCacheSqlLimit() {
+		return ((Integer) this.preparedStatementCacheSqlLimit
+				.getValueAsObject()).intValue();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getProfileSql() {
+		return this.profileSQLAsBoolean;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the profileSQL flag
+	 */
+	public boolean getProfileSQL() {
+		return this.profileSQL.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the propertiesTransform.
+	 */
+	public String getPropertiesTransform() {
+		return this.propertiesTransform.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public int getQueriesBeforeRetryMaster() {
+		return this.queriesBeforeRetryMaster.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getReconnectAtTxEnd() {
+		return this.reconnectTxAtEndAsBoolean;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getRelaxAutoCommit() {
+		return this.relaxAutoCommit.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the reportMetricsIntervalMillis.
+	 */
+	public int getReportMetricsIntervalMillis() {
+		return this.reportMetricsIntervalMillis.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getRequireSSL() {
+		return this.requireSSL.getValueAsBoolean();
+	}
+
+	protected boolean getRetainStatementAfterResultSetClose() {
+		return this.retainStatementAfterResultSetClose.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the rollbackOnPooledClose.
+	 */
+	public boolean getRollbackOnPooledClose() {
+		return this.rollbackOnPooledClose.getValueAsBoolean();
+	}
+
+	/**
+	 * 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();
+	}
+
+	/**
+	 * @return Returns the runningCTS13.
+	 */
+	public boolean getRunningCTS13() {
+		return this.runningCTS13.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public int getSecondsBeforeRetryMaster() {
+		return this.secondsBeforeRetryMaster.getValueAsInt();
+	}
+
+	/**
+	 * Returns the 'serverTimezone' property.
+	 * 
+	 * @return the configured server timezone property.
+	 */
+	public String getServerTimezone() {
+		return this.serverTimezone.getValueAsString();
+	}
+
+	/**
+	 * @return Returns the sessionVariables.
+	 */
+	public String getSessionVariables() {
+		return sessionVariables.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the slowQueryThresholdMillis.
+	 */
+	public int getSlowQueryThresholdMillis() {
+		return this.slowQueryThresholdMillis.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public String getSocketFactoryClassName() {
+		return this.socketFactoryClassName.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public int getSocketTimeout() {
+		return this.socketTimeout.getValueAsInt();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getStrictFloatingPoint() {
+		return this.strictFloatingPoint.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getStrictUpdates() {
+		return this.strictUpdates.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the tinyInt1isBit.
+	 */
+	public boolean getTinyInt1isBit() {
+		return this.tinyInt1isBit.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the logProtocol.
+	 */
+	public boolean getTraceProtocol() {
+		return this.traceProtocol.getValueAsBoolean();
+	}
+
+	public boolean getTransformedBitIsBoolean() {
+		return this.transformedBitIsBoolean.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getUseCompression() {
+		return this.useCompression.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the useFastIntParsing.
+	 */
+	public boolean getUseFastIntParsing() {
+		return this.useFastIntParsing.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getUseHostsInPrivileges() {
+		return this.useHostsInPrivileges.getValueAsBoolean();
+	}
+
+	public boolean getUseInformationSchema() {
+		return this.useInformationSchema.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the useLocalSessionState.
+	 */
+	public boolean getUseLocalSessionState() {
+		return this.useLocalSessionState.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the useOldUTF8Behavior.
+	 */
+	public boolean getUseOldUTF8Behavior() {
+		return this.useOldUTF8BehaviorAsBoolean;
+	}
+
+	/**
+	 * @return Returns the useOnlyServerErrorMessages.
+	 */
+	public boolean getUseOnlyServerErrorMessages() {
+		return this.useOnlyServerErrorMessages.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the useReadAheadInput.
+	 */
+	public boolean getUseReadAheadInput() {
+		return this.useReadAheadInput.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getUseServerPreparedStmts() {
+		return this.detectServerPreparedStmts.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the useSqlStateCodes state.
+	 */
+	public boolean getUseSqlStateCodes() {
+		return this.useSqlStateCodes.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getUseSSL() {
+		return this.useSSL.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getUseStreamLengthsInPrepStmts() {
+		return this.useStreamLengthsInPrepStmts.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getUseTimezone() {
+		return this.useTimezone.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getUseUltraDevWorkAround() {
+		return this.useUltraDevWorkAround.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the useUnbufferedInput.
+	 */
+	public boolean getUseUnbufferedInput() {
+		return this.useUnbufferedInput.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return
+	 */
+	public boolean getUseUnicode() {
+		return this.useUnicodeAsBoolean;
+	}
+
+	/**
+	 * Returns whether or not the driver advises of proper usage.
+	 * 
+	 * @return the value of useUsageAdvisor
+	 */
+	public boolean getUseUsageAdvisor() {
+		return this.useUsageAdvisorAsBoolean;
+	}
+
+	public boolean getYearIsDateType() {
+		return this.yearIsDateType.getValueAsBoolean();
+	}
+
+	/**
+	 * @return Returns the zeroDateTimeBehavior.
+	 */
+	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",
+						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);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setAllowMultiQueries(boolean property) {
+		this.allowMultiQueries.setValue(property);
+	}
+
+	/**
+	 * @param allowNanAndInf
+	 *            The allowNanAndInf to set.
+	 */
+	public void setAllowNanAndInf(boolean flag) {
+		this.allowNanAndInf.setValue(flag);
+	}
+
+	/**
+	 * @param allowUrlInLocalInfile
+	 *            The allowUrlInLocalInfile to set.
+	 */
+	public void setAllowUrlInLocalInfile(boolean flag) {
+		this.allowUrlInLocalInfile.setValue(flag);
+	}
+
+	/**
+	 * @param alwaysSendSetIsolation
+	 *            The alwaysSendSetIsolation to set.
+	 */
+	public void setAlwaysSendSetIsolation(boolean flag) {
+		this.alwaysSendSetIsolation.setValue(flag);
+	}
+
+	/**
+	 * @param autoDeserialize
+	 *            The autoDeserialize to set.
+	 */
+	public void setAutoDeserialize(boolean flag) {
+		this.autoDeserialize.setValue(flag);
+	}
+
+	public void setAutoGenerateTestcaseScript(boolean flag) {
+		this.autoGenerateTestcaseScript.setValue(flag);
+		this.autoGenerateTestcaseScriptAsBoolean = this.autoGenerateTestcaseScript
+				.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The autoReconnect to set.
+	 */
+	public void setAutoReconnect(boolean flag) {
+		this.autoReconnect.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setAutoReconnectForConnectionPools(boolean property) {
+		this.autoReconnectForPools.setValue(property);
+		this.autoReconnectForPoolsAsBoolean = this.autoReconnectForPools
+				.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The autoReconnectForPools to set.
+	 */
+	public void setAutoReconnectForPools(boolean flag) {
+		this.autoReconnectForPools.setValue(flag);
+	}
+
+	/**
+	 * @param blobSendChunkSize
+	 *            The blobSendChunkSize to set.
+	 */
+	public void setBlobSendChunkSize(String value) throws SQLException {
+		this.blobSendChunkSize.setValue(value);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The cacheCallableStatements to set.
+	 */
+	public void setCacheCallableStatements(boolean flag) {
+		this.cacheCallableStatements.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The cachePreparedStatements to set.
+	 */
+	public void setCachePreparedStatements(boolean flag) {
+		this.cachePreparedStatements.setValue(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();
+	}
+
+	/**
+	 * @param cacheServerConfiguration
+	 *            The cacheServerConfiguration to set.
+	 */
+	public void setCacheServerConfiguration(boolean flag) {
+		this.cacheServerConfiguration.setValue(flag);
+	}
+
+	/**
+	 * Configures the number of callable statements to cache. (this is
+	 * configurable during the life of the connection).
+	 * 
+	 * @param size
+	 *            The callableStatementCacheSize to set.
+	 */
+	public void setCallableStatementCacheSize(int size) {
+		this.callableStatementCacheSize.setValue(size);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setCapitalizeDBMDTypes(boolean property) {
+		this.capitalizeTypeNames.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The capitalizeTypeNames to set.
+	 */
+	public void setCapitalizeTypeNames(boolean flag) {
+		this.capitalizeTypeNames.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param encoding
+	 *            The characterEncoding to set.
+	 */
+	public void setCharacterEncoding(String encoding) {
+		this.characterEncoding.setValue(encoding);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param characterSet
+	 *            The characterSetResults to set.
+	 */
+	public void setCharacterSetResults(String characterSet) {
+		this.characterSetResults.setValue(characterSet);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The clobberStreamingResults to set.
+	 */
+	public void setClobberStreamingResults(boolean flag) {
+		this.clobberStreamingResults.setValue(flag);
+	}
+
+	public void setClobCharacterEncoding(String encoding) {
+		this.clobCharacterEncoding.setValue(encoding);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param collation
+	 *            The connectionCollation to set.
+	 */
+	public void setConnectionCollation(String collation) {
+		this.connectionCollation.setValue(collation);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param timeoutMs
+	 */
+	public void setConnectTimeout(int timeoutMs) {
+		this.connectTimeout.setValue(timeoutMs);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setContinueBatchOnError(boolean property) {
+		this.continueBatchOnError.setValue(property);
+	}
+
+	public void setCreateDatabaseIfNotExist(boolean flag) {
+		this.createDatabaseIfNotExist.setValue(flag);
+	}
+
+	public void setDefaultFetchSize(int n) {
+		this.defaultFetchSize.setValue(n);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setDetectServerPreparedStmts(boolean property) {
+		this.detectServerPreparedStmts.setValue(property);
+	}
+
+	/**
+	 * @param dontTrackOpenResources
+	 *            The dontTrackOpenResources to set.
+	 */
+	public void setDontTrackOpenResources(boolean flag) {
+		this.dontTrackOpenResources.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The dumpQueriesOnException to set.
+	 */
+	public void setDumpQueriesOnException(boolean flag) {
+		this.dumpQueriesOnException.setValue(flag);
+	}
+
+	/**
+	 * @param dynamicCalendars
+	 *            The dynamicCalendars to set.
+	 */
+	public void setDynamicCalendars(boolean flag) {
+		this.dynamicCalendars.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The elideSetAutoCommits to set.
+	 */
+	public void setElideSetAutoCommits(boolean flag) {
+		this.elideSetAutoCommits.setValue(flag);
+	}
+
+	public void setEmptyStringsConvertToZero(boolean flag) {
+		this.emptyStringsConvertToZero.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setEmulateLocators(boolean property) {
+		this.emulateLocators.setValue(property);
+	}
+
+	/**
+	 * @param emulateUnsupportedPstmts
+	 *            The emulateUnsupportedPstmts to set.
+	 */
+	public void setEmulateUnsupportedPstmts(boolean flag) {
+		this.emulateUnsupportedPstmts.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The enablePacketDebug to set.
+	 */
+	public void setEnablePacketDebug(boolean flag) {
+		this.enablePacketDebug.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setEncoding(String property) {
+		this.characterEncoding.setValue(property);
+		this.characterEncodingAsString = this.characterEncoding
+				.getValueAsString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The explainSlowQueries to set.
+	 */
+	public void setExplainSlowQueries(boolean flag) {
+		this.explainSlowQueries.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The failOverReadOnly to set.
+	 */
+	public void setFailOverReadOnly(boolean flag) {
+		this.failOverReadOnly.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The gatherPerformanceMetrics to set.
+	 */
+	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();
+	}
+
+	/**
+	 * @param holdResultsOpenOverStatementClose
+	 *            The holdResultsOpenOverStatementClose to set.
+	 */
+	public void setHoldResultsOpenOverStatementClose(boolean flag) {
+		this.holdResultsOpenOverStatementClose.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setIgnoreNonTxTables(boolean property) {
+		this.ignoreNonTxTables.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setInitialTimeout(int property) {
+		this.initialTimeout.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setIsInteractiveClient(boolean property) {
+		this.isInteractiveClient.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The jdbcCompliantTruncation to set.
+	 */
+	public void setJdbcCompliantTruncation(boolean flag) {
+		this.jdbcCompliantTruncation.setValue(flag);
+	}
+
+	/**
+	 * @param locatorFetchBufferSize
+	 *            The locatorFetchBufferSize to set.
+	 */
+	public void setLocatorFetchBufferSize(String value) throws SQLException {
+		this.locatorFetchBufferSize.setValue(value);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setLogger(String property) {
+		this.loggerClassName.setValueAsObject(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param className
+	 *            The loggerClassName to set.
+	 */
+	public void setLoggerClassName(String className) {
+		this.loggerClassName.setValue(className);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The logSlowQueries to set.
+	 */
+	public void setLogSlowQueries(boolean flag) {
+		this.logSlowQueries.setValue(flag);
+	}
+
+	public void setMaintainTimeStats(boolean flag) {
+		this.maintainTimeStats.setValue(flag);
+		this.maintainTimeStatsAsBoolean = this.maintainTimeStats
+				.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param sizeInBytes
+	 *            The maxQuerySizeToLog to set.
+	 */
+	public void setMaxQuerySizeToLog(int sizeInBytes) {
+		this.maxQuerySizeToLog.setValue(sizeInBytes);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setMaxReconnects(int property) {
+		this.maxReconnects.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setMaxRows(int property) {
+		this.maxRows.setValue(property);
+		this.maxRowsAsInt = this.maxRows.getValueAsInt();
+	}
+
+	/**
+	 * Sets the number of queries that metadata can be cached if caching is
+	 * enabled.
+	 * 
+	 * @param value
+	 *            the number of queries to cache metadata for.
+	 */
+	public void setMetadataCacheSize(int value) {
+		this.metadataCacheSize.setValue(value);
+	}
+
+	/**
+	 * @param noDatetimeStringSync
+	 *            The noDatetimeStringSync to set.
+	 */
+	public void setNoDatetimeStringSync(boolean flag) {
+		this.noDatetimeStringSync.setValue(flag);
+	}
+
+	public void setNullCatalogMeansCurrent(boolean value) {
+		this.nullCatalogMeansCurrent.setValue(value);
+	}
+
+	public void setNullNamePatternMatchesAll(boolean value) {
+		this.nullNamePatternMatchesAll.setValue(value);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param size
+	 *            The packetDebugBufferSize to set.
+	 */
+	public void setPacketDebugBufferSize(int size) {
+		this.packetDebugBufferSize.setValue(size);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setParanoid(boolean property) {
+		this.paranoid.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setPedantic(boolean property) {
+		this.pedantic.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param cacheSize
+	 *            The preparedStatementCacheSize to set.
+	 */
+	public void setPreparedStatementCacheSize(int cacheSize) {
+		this.preparedStatementCacheSize.setValue(cacheSize);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param cacheSqlLimit
+	 *            The preparedStatementCacheSqlLimit to set.
+	 */
+	public void setPreparedStatementCacheSqlLimit(int cacheSqlLimit) {
+		this.preparedStatementCacheSqlLimit.setValue(cacheSqlLimit);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setProfileSql(boolean property) {
+		this.profileSQL.setValue(property);
+		this.profileSQLAsBoolean = this.profileSQL.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The profileSQL to set.
+	 */
+	public void setProfileSQL(boolean flag) {
+		this.profileSQL.setValue(flag);
+	}
+
+	/**
+	 * @param propertiesTransform
+	 *            The propertiesTransform to set.
+	 */
+	public void setPropertiesTransform(String value) {
+		this.propertiesTransform.setValue(value);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setQueriesBeforeRetryMaster(int property) {
+		this.queriesBeforeRetryMaster.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setReconnectAtTxEnd(boolean property) {
+		this.reconnectAtTxEnd.setValue(property);
+		this.reconnectTxAtEndAsBoolean = this.reconnectAtTxEnd
+				.getValueAsBoolean();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setRelaxAutoCommit(boolean property) {
+		this.relaxAutoCommit.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param millis
+	 *            The reportMetricsIntervalMillis to set.
+	 */
+	public void setReportMetricsIntervalMillis(int millis) {
+		this.reportMetricsIntervalMillis.setValue(millis);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setRequireSSL(boolean property) {
+		this.requireSSL.setValue(property);
+	}
+
+	public void setRetainStatementAfterResultSetClose(boolean flag) {
+		this.retainStatementAfterResultSetClose.setValue(flag);
+	}
+
+	/**
+	 * @param rollbackOnPooledClose
+	 *            The rollbackOnPooledClose to set.
+	 */
+	public void setRollbackOnPooledClose(boolean flag) {
+		this.rollbackOnPooledClose.setValue(flag);
+	}
+
+	/**
+	 * Sets whether or not hosts will be picked in a round-robin fashion.
+	 * 
+	 * @param flag
+	 *            The roundRobinLoadBalance property to set.
+	 */
+	public void setRoundRobinLoadBalance(boolean flag) {
+		this.roundRobinLoadBalance.setValue(flag);
+	}
+
+	/**
+	 * @param runningCTS13
+	 *            The runningCTS13 to set.
+	 */
+	public void setRunningCTS13(boolean flag) {
+		this.runningCTS13.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setSecondsBeforeRetryMaster(int property) {
+		this.secondsBeforeRetryMaster.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 *            DOCUMENT ME!
+	 */
+	public void setServerTimezone(String property) {
+		this.serverTimezone.setValue(property);
+	}
+
+	/**
+	 * @param sessionVariables
+	 *            The sessionVariables to set.
+	 */
+	public void setSessionVariables(String variables) {
+		this.sessionVariables.setValue(variables);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param millis
+	 *            The slowQueryThresholdMillis to set.
+	 */
+	public void setSlowQueryThresholdMillis(int millis) {
+		this.slowQueryThresholdMillis.setValue(millis);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setSocketFactoryClassName(String property) {
+		this.socketFactoryClassName.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setSocketTimeout(int property) {
+		this.socketTimeout.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setStrictFloatingPoint(boolean property) {
+		this.strictFloatingPoint.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setStrictUpdates(boolean property) {
+		this.strictUpdates.setValue(property);
+	}
+
+	/**
+	 * @param tinyInt1isBit
+	 *            The tinyInt1isBit to set.
+	 */
+	public void setTinyInt1isBit(boolean flag) {
+		this.tinyInt1isBit.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The logProtocol to set.
+	 */
+	public void setTraceProtocol(boolean flag) {
+		this.traceProtocol.setValue(flag);
+	}
+
+	public void setTransformedBitIsBoolean(boolean flag) {
+		this.transformedBitIsBoolean.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setUseCompression(boolean property) {
+		this.useCompression.setValue(property);
+	}
+
+	/**
+	 * @param useFastIntParsing
+	 *            The useFastIntParsing to set.
+	 */
+	public void setUseFastIntParsing(boolean flag) {
+		this.useFastIntParsing.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setUseHostsInPrivileges(boolean property) {
+		this.useHostsInPrivileges.setValue(property);
+	}
+
+	public void setUseInformationSchema(boolean flag) {
+		this.useInformationSchema.setValue(flag);
+	}
+
+	/**
+	 * @param useLocalSessionState
+	 *            The useLocalSessionState to set.
+	 */
+	public void setUseLocalSessionState(boolean flag) {
+		this.useLocalSessionState.setValue(flag);
+	}
+
+	/**
+	 * @param useOldUTF8Behavior
+	 *            The useOldUTF8Behavior to set.
+	 */
+	public void setUseOldUTF8Behavior(boolean flag) {
+		this.useOldUTF8Behavior.setValue(flag);
+		this.useOldUTF8BehaviorAsBoolean = this.useOldUTF8Behavior
+				.getValueAsBoolean();
+	}
+
+	/**
+	 * @param useOnlyServerErrorMessages
+	 *            The useOnlyServerErrorMessages to set.
+	 */
+	public void setUseOnlyServerErrorMessages(boolean flag) {
+		this.useOnlyServerErrorMessages.setValue(flag);
+	}
+
+	/**
+	 * @param useReadAheadInput
+	 *            The useReadAheadInput to set.
+	 */
+	public void setUseReadAheadInput(boolean flag) {
+		this.useReadAheadInput.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The detectServerPreparedStmts to set.
+	 */
+	public void setUseServerPreparedStmts(boolean flag) {
+		this.detectServerPreparedStmts.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The useSqlStateCodes to set.
+	 */
+	public void setUseSqlStateCodes(boolean flag) {
+		this.useSqlStateCodes.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setUseSSL(boolean property) {
+		this.useSSL.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setUseStreamLengthsInPrepStmts(boolean property) {
+		this.useStreamLengthsInPrepStmts.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setUseTimezone(boolean property) {
+		this.useTimezone.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param property
+	 */
+	public void setUseUltraDevWorkAround(boolean property) {
+		this.useUltraDevWorkAround.setValue(property);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The useUnbufferedInput to set.
+	 */
+	public void setUseUnbufferedInput(boolean flag) {
+		this.useUnbufferedInput.setValue(flag);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param flag
+	 *            The useUnicode to set.
+	 */
+	public void setUseUnicode(boolean flag) {
+		this.useUnicode.setValue(flag);
+		this.useUnicodeAsBoolean = this.useUnicode.getValueAsBoolean();
+	}
+
+	/**
+	 * Sets whether or not the driver advises of proper usage.
+	 * 
+	 * @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 void setYearIsDateType(boolean flag) {
+		this.yearIsDateType.setValue(flag);
+	}
+
+	/**
+	 * @param zeroDateTimeBehavior
+	 *            The zeroDateTimeBehavior to set.
+	 */
+	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("Huh?");
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return Returns the useUnbufferedInput.
+	 */
+	public boolean useUnbufferedInput() {
+		return this.useUnbufferedInput.getValueAsBoolean();
+	}
+
+	public boolean getUseCursorFetch() {
+		return this.useCursorFetch.getValueAsBoolean();
+	}
+
+	public void setUseCursorFetch(boolean flag) {
+		this.useCursorFetch.setValue(flag);
+	}
+
+	public boolean getOverrideSupportsIntegrityEnhancementFacility() {
+		return this.overrideSupportsIntegrityEnhancementFacility.getValueAsBoolean();
+	}
+
+	public void setOverrideSupportsIntegrityEnhancementFacility(boolean flag) {
+		this.overrideSupportsIntegrityEnhancementFacility.setValue(flag);	
+	}
+	
+	public boolean getNoTimezoneConversionForTimeType() {
+		return this.noTimezoneConversionForTimeType.getValueAsBoolean();
+	}
+
+	public void setNoTimezoneConversionForTimeType(boolean flag) {
+		this.noTimezoneConversionForTimeType.setValue(flag);
+	}
+
+	public boolean getUseJDBCCompliantTimezoneShift() {
+		return this.useJDBCCompliantTimezoneShift.getValueAsBoolean();
+	}
+
+	public void setUseJDBCCompliantTimezoneShift(boolean flag) {
+		this.useJDBCCompliantTimezoneShift.setValue(flag);
+	}
+	
+	public boolean getAutoClosePStmtStreams() {
+		return this.autoClosePStmtStreams.getValueAsBoolean();
+	}
+
+	public void setAutoClosePStmtStreams(boolean flag) {
+		this.autoClosePStmtStreams.setValue(flag);
+	}
+
+	public boolean getProcessEscapeCodesForPrepStmts() {
+		return this.processEscapeCodesForPrepStmts.getValueAsBoolean();
+	}
+
+	public void setProcessEscapeCodesForPrepStmts(boolean flag) {
+		this.processEscapeCodesForPrepStmts.setValue(flag);
+	}
+
+	public boolean getUseGmtMillisForDatetimes() {
+		return this.useGmtMillisForDatetimes.getValueAsBoolean();
+	}
+
+	public void setUseGmtMillisForDatetimes(boolean flag) {
+		this.useGmtMillisForDatetimes.setValue(flag);
+	}
+	
+	public boolean getDumpMetadataOnColumnNotFound() {
+		return this.dumpMetadataOnColumnNotFound.getValueAsBoolean();
+	}
+
+	public void setDumpMetadataOnColumnNotFound(boolean flag) {
+		this.dumpMetadataOnColumnNotFound.setValue(flag);
+	}
+
+	public String getResourceId() {
+		return this.resourceId.getValueAsString();
+	}
+
+	public void setResourceId(String resourceId) {
+		this.resourceId.setValue(resourceId);
+	}
+	
+	public boolean getRewriteBatchedStatements() {
+		return this.rewriteBatchedStatements.getValueAsBoolean();
+	}
+
+	public void setRewriteBatchedStatements(boolean flag) {
+		this.rewriteBatchedStatements.setValue(flag);
+	}
+	
+	public boolean getJdbcCompliantTruncationForReads() {
+		return this.jdbcCompliantTruncationForReads;
+	}
+
+	public void setJdbcCompliantTruncationForReads(
+			boolean jdbcCompliantTruncationForReads) {
+		this.jdbcCompliantTruncationForReads = jdbcCompliantTruncationForReads;
+	}
+
+	public boolean getUseJvmCharsetConverters() {
+		return this.useJvmCharsetConverters.getValueAsBoolean();
+	}
+
+	public void setUseJvmCharsetConverters(boolean flag) {
+		this.useJvmCharsetConverters.setValue(flag);
+	}
+
+	public boolean getPinGlobalTxToPhysicalConnection() {
+		return this.pinGlobalTxToPhysicalConnection.getValueAsBoolean();
+	}
+
+	public void setPinGlobalTxToPhysicalConnection(boolean flag) {
+		this.pinGlobalTxToPhysicalConnection.setValue(flag);
+	}
+	
+	/*
+	 * "Aliases" which match the property names to make using 
+	 * from datasources easier.
+	 */
+	
+	public void setUseServerPrepStmts(boolean flag) {
+		setUseServerPreparedStmts(flag);
+	}
+
+	public boolean getUseServerPrepStmts() {
+		return getUseServerPreparedStmts();
+	}
+
+	public void setCacheCallableStmts(boolean flag) {
+		setCacheCallableStatements(flag);
+	}
+
+	public boolean getCacheCallableStmts() {
+		return getCacheCallableStatements();
+	}
+
+	public void setCachePrepStmts(boolean flag) {
+		setCachePreparedStatements(flag);
+	}
+
+	public boolean getCachePrepStmts() {
+		return getCachePreparedStatements();
+	}
+
+	public void setCallableStmtCacheSize(int cacheSize) {
+		setCallableStatementCacheSize(cacheSize);
+	}
+
+	public int getCallableStmtCacheSize() {
+		return getCallableStatementCacheSize();
+	}
+
+	public void setPrepStmtCacheSize(int cacheSize) {
+		setPreparedStatementCacheSize(cacheSize);
+	}
+
+	public int getPrepStmtCacheSize() {
+		return getPreparedStatementCacheSize();
+	}
+
+	public void setPrepStmtCacheSqlLimit(int sqlLimit) {
+		setPreparedStatementCacheSqlLimit(sqlLimit);
+	}
+
+	public int getPrepStmtCacheSqlLimit() {
+		return getPreparedStatementCacheSqlLimit();
+	}
+
+	public boolean getNoAccessToProcedureBodies() {
+		return this.noAccessToProcedureBodies.getValueAsBoolean();
+	}
+
+	public void setNoAccessToProcedureBodies(boolean flag) {
+		this.noAccessToProcedureBodies.setValue(flag);
+	}
+
+	public boolean getUseOldAliasMetadataBehavior() {
+		return this.useOldAliasMetadataBehavior.getValueAsBoolean();
+	}
+
+	public void setUseOldAliasMetadataBehavior(boolean flag) {
+		this.useOldAliasMetadataBehavior.setValue(flag);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionPropertiesTransform.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionPropertiesTransform.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ConnectionPropertiesTransform.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+/**
+ * Implement this interface, and pass the class name as the
+ * 'propertiesTransform' property in your JDBC URL, and the driver will pass the
+ * properties it has parsed to your transform implementation so that you can
+ * modify/substitute/add any that you desire.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: ConnectionPropertiesTransform.java,v 1.1.2.1 2005/05/13
+ *          18:58:37 mmatthews Exp $
+ */
+public interface ConnectionPropertiesTransform {
+	/**
+	 * The JDBC driver will call this method if the user has loaded your
+	 * implementation of this interface by specifying the 'propertiesTransform'
+	 * property in their JDBC URL.
+	 * 
+	 * @param props
+	 *            the properties as passed by the driver (never null)
+	 * 
+	 * @return the same properties with any transformations that your
+	 *         implementation has made
+	 * 
+	 * @throws SQLException
+	 *             if a transform can not be made for any reason.
+	 */
+	public Properties transformProperties(Properties props) throws SQLException;
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Constants.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Constants.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Constants.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,45 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+/**
+ * Represents various constants used in the driver.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: Constants.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+class Constants {
+	/**
+	 * Avoids allocation of empty byte[] when representing 0-length strings.
+	 */
+	public final static byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+	/**
+	 * Prevents instantiation
+	 */
+	private Constants() {
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CursorRowProvider.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CursorRowProvider.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/CursorRowProvider.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,440 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package com.mysql.jdbc;
+
+import 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 CursorRowProvider 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 ResultSet 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[] fields;
+
+	/**
+	 * 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;
+
+	/**
+	 * 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 CursorRowProvider(MysqlIO ioChannel,
+			ServerPreparedStatement creatingStatement, Field[] metadata) {
+		this.currentPositionInEntireResult = BEFORE_START_OF_ROWS;
+		this.fields = metadata;
+		this.mysql = ioChannel;
+		this.statementIdOnServer = creatingStatement.getServerStatementId();
+		this.prepStmt = creatingStatement;
+	}
+
+	/**
+	 * 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 Object[] 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(byte[][] 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.fields = 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.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 Object[] next() throws SQLException {
+
+		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;
+		}
+
+		Object[] row = (Object[]) this.fetchedRows
+				.get(this.currentPositionInFetchedRows);
+
+		return row;
+	}
+
+	/**
+	 * 
+	 */
+	private void fetchMoreRows() throws SQLException {
+		if (this.lastRowFetched) {
+			this.fetchedRows = new ArrayList(0);
+			return;
+		}
+
+		synchronized (this.owner.connection.getMutex()) {
+			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.fields, numRowsToFetch);
+			this.currentPositionInFetchedRows = BEFORE_START_OF_ROWS;
+
+			if ((this.mysql.getServerStatus() & SERVER_STATUS_LAST_ROW_SENT) != 0) {
+				this.lastRowFetched = 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(ResultSet rs) {
+		this.owner = rs;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.RowProvider#getOwner()
+	 */
+	public ResultSet getOwner() {
+		return this.owner;
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DatabaseMetaData.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DatabaseMetaData.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DatabaseMetaData.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,7396 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.UnsupportedEncodingException;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TreeMap;
+
+/**
+ * JDBC Interface to Mysql functions
+ * <p>
+ * This class provides information about the database as a whole.
+ * </p>
+ * <p>
+ * Many of the methods here return lists of information in ResultSets. You can
+ * use the normal ResultSet methods such as getString and getInt to retrieve the
+ * data from these ResultSets. If a given form of metadata is not available,
+ * these methods show throw a SQLException.
+ * </p>
+ * <p>
+ * Some of these methods take arguments that are String patterns. These methods
+ * all have names such as fooPattern. Within a pattern String "%" means match
+ * any substring of 0 or more characters and "_" means match any one character.
+ * </p>
+ * 
+ * @author Mark Matthews
+ * @version $Id: DatabaseMetaData.java,v 1.27.4.66 2005/05/03 18:40:39 mmatthews
+ *          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;
+
+		abstract boolean hasNext() throws SQLException;
+
+		abstract Object next() throws SQLException;
+	}
+
+	class LocalAndReferencedColumns {
+		String constraintName;
+
+		List localColumnsList;
+
+		String referencedCatalog;
+
+		List referencedColumnsList;
+
+		String referencedTable;
+
+		LocalAndReferencedColumns(List localColumns, List refColumns,
+				String constName, String refCatalog, String refTable) {
+			this.localColumnsList = localColumns;
+			this.referencedColumnsList = refColumns;
+			this.constraintName = constName;
+			this.referencedTable = refTable;
+			this.referencedCatalog = refCatalog;
+		}
+	}
+
+	protected class ResultSetIterator extends IteratorWithCleanup {
+		int colIndex;
+
+		ResultSet resultSet;
+
+		ResultSetIterator(ResultSet rs, int index) {
+			resultSet = rs;
+			colIndex = index;
+		}
+
+		void close() throws SQLException {
+			resultSet.close();
+		}
+
+		boolean hasNext() throws SQLException {
+			return resultSet.next();
+		}
+
+		Object next() throws SQLException {
+			return resultSet.getObject(colIndex);
+		}
+	}
+
+	protected class SingleStringIterator extends IteratorWithCleanup {
+		boolean onFirst = true;
+
+		String value;
+
+		SingleStringIterator(String s) {
+			value = s;
+		}
+
+		void close() throws SQLException {
+			// not needed
+
+		}
+
+		boolean hasNext() throws SQLException {
+			return onFirst;
+		}
+
+		Object next() throws SQLException {
+			onFirst = false;
+			return value;
+		}
+	}
+
+	/**
+	 * Parses and represents common data type information used by various
+	 * column/parameter methods.
+	 */
+	class TypeDescriptor {
+		int bufferLength;
+
+		int charOctetLength;
+
+		int columnSize;
+
+		short dataType;
+
+		int decimalDigits;
+
+		String isNullable;
+
+		int nullability;
+
+		int numPrecRadix = 10;
+
+		String typeName;
+
+		TypeDescriptor(String typeInfo, String nullabilityInfo)
+				throws SQLException {
+			String mysqlType = "";
+			String fullMysqlType = null;
+
+			if (typeInfo.indexOf("(") != -1) {
+				mysqlType = typeInfo.substring(0, typeInfo.indexOf("("));
+			} else {
+				mysqlType = typeInfo;
+			}
+
+			int indexOfUnsignedInMysqlType = StringUtils.indexOfIgnoreCase(
+					mysqlType, "unsigned");
+
+			if (indexOfUnsignedInMysqlType != -1) {
+				mysqlType = mysqlType.substring(0,
+						(indexOfUnsignedInMysqlType - 1));
+			}
+
+			// Add unsigned to typename reported to enduser as 'native type', if
+			// present
+
+			if (StringUtils.indexOfIgnoreCase(typeInfo, "unsigned") != -1) {
+				fullMysqlType = mysqlType + " unsigned";
+			} else {
+				fullMysqlType = mysqlType;
+			}
+
+			if (conn.getCapitalizeTypeNames()) {
+				fullMysqlType = fullMysqlType.toUpperCase(Locale.ENGLISH);
+			}
+
+			this.dataType = (short) MysqlDefs.mysqlToJavaType(mysqlType);
+
+			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;
+
+					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;
+
+					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;
+
+					/* If the size is specified with the DDL, use that */
+					if (typeInfo.indexOf("(") != -1) {
+						int endParenIndex = typeInfo.indexOf(")");
+
+						if (endParenIndex == -1) {
+							endParenIndex = typeInfo.length();
+						}
+
+						this.columnSize = Integer.parseInt(typeInfo.substring(
+								(typeInfo.indexOf("(") + 1), endParenIndex));
+
+						// 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";
+							}
+						}
+					} 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;
+					}
+
+					this.decimalDigits = 0;
+				}
+			} else {
+				this.decimalDigits = 0;
+				this.columnSize = 0;
+			}
+
+			// BUFFER_LENGTH
+			this.bufferLength = MysqlIO.getMaxBuf();
+
+			// NUM_PREC_RADIX (is this right for char?)
+			this.numPrecRadix = 10;
+
+			// Nullable?
+			if (nullabilityInfo != null) {
+				if (nullabilityInfo.equals("YES")) {
+					this.nullability = java.sql.DatabaseMetaData.columnNullable;
+					this.isNullable = "YES";
+
+					// IS_NULLABLE
+				} else {
+					this.nullability = java.sql.DatabaseMetaData.columnNoNulls;
+					this.isNullable = "NO";
+				}
+			} else {
+				this.nullability = java.sql.DatabaseMetaData.columnNoNulls;
+				this.isNullable = "NO";
+			}
+		}
+	}
+
+	private static final int DEFERRABILITY = 13;
+
+	private static final int DELETE_RULE = 10;
+
+	private static final int FK_NAME = 11;
+
+	private static final int FKCOLUMN_NAME = 7;
+
+	private static final int FKTABLE_CAT = 4;
+
+	private static final int FKTABLE_NAME = 6;
+
+	private static final int FKTABLE_SCHEM = 5;
+
+	private static final int KEY_SEQ = 8;
+
+	private static final int PK_NAME = 12;
+
+	private static final int PKCOLUMN_NAME = 3;
+
+	//
+	// Column indexes used by all DBMD foreign key
+	// ResultSets
+	//
+	private static final int PKTABLE_CAT = 0;
+
+	private static final int PKTABLE_NAME = 2;
+
+	private static final int PKTABLE_SCHEM = 1;
+
+	/** The table type for generic tables that support foreign keys. */
+	private static final String SUPPORTS_FK = "SUPPORTS_FK";
+
+	private static final byte[] TABLE_AS_BYTES = "TABLE".getBytes();
+
+	private static final int UPDATE_RULE = 9;
+
+	private static final byte[] VIEW_AS_BYTES = "VIEW".getBytes();
+
+	/** The connection to the database */
+	protected Connection conn;
+
+	/** The 'current' database name being used */
+	protected String database = null;
+
+	/** What character to use when quoting identifiers */
+	protected String quotedId = null;
+
+	/**
+	 * Creates a new DatabaseMetaData object.
+	 * 
+	 * @param connToSet
+	 *            DOCUMENT ME!
+	 * @param databaseToSet
+	 *            DOCUMENT ME!
+	 */
+	public DatabaseMetaData(Connection connToSet, String databaseToSet) {
+		this.conn = connToSet;
+		this.database = databaseToSet;
+
+		try {
+			this.quotedId = this.conn.supportsQuotedIdentifiers() ? getIdentifierQuoteString()
+					: "";
+		} catch (SQLException sqlEx) {
+			// Forced by API, never thrown from getIdentifierQuoteString() in
+			// this
+			// implementation.
+			AssertionFailedException.shouldNotHappen(sqlEx);
+		}
+	}
+
+	/**
+	 * Can all the procedures returned by getProcedures be called by the current
+	 * user?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean allProceduresAreCallable() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can all the tables returned by getTable be SELECTed by the current user?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean allTablesAreSelectable() throws SQLException {
+		return false;
+	}
+
+	private java.sql.ResultSet buildResultSet(com.mysql.jdbc.Field[] fields,
+			java.util.ArrayList rows) throws SQLException {
+		return buildResultSet(fields, rows, this.conn);
+	}
+	
+	static java.sql.ResultSet buildResultSet(com.mysql.jdbc.Field[] fields,
+			java.util.ArrayList rows, Connection c) throws SQLException {
+		int fieldsLength = fields.length;
+
+		for (int i = 0; i < fieldsLength; i++) {
+			fields[i].setConnection(c);
+			fields[i].setUseOldNameMetadata(true);
+		}
+
+		return new com.mysql.jdbc.ResultSet(c.getCatalog(), fields,
+				new RowDataStatic(rows), c, null);
+	}
+
+	private void convertToJdbcFunctionList(String catalog,
+			ResultSet proceduresRs, boolean needsClientFiltering, String db,
+			Map procedureRowsOrderedByName, int nameIndex) throws SQLException {
+		while (proceduresRs.next()) {
+			boolean shouldAdd = true;
+
+			if (needsClientFiltering) {
+				shouldAdd = false;
+
+				String procDb = proceduresRs.getString(1);
+
+				if (db == null && procDb == null) {
+					shouldAdd = true;
+				} 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));
+
+				procedureRowsOrderedByName.put(functionName, rowData);
+			}
+		}
+	}
+
+	private void convertToJdbcProcedureList(boolean fromSelect, String catalog,
+			ResultSet proceduresRs, boolean needsClientFiltering, String db,
+			Map procedureRowsOrderedByName, int nameIndex) throws SQLException {
+		while (proceduresRs.next()) {
+			boolean shouldAdd = true;
+
+			if (needsClientFiltering) {
+				shouldAdd = false;
+
+				String procDb = proceduresRs.getString(1);
+
+				if (db == null && procDb == null) {
+					shouldAdd = true;
+				} else if (db != null & db.equals(procDb)) {
+					shouldAdd = true;
+				}
+			}
+
+			if (shouldAdd) {
+				String procedureName = proceduresRs.getString(nameIndex);
+				byte[][] rowData = new byte[8][];
+				rowData[0] = catalog == null ? null : s2b(catalog);
+				rowData[1] = null;
+				rowData[2] = s2b(procedureName);
+				rowData[3] = null;
+				rowData[4] = null;
+				rowData[5] = null;
+				rowData[6] = null;
+
+				boolean isFunction = fromSelect ? "FUNCTION"
+						.equalsIgnoreCase(proceduresRs.getString("type"))
+						: false;
+				rowData[7] = s2b(isFunction ? Integer
+						.toString(procedureReturnsResult) : Integer
+						.toString(procedureResultUnknown));
+
+				procedureRowsOrderedByName.put(procedureName, rowData);
+			}
+		}
+	}
+
+	private byte[][] convertTypeDescriptorToProcedureRow(
+			byte[] procNameAsBytes, String paramName, boolean isOutParam,
+			boolean isInParam, boolean isReturnParam, TypeDescriptor typeDesc)
+			throws SQLException {
+		byte[][] row = 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
+		if (isInParam && isOutParam) {
+			row[4] = s2b(String.valueOf(procedureColumnInOut));
+		} else if (isInParam) {
+			row[4] = s2b(String.valueOf(procedureColumnIn));
+		} else if (isOutParam) {
+			row[4] = s2b(String.valueOf(procedureColumnOut));
+		} else if (isReturnParam) {
+			row[4] = s2b(String.valueOf(procedureColumnReturn));
+		} else {
+			row[4] = s2b(String.valueOf(procedureColumnUnknown));
+		}
+		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[8] = s2b(Integer.toString(typeDesc.bufferLength)); // LENGTH
+		row[9] = s2b(Integer.toString(typeDesc.decimalDigits)); // 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
+
+			break;
+
+		case columnNullable:
+			row[11] = s2b(Integer.toString(procedureNullable)); // NULLABLE
+
+			break;
+
+		case columnNullableUnknown:
+			row[11] = s2b(Integer.toString(procedureNullableUnknown)); // nullable
+
+			break;
+
+		default:
+			throw SQLError.createSQLException(
+					"Internal error while parsing callable statement metadata (unknown nullability value fount)",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+		row[12] = null;
+		return row;
+	}
+
+	/**
+	 * Does a data definition statement within a transaction force the
+	 * transaction to commit?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean dataDefinitionCausesTransactionCommit() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is a data definition statement within a transaction ignored?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean dataDefinitionIgnoredInTransactions() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * JDBC 2.0 Determine whether or not a visible row delete can be detected by
+	 * calling ResultSet.rowDeleted(). If deletesAreDetected() returns false,
+	 * then deleted rows are removed from the result set.
+	 * 
+	 * @param type
+	 *            set type, i.e. ResultSet.TYPE_XXX
+	 * @return true if changes are detected by the resultset type
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean deletesAreDetected(int type) throws SQLException {
+		return false;
+	}
+
+	// ----------------------------------------------------------------------
+
+	/**
+	 * Did getMaxRowSize() include LONGVARCHAR and LONGVARBINARY blobs?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean doesMaxRowSizeIncludeBlobs() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Extracts foreign key info for one table.
+	 * 
+	 * @param rows
+	 *            the list of rows to add to
+	 * @param rs
+	 *            the result set from 'SHOW CREATE TABLE'
+	 * @param catalog
+	 *            the database name
+	 * @return the list of rows with new rows added
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public List extractForeignKeyForTable(ArrayList rows,
+			java.sql.ResultSet rs, String catalog) throws SQLException {
+		byte[][] row = new byte[3][];
+		row[0] = rs.getBytes(1);
+		row[1] = s2b(SUPPORTS_FK);
+	
+		String createTableString = rs.getString(2);
+		StringTokenizer lineTokenizer = new StringTokenizer(createTableString,
+				"\n");
+		StringBuffer commentBuf = new StringBuffer("comment; ");
+		boolean firstTime = true;
+	
+		String quoteChar = getIdentifierQuoteString();
+	
+		if (quoteChar == null) {
+			quoteChar = "`";
+		}
+	
+		while (lineTokenizer.hasMoreTokens()) {
+			String line = lineTokenizer.nextToken().trim();
+	
+			String constraintName = null;
+	
+			if (StringUtils.startsWithIgnoreCase(line, "CONSTRAINT")) {
+				boolean usingBackTicks = true;
+				int beginPos = line.indexOf(quoteChar);
+	
+				if (beginPos == -1) {
+					beginPos = line.indexOf("\"");
+					usingBackTicks = false;
+				}
+	
+				if (beginPos != -1) {
+					int endPos = -1;
+	
+					if (usingBackTicks) {
+						endPos = line.indexOf(quoteChar, beginPos + 1);
+					} else {
+						endPos = line.indexOf("\"", beginPos + 1);
+					}
+	
+					if (endPos != -1) {
+						constraintName = line.substring(beginPos + 1, endPos);
+						line = line.substring(endPos + 1, line.length()).trim();
+					}
+				}
+			}
+	
+			
+			if (line.startsWith("FOREIGN KEY")) {
+				if (line.endsWith(",")) {
+					line = line.substring(0, line.length() - 1);
+				}
+	
+				char quote = this.quotedId.charAt(0);
+				
+				int indexOfFK = line.indexOf("FOREIGN KEY");
+				
+				String localColumnName = null;
+				String referencedCatalogName = this.quotedId + catalog + this.quotedId;
+				String referencedTableName = null;
+				String referencedColumnName = null;
+				
+				
+				if (indexOfFK != -1) {
+					int afterFk = indexOfFK + "FOREIGN KEY".length();
+					
+					int indexOfRef = StringUtils.indexOfIgnoreCaseRespectQuotes(afterFk, line, "REFERENCES", quote, true);
+					
+					if (indexOfRef != -1) {
+						
+						int indexOfParenOpen = line.indexOf('(', afterFk);
+						int indexOfParenClose = StringUtils.indexOfIgnoreCaseRespectQuotes(indexOfParenOpen, line, ")", quote, true);
+						
+						if (indexOfParenOpen == -1 || indexOfParenClose == -1) {
+							// throw SQLError.createSQLException();
+						}
+						
+						localColumnName = line.substring(indexOfParenOpen + 1, indexOfParenClose);
+						
+						int afterRef = indexOfRef + "REFERENCES".length();
+						
+						int referencedColumnBegin = StringUtils.indexOfIgnoreCaseRespectQuotes(afterRef, line, "(", quote, true);
+						
+						if (referencedColumnBegin != -1) {
+							referencedTableName = line.substring(afterRef, referencedColumnBegin);
+	
+							int referencedColumnEnd = StringUtils.indexOfIgnoreCaseRespectQuotes(referencedColumnBegin + 1, line, ")", quote, true);
+							
+							if (referencedColumnEnd != -1) {
+								referencedColumnName = line.substring(referencedColumnBegin + 1, referencedColumnEnd);
+							}
+							
+							int indexOfCatalogSep = StringUtils.indexOfIgnoreCaseRespectQuotes(0, referencedTableName, ".", quote, true);
+							
+							if (indexOfCatalogSep != -1) {
+								referencedCatalogName = referencedTableName.substring(0, indexOfCatalogSep);
+								referencedTableName = referencedTableName.substring(indexOfCatalogSep + 1);
+							}
+						}
+					}
+				}
+				
+				
+				if (!firstTime) {
+					commentBuf.append("; ");
+				} else {
+					firstTime = false;
+				}
+	
+				if (constraintName != null) {
+					commentBuf.append(constraintName);
+				} else {
+					commentBuf.append("not_available");
+				}
+	
+				commentBuf.append("(");
+				commentBuf.append(localColumnName);
+				commentBuf.append(") REFER ");
+				commentBuf.append(referencedCatalogName);
+				commentBuf.append("/");
+				commentBuf.append(referencedTableName);
+				commentBuf.append("(");
+				commentBuf.append(referencedColumnName);
+				commentBuf.append(")");
+	
+				int lastParenIndex = line.lastIndexOf(")");
+	
+				if (lastParenIndex != (line.length() - 1)) {
+					String cascadeOptions = cascadeOptions = line
+							.substring(lastParenIndex + 1);
+					commentBuf.append(" ");
+					commentBuf.append(cascadeOptions);
+				}
+			}
+		}
+	
+		row[2] = s2b(commentBuf.toString());
+		rows.add(row);
+	
+		return rows;
+	}
+
+	/**
+	 * Creates a result set similar enough to 'SHOW TABLE STATUS' to allow the
+	 * same code to work on extracting the foreign key data
+	 * 
+	 * @param connToUse
+	 *            the database connection to use
+	 * @param metadata
+	 *            the DatabaseMetaData instance calling this method
+	 * @param catalog
+	 *            the database name to extract foreign key info for
+	 * @param tableName
+	 *            the table to extract foreign key info for
+	 * @return A result set that has the structure of 'show table status'
+	 * @throws SQLException
+	 *             if a database access error occurs.
+	 */
+	public ResultSet extractForeignKeyFromCreateTable(String catalog,
+			String tableName) throws SQLException {
+		ArrayList tableList = new ArrayList();
+		java.sql.ResultSet rs = null;
+		java.sql.Statement stmt = null;
+
+		if (tableName != null) {
+			tableList.add(tableName);
+		} else {
+			try {
+				rs = getTables(catalog, "", "%", new String[] { "TABLE" });
+
+				while (rs.next()) {
+					tableList.add(rs.getString("TABLE_NAME"));
+				}
+			} finally {
+				if (rs != null) {
+					rs.close();
+				}
+
+				rs = null;
+			}
+		}
+
+		ArrayList rows = new ArrayList();
+		Field[] fields = new Field[3];
+		fields[0] = new Field("", "Name", Types.CHAR, Integer.MAX_VALUE);
+		fields[1] = new Field("", "Type", Types.CHAR, 255);
+		fields[2] = new Field("", "Comment", Types.CHAR, Integer.MAX_VALUE);
+
+		int numTables = tableList.size();
+		stmt = this.conn.getMetadataSafeStatement();
+
+		String quoteChar = getIdentifierQuoteString();
+
+		if (quoteChar == null) {
+			quoteChar = "`";
+		}
+
+		try {
+			for (int i = 0; i < numTables; i++) {
+				String tableToExtract = (String) tableList.get(i);
+
+				String query = new StringBuffer("SHOW CREATE TABLE ").append(
+						quoteChar).append(catalog).append(quoteChar)
+						.append(".").append(quoteChar).append(tableToExtract)
+						.append(quoteChar).toString();
+				rs = stmt.executeQuery(query);
+
+				while (rs.next()) {
+					extractForeignKeyForTable(rows, rs, catalog);
+				}
+			}
+		} finally {
+			if (rs != null) {
+				rs.close();
+			}
+
+			rs = null;
+
+			if (stmt != null) {
+				stmt.close();
+			}
+
+			stmt = null;
+		}
+
+		return buildResultSet(fields, rows);
+	}
+
+	/**
+	 * @see DatabaseMetaData#getAttributes(String, String, String, String)
+	 */
+	public java.sql.ResultSet getAttributes(String arg0, String arg1,
+			String arg2, String arg3) throws SQLException {
+		Field[] fields = new Field[21];
+		fields[0] = new Field("", "TYPE_CAT", Types.CHAR, 32);
+		fields[1] = new Field("", "TYPE_SCHEM", Types.CHAR, 32);
+		fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
+		fields[3] = new Field("", "ATTR_NAME", Types.CHAR, 32);
+		fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
+		fields[5] = new Field("", "ATTR_TYPE_NAME", Types.CHAR, 32);
+		fields[6] = new Field("", "ATTR_SIZE", Types.INTEGER, 32);
+		fields[7] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 32);
+		fields[8] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 32);
+		fields[9] = new Field("", "NULLABLE ", Types.INTEGER, 32);
+		fields[10] = new Field("", "REMARKS", Types.CHAR, 32);
+		fields[11] = new Field("", "ATTR_DEF", Types.CHAR, 32);
+		fields[12] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 32);
+		fields[13] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 32);
+		fields[14] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, 32);
+		fields[15] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 32);
+		fields[16] = new Field("", "IS_NULLABLE", Types.CHAR, 32);
+		fields[17] = new Field("", "SCOPE_CATALOG", Types.CHAR, 32);
+		fields[18] = new Field("", "SCOPE_SCHEMA", Types.CHAR, 32);
+		fields[19] = new Field("", "SCOPE_TABLE", Types.CHAR, 32);
+		fields[20] = new Field("", "SOURCE_DATA_TYPE", Types.SMALLINT, 32);
+
+		return buildResultSet(fields, new ArrayList());
+	}
+
+	/**
+	 * Get a description of a table's optimal set of columns that uniquely
+	 * identifies a row. They are ordered by SCOPE.
+	 * <P>
+	 * Each column description has the following columns:
+	 * <OL>
+	 * <li> <B>SCOPE</B> short => actual scope of result
+	 * <UL>
+	 * <li> bestRowTemporary - very temporary, while using row </li>
+	 * <li> bestRowTransaction - valid for remainder of current transaction
+	 * </li>
+	 * <li> bestRowSession - valid for remainder of current session </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
+	 * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
+	 * <li> <B>COLUMN_SIZE</B> int => precision </li>
+	 * <li> <B>BUFFER_LENGTH</B> int => not used </li>
+	 * <li> <B>DECIMAL_DIGITS</B> short => scale </li>
+	 * <li> <B>PSEUDO_COLUMN</B> short => is this a pseudo column like an
+	 * Oracle ROWID
+	 * <UL>
+	 * <li> bestRowUnknown - may or may not be pseudo column </li>
+	 * <li> bestRowNotPseudo - is NOT a pseudo column </li>
+	 * <li> bestRowPseudo - is a pseudo column </li>
+	 * </ul>
+	 * </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @param scope
+	 *            the scope of interest; use same values as SCOPE
+	 * @param nullable
+	 *            include columns that are nullable?
+	 * @return ResultSet each row is a column description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.ResultSet getBestRowIdentifier(String catalog,
+			String schema, final String table, int scope, boolean nullable)
+			throws SQLException {
+		if (table == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		Field[] fields = new Field[8];
+		fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5);
+		fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
+		fields[2] = new Field("", "DATA_TYPE", Types.SMALLINT, 32);
+		fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 32);
+		fields[4] = new Field("", "COLUMN_SIZE", Types.INTEGER, 10);
+		fields[5] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
+		fields[6] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
+		fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5);
+
+		final ArrayList rows = new ArrayList();
+		final Statement stmt = this.conn.getMetadataSafeStatement();
+
+		try {
+
+			new IterateBlock(getCatalogIterator(catalog)) {
+				void forEach(Object catalogStr) throws SQLException {
+					ResultSet results = null;
+
+					try {
+						StringBuffer queryBuf = new StringBuffer(
+								"SHOW COLUMNS FROM ");
+						queryBuf.append(quotedId);
+						queryBuf.append(table);
+						queryBuf.append(quotedId);
+						queryBuf.append(" FROM ");
+						queryBuf.append(quotedId);
+						queryBuf.append(catalogStr.toString());
+						queryBuf.append(quotedId);
+
+						results = stmt.executeQuery(queryBuf.toString());
+
+						while (results.next()) {
+							String keyType = results.getString("Key");
+
+							if (keyType != null) {
+								if (StringUtils.startsWithIgnoreCase(keyType,
+										"PRI")) {
+									byte[][] rowVal = new byte[8][];
+									rowVal[0] = Integer
+											.toString(
+													java.sql.DatabaseMetaData.bestRowSession)
+											.getBytes();
+									rowVal[1] = results.getBytes("Field");
+
+									String type = results.getString("Type");
+									int size = MysqlIO.getMaxBuf();
+									int decimals = 0;
+
+									/*
+									 * Parse the Type column from MySQL
+									 */
+									if (type.indexOf("enum") != -1) {
+										String temp = type.substring(type
+												.indexOf("("), type
+												.indexOf(")"));
+										java.util.StringTokenizer tokenizer = new java.util.StringTokenizer(
+												temp, ",");
+										int maxLength = 0;
+
+										while (tokenizer.hasMoreTokens()) {
+											maxLength = Math.max(maxLength,
+													(tokenizer.nextToken()
+															.length() - 2));
+										}
+
+										size = maxLength;
+										decimals = 0;
+										type = "enum";
+									} else if (type.indexOf("(") != -1) {
+										if (type.indexOf(",") != -1) {
+											size = Integer.parseInt(type
+													.substring(type
+															.indexOf("(") + 1,
+															type.indexOf(",")));
+											decimals = Integer.parseInt(type
+													.substring(type
+															.indexOf(",") + 1,
+															type.indexOf(")")));
+										} else {
+											size = Integer.parseInt(type
+													.substring(type
+															.indexOf("(") + 1,
+															type.indexOf(")")));
+										}
+
+										type = type.substring(0, type
+												.indexOf("("));
+									}
+
+									rowVal[2] = s2b(String.valueOf(MysqlDefs
+											.mysqlToJavaType(type)));
+									rowVal[3] = s2b(type);
+									rowVal[4] = Integer.toString(
+											size + decimals).getBytes();
+									rowVal[5] = Integer.toString(
+											size + decimals).getBytes();
+									rowVal[6] = Integer.toString(decimals)
+											.getBytes();
+									rowVal[7] = Integer
+											.toString(
+													java.sql.DatabaseMetaData.bestRowNotPseudo)
+											.getBytes();
+
+									rows.add(rowVal);
+								}
+							}
+						}
+
+					} finally {
+						if (results != null) {
+							try {
+								results.close();
+							} catch (Exception ex) {
+								;
+							}
+
+							results = null;
+						}
+					}
+				}
+			}.doForAll();
+		} finally {
+			if (stmt != null) {
+				stmt.close();
+			}
+		}
+
+		java.sql.ResultSet results = buildResultSet(fields, rows);
+
+		return results;
+
+	}
+
+	/*
+	 * * Each row in the ResultSet is a parameter desription or column
+	 * description with the following fields: <OL> <li> <B>PROCEDURE_CAT</B>
+	 * String => procedure catalog (may be null) </li> <li> <B>PROCEDURE_SCHEM</B>
+	 * String => procedure schema (may be null) </li> <li> <B>PROCEDURE_NAME</B>
+	 * String => procedure name </li> <li> <B>COLUMN_NAME</B> String =>
+	 * column/parameter name </li> <li> <B>COLUMN_TYPE</B> Short => kind of
+	 * column/parameter: <UL> <li> procedureColumnUnknown - nobody knows </li>
+	 * <li> procedureColumnIn - IN parameter </li> <li> procedureColumnInOut -
+	 * INOUT parameter </li> <li> procedureColumnOut - OUT parameter </li> <li>
+	 * procedureColumnReturn - procedure return value </li> <li>
+	 * procedureColumnResult - result column in ResultSet </li> </ul> </li> <li>
+	 * <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li> <li>
+	 * <B>TYPE_NAME</B> String => SQL type name </li> <li> <B>PRECISION</B>
+	 * int => precision </li> <li> <B>LENGTH</B> int => length in bytes of data
+	 * </li> <li> <B>SCALE</B> short => scale </li> <li> <B>RADIX</B> short =>
+	 * radix </li> <li> <B>NULLABLE</B> short => can it contain NULL? <UL> <li>
+	 * procedureNoNulls - does not allow NULL values </li> <li>
+	 * procedureNullable - allows NULL values </li> <li>
+	 * procedureNullableUnknown - nullability unknown </li> </ul> </li> <li>
+	 * <B>REMARKS</B> String => comment describing parameter/column </li> </ol>
+	 * </p> <P> <B>Note:</B> Some databases may not return the column
+	 * descriptions for a procedure. Additional columns beyond REMARKS can be
+	 * defined by the database. </p> @param catalog a catalog name; "" retrieves
+	 * those without a catalog @param schemaPattern a schema name pattern; ""
+	 * retrieves those without a schema @param procedureNamePattern a procedure
+	 * name pattern @param columnNamePattern a column name pattern @return
+	 * ResultSet each row is a stored procedure parameter or column description
+	 * @throws SQLException if a database access error occurs
+	 * 
+	 * @see #getSearchStringEscape
+	 */
+	private void getCallStmtParameterTypes(String catalog, String procName,
+			String parameterNamePattern, List resultRows) throws SQLException {
+		java.sql.Statement paramRetrievalStmt = null;
+		java.sql.ResultSet paramRetrievalRs = null;
+
+		if (parameterNamePattern == null) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				parameterNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Parameter/Column name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		byte[] procNameAsBytes = null;
+
+		try {
+			procNameAsBytes = procName.getBytes("UTF-8");
+		} catch (UnsupportedEncodingException ueEx) {
+			procNameAsBytes = s2b(procName);
+
+			// Set all fields to connection encoding
+		}
+
+		String quoteChar = getIdentifierQuoteString();
+
+		String storageDefnDelims = "(" + quoteChar;
+		String storageDefnClosures = ")" + quoteChar;
+
+		// First try 'select from mysql.proc, as this is easier to parse...
+		String parameterDef = null;
+		
+		try {
+			paramRetrievalStmt = this.conn.getMetadataSafeStatement();
+			
+			if (this.conn.lowerCaseTableNames() && catalog != null 
+					&& catalog.length() != 0) {
+				// Workaround for bug in server wrt. to 
+				// SHOW CREATE PROCEDURE not respecting
+				// lower-case table names
+				
+				String oldCatalog = this.conn.getCatalog();
+				ResultSet rs = null;
+				
+				try {
+					this.conn.setCatalog(catalog);
+					rs = paramRetrievalStmt.executeQuery("SELECT DATABASE()");
+					rs.next();
+					
+					catalog = rs.getString(1);
+					
+				} finally {
+					
+					this.conn.setCatalog(oldCatalog);
+					
+					if (rs != null) {
+						rs.close();
+					}
+				}
+			}
+			
+			if (paramRetrievalStmt.getMaxRows() != 0) {
+				paramRetrievalStmt.setMaxRows(0);
+			}
+
+			int dotIndex = -1;
+
+			if (!" ".equals(quoteChar)) {
+				dotIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
+						procName, ".", quoteChar.charAt(0), !this.conn
+								.isNoBackslashEscapesSet());
+			} else {
+				dotIndex = procName.indexOf(".");
+			}
+
+			String dbName = null;
+
+			if (dotIndex != -1 && (dotIndex + 1) < procName.length()) {
+				dbName = procName.substring(0, dotIndex);
+				procName = procName.substring(dotIndex + 1);
+			} else {
+				dbName = catalog;
+			}
+
+			StringBuffer procNameBuf = new StringBuffer();
+
+			if (dbName != null) {
+				if (!" ".equals(quoteChar) && !dbName.startsWith(quoteChar)) {
+					procNameBuf.append(quoteChar);
+				}
+
+				procNameBuf.append(dbName);
+
+				if (!" ".equals(quoteChar) && !dbName.startsWith(quoteChar)) {
+					procNameBuf.append(quoteChar);
+				}
+
+				procNameBuf.append(".");
+			}
+
+			boolean procNameIsNotQuoted = !procName.startsWith(quoteChar);
+
+			if (!" ".equals(quoteChar) && procNameIsNotQuoted) {
+				procNameBuf.append(quoteChar);
+			}
+
+			procNameBuf.append(procName);
+
+			if (!" ".equals(quoteChar) && procNameIsNotQuoted) {
+				procNameBuf.append(quoteChar);
+			}
+
+			boolean parsingFunction = false;
+
+			try {
+				paramRetrievalRs = paramRetrievalStmt
+						.executeQuery("SHOW CREATE PROCEDURE "
+								+ procNameBuf.toString());
+				parsingFunction = false;
+			} catch (SQLException sqlEx) {
+				paramRetrievalRs = paramRetrievalStmt
+						.executeQuery("SHOW CREATE FUNCTION "
+								+ procNameBuf.toString());
+				parsingFunction = true;
+			}
+
+			if (paramRetrievalRs.next()) {
+				String procedureDef = parsingFunction ? paramRetrievalRs
+						.getString("Create Function") : paramRetrievalRs
+						.getString("Create Procedure");
+
+				int openParenIndex = StringUtils
+						.indexOfIgnoreCaseRespectQuotes(0, procedureDef, "(",
+								quoteChar.charAt(0), !this.conn
+										.isNoBackslashEscapesSet());
+
+				String beforeBegin = null;
+
+				// Try and fudge this with the 'begin' statement
+				int beginIndex = 0;
+
+				if (!parsingFunction) {
+					beginIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
+							procedureDef, "\nbegin", quoteChar.charAt(0),
+							!this.conn.isNoBackslashEscapesSet());
+				} else {
+					// Grab the return column first, since it needs
+					// to go first in the output result set
+					int returnsIndex = StringUtils
+							.indexOfIgnoreCaseRespectQuotes(0, procedureDef,
+									" RETURNS ", quoteChar.charAt(0),
+									!this.conn.isNoBackslashEscapesSet());
+
+					beginIndex = StringUtils.indexOfIgnoreCaseRespectQuotes(
+							returnsIndex, procedureDef, "\nbegin", quoteChar
+									.charAt(0), !this.conn
+									.isNoBackslashEscapesSet());
+
+					if (beginIndex == -1) {
+						beginIndex = StringUtils
+								.indexOfIgnoreCaseRespectQuotes(0,
+										procedureDef, "\n",
+										quoteChar.charAt(0), !this.conn
+												.isNoBackslashEscapesSet());
+					}
+
+					// Okay, give up...
+
+					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);
+					}
+
+					String returnsDefn = procedureDef.substring(returnsIndex
+							+ "RETURNS ".length(), beginIndex);
+					TypeDescriptor returnDescriptor = new TypeDescriptor(
+							returnsDefn, null);
+
+					resultRows.add(convertTypeDescriptorToProcedureRow(
+							procNameAsBytes, "", false, false, true,
+							returnDescriptor));
+
+					beginIndex = returnsIndex; // further processing needs to
+					// look before "RETURNS" token
+				}
+
+				// 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)) {
+					// parse error?
+					throw SQLError.createSQLException(
+							"Internal error when parsing callable statement metadata",
+							SQLError.SQL_STATE_GENERAL_ERROR);
+				}
+
+				parameterDef = procedureDef.substring(openParenIndex + 1,
+						endParenIndex);
+			}
+		} finally {
+			SQLException sqlExRethrow = null;
+
+			if (paramRetrievalRs != null) {
+				try {
+					paramRetrievalRs.close();
+				} catch (SQLException sqlEx) {
+					sqlExRethrow = sqlEx;
+				}
+
+				paramRetrievalRs = null;
+			}
+
+			if (paramRetrievalStmt != null) {
+				try {
+					paramRetrievalStmt.close();
+				} catch (SQLException sqlEx) {
+					sqlExRethrow = sqlEx;
+				}
+
+				paramRetrievalStmt = null;
+			}
+
+			if (sqlExRethrow != null) {
+				throw sqlExRethrow;
+			}
+		}
+
+		if (parameterDef != null) {
+			List parseList = StringUtils.split(parameterDef, ",",
+					storageDefnDelims, storageDefnClosures, true);
+
+			int parseListLen = parseList.size();
+
+			for (int i = 0; i < parseListLen; i++) {
+				String declaration = (String) parseList.get(i);
+
+				if (declaration.trim().length() == 0) {
+					break; // no parameters actually declared, but whitespace spans lines
+				}
+				
+				StringTokenizer declarationTok = new StringTokenizer(
+						declaration, " \t");
+
+				String paramName = null;
+				boolean isOutParam = false;
+				boolean isInParam = false;
+
+				if (declarationTok.hasMoreTokens()) {
+					String possibleParamName = declarationTok.nextToken();
+
+					if (possibleParamName.equalsIgnoreCase("OUT")) {
+						isOutParam = true;
+
+						if (declarationTok.hasMoreTokens()) {
+							paramName = declarationTok.nextToken();
+						} else {
+							throw SQLError.createSQLException(
+									"Internal error when parsing callable statement metadata (missing parameter name)",
+									SQLError.SQL_STATE_GENERAL_ERROR);
+						}
+					} else if (possibleParamName.equalsIgnoreCase("INOUT")) {
+						isOutParam = true;
+						isInParam = true;
+
+						if (declarationTok.hasMoreTokens()) {
+							paramName = declarationTok.nextToken();
+						} else {
+							throw SQLError.createSQLException(
+									"Internal error when parsing callable statement metadata (missing parameter name)",
+									SQLError.SQL_STATE_GENERAL_ERROR);
+						}
+					} else if (possibleParamName.equalsIgnoreCase("IN")) {
+						isOutParam = false;
+						isInParam = true;
+
+						if (declarationTok.hasMoreTokens()) {
+							paramName = declarationTok.nextToken();
+						} else {
+							throw SQLError.createSQLException(
+									"Internal error when parsing callable statement metadata (missing parameter name)",
+									SQLError.SQL_STATE_GENERAL_ERROR);
+						}
+					} else {
+						isOutParam = false;
+						isInParam = true;
+
+						paramName = possibleParamName;
+					}
+
+					TypeDescriptor typeDesc = null;
+
+					if (declarationTok.hasMoreTokens()) {
+						StringBuffer typeInfoBuf = new StringBuffer(
+								declarationTok.nextToken());
+
+						while (declarationTok.hasMoreTokens()) {
+							typeInfoBuf.append(" ");
+							typeInfoBuf.append(declarationTok.nextToken());
+						}
+
+						String typeInfo = typeInfoBuf.toString();
+
+						typeDesc = new TypeDescriptor(typeInfo, null);
+					} else {
+						throw SQLError.createSQLException(
+								"Internal error when parsing callable statement metadata (missing parameter type)",
+								SQLError.SQL_STATE_GENERAL_ERROR);
+					}
+
+					int wildCompareRes = StringUtils.wildCompare(paramName,
+							parameterNamePattern);
+
+					if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) {
+						byte[][] row = convertTypeDescriptorToProcedureRow(
+								procNameAsBytes, paramName, isOutParam,
+								isInParam, false, typeDesc);
+
+						resultRows.add(row);
+					}
+				} else {
+					throw SQLError.createSQLException(
+							"Internal error when parsing callable statement metadata (unknown output from 'SHOW CREATE PROCEDURE')",
+							SQLError.SQL_STATE_GENERAL_ERROR);
+				}
+			}
+		} else {
+			// Is this an error? JDBC spec doesn't make it clear if stored
+			// procedure doesn't
+			// exist, is it an error....
+		}
+	}
+
+	/**
+	 * Parses the cascade option string and returns the DBMD constant that
+	 * represents it (for deletes)
+	 * 
+	 * @param cascadeOptions
+	 *            the comment from 'SHOW TABLE STATUS'
+	 * @return the DBMD constant that represents the cascade option
+	 */
+	private int getCascadeDeleteOption(String cascadeOptions) {
+		int onDeletePos = cascadeOptions.indexOf("ON DELETE");
+
+		if (onDeletePos != -1) {
+			String deleteOptions = cascadeOptions.substring(onDeletePos,
+					cascadeOptions.length());
+
+			if (deleteOptions.startsWith("ON DELETE CASCADE")) {
+				return java.sql.DatabaseMetaData.importedKeyCascade;
+			} else if (deleteOptions.startsWith("ON DELETE SET NULL")) {
+				return java.sql.DatabaseMetaData.importedKeySetNull;
+			} else if (deleteOptions.startsWith("ON DELETE RESTRICT")) {
+				return java.sql.DatabaseMetaData.importedKeyRestrict;
+			} else if (deleteOptions.startsWith("ON DELETE NO ACTION")) {
+				return java.sql.DatabaseMetaData.importedKeyNoAction;
+			}
+		}
+
+		return java.sql.DatabaseMetaData.importedKeyNoAction;
+	}
+
+	/**
+	 * Parses the cascade option string and returns the DBMD constant that
+	 * represents it (for Updates)
+	 * 
+	 * @param cascadeOptions
+	 *            the comment from 'SHOW TABLE STATUS'
+	 * @return the DBMD constant that represents the cascade option
+	 */
+	private int getCascadeUpdateOption(String cascadeOptions) {
+		int onUpdatePos = cascadeOptions.indexOf("ON UPDATE");
+
+		if (onUpdatePos != -1) {
+			String updateOptions = cascadeOptions.substring(onUpdatePos,
+					cascadeOptions.length());
+
+			if (updateOptions.startsWith("ON UPDATE CASCADE")) {
+				return java.sql.DatabaseMetaData.importedKeyCascade;
+			} else if (updateOptions.startsWith("ON UPDATE SET NULL")) {
+				return java.sql.DatabaseMetaData.importedKeySetNull;
+			} else if (updateOptions.startsWith("ON UPDATE RESTRICT")) {
+				return java.sql.DatabaseMetaData.importedKeyRestrict;
+			} else if (updateOptions.startsWith("ON UPDATE NO ACTION")) {
+				return java.sql.DatabaseMetaData.importedKeyNoAction;
+			}
+		}
+
+		return java.sql.DatabaseMetaData.importedKeyNoAction;
+	}
+
+	protected IteratorWithCleanup getCatalogIterator(String catalogSpec)
+			throws SQLException {
+		IteratorWithCleanup allCatalogsIter;
+		if (catalogSpec != null) {
+			if (!catalogSpec.equals("")) {
+				allCatalogsIter = new SingleStringIterator(catalogSpec);
+			} else {
+				// legacy mode of operation
+				allCatalogsIter = new SingleStringIterator(this.database);
+			}
+		} else if (this.conn.getNullCatalogMeansCurrent()) {
+			allCatalogsIter = new SingleStringIterator(this.database);
+		} else {
+			allCatalogsIter = new ResultSetIterator(getCatalogs(), 1);
+		}
+
+		return allCatalogsIter;
+	}
+
+	/**
+	 * Get the catalog names available in this database. The results are ordered
+	 * by catalog name.
+	 * <P>
+	 * The catalog column is:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => catalog name </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @return ResultSet each row has a single String column that is a catalog
+	 *         name
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.ResultSet getCatalogs() throws SQLException {
+		java.sql.ResultSet results = null;
+		java.sql.Statement stmt = null;
+
+		try {
+			stmt = this.conn.createStatement();
+			stmt.setEscapeProcessing(false);
+			results = stmt.executeQuery("SHOW DATABASES");
+
+			java.sql.ResultSetMetaData resultsMD = results.getMetaData();
+			Field[] fields = new Field[1];
+			fields[0] = new Field("", "TABLE_CAT", Types.VARCHAR, resultsMD
+					.getColumnDisplaySize(1));
+
+			ArrayList tuples = new ArrayList();
+
+			while (results.next()) {
+				byte[][] rowVal = new byte[1][];
+				rowVal[0] = results.getBytes(1);
+				tuples.add(rowVal);
+			}
+
+			return buildResultSet(fields, tuples);
+		} finally {
+			if (results != null) {
+				try {
+					results.close();
+				} catch (SQLException sqlEx) {
+					AssertionFailedException.shouldNotHappen(sqlEx);
+				}
+
+				results = null;
+			}
+
+			if (stmt != null) {
+				try {
+					stmt.close();
+				} catch (SQLException sqlEx) {
+					AssertionFailedException.shouldNotHappen(sqlEx);
+				}
+
+				stmt = null;
+			}
+		}
+	}
+
+	/**
+	 * What's the separator between catalog and table name?
+	 * 
+	 * @return the separator string
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getCatalogSeparator() throws SQLException {
+		return ".";
+	}
+
+	// ----------------------------------------------------------------------
+	// The following group of methods exposes various limitations
+	// based on the target database with the current driver.
+	// Unless otherwise specified, a result of zero means there is no
+	// limit, or the limit is not known.
+
+	/**
+	 * What's the database vendor's preferred term for "catalog"?
+	 * 
+	 * @return the vendor term
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getCatalogTerm() throws SQLException {
+		return "database";
+	}
+
+	/**
+	 * Get a description of the access rights for a table's columns.
+	 * <P>
+	 * Only privileges matching the column name criteria are returned. They are
+	 * ordered by COLUMN_NAME and PRIVILEGE.
+	 * </p>
+	 * <P>
+	 * Each privilige description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>GRANTOR</B> => grantor of access (may be null) </li>
+	 * <li> <B>GRANTEE</B> String => grantee of access </li>
+	 * <li> <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
+	 * REFRENCES, ...) </li>
+	 * <li> <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to
+	 * grant to others; "NO" if not; null if unknown </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @param columnNamePattern
+	 *            a column name pattern
+	 * @return ResultSet each row is a column privilege description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getSearchStringEscape
+	 */
+	public java.sql.ResultSet getColumnPrivileges(String catalog,
+			String schema, String table, String columnNamePattern)
+			throws SQLException {
+		Field[] fields = new Field[8];
+		fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
+		fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
+		fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
+		fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 64);
+		fields[4] = new Field("", "GRANTOR", Types.CHAR, 77);
+		fields[5] = new Field("", "GRANTEE", Types.CHAR, 77);
+		fields[6] = new Field("", "PRIVILEGE", Types.CHAR, 64);
+		fields[7] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
+
+		StringBuffer grantQuery = new StringBuffer(
+				"SELECT c.host, c.db, t.grantor, c.user, "
+						+ "c.table_name, c.column_name, c.column_priv "
+						+ "from mysql.columns_priv c, mysql.tables_priv t "
+						+ "where c.host = t.host and c.db = t.db and "
+						+ "c.table_name = t.table_name ");
+
+		if ((catalog != null) && (catalog.length() != 0)) {
+			grantQuery.append(" AND c.db='");
+			grantQuery.append(catalog);
+			grantQuery.append("' ");
+			;
+		}
+
+		grantQuery.append(" AND c.table_name ='");
+		grantQuery.append(table);
+		grantQuery.append("' AND c.column_name like '");
+		grantQuery.append(columnNamePattern);
+		grantQuery.append("'");
+
+		Statement stmt = null;
+		ResultSet results = null;
+		ArrayList grantRows = new ArrayList();
+
+		try {
+			stmt = this.conn.createStatement();
+			stmt.setEscapeProcessing(false);
+			results = stmt.executeQuery(grantQuery.toString());
+
+			while (results.next()) {
+				String host = results.getString(1);
+				String db = results.getString(2);
+				String grantor = results.getString(3);
+				String user = results.getString(4);
+
+				if ((user == null) || (user.length() == 0)) {
+					user = "%";
+				}
+
+				StringBuffer fullUser = new StringBuffer(user);
+
+				if ((host != null) && this.conn.getUseHostsInPrivileges()) {
+					fullUser.append("@");
+					fullUser.append(host);
+				}
+
+				String columnName = results.getString(6);
+				String allPrivileges = results.getString(7);
+
+				if (allPrivileges != null) {
+					allPrivileges = allPrivileges.toUpperCase(Locale.ENGLISH);
+
+					StringTokenizer st = new StringTokenizer(allPrivileges, ",");
+
+					while (st.hasMoreTokens()) {
+						String privilege = st.nextToken().trim();
+						byte[][] tuple = new byte[8][];
+						tuple[0] = s2b(db);
+						tuple[1] = null;
+						tuple[2] = s2b(table);
+						tuple[3] = s2b(columnName);
+
+						if (grantor != null) {
+							tuple[4] = s2b(grantor);
+						} else {
+							tuple[4] = null;
+						}
+
+						tuple[5] = s2b(fullUser.toString());
+						tuple[6] = s2b(privilege);
+						tuple[7] = null;
+						grantRows.add(tuple);
+					}
+				}
+			}
+		} finally {
+			if (results != null) {
+				try {
+					results.close();
+				} catch (Exception ex) {
+					;
+				}
+
+				results = null;
+			}
+
+			if (stmt != null) {
+				try {
+					stmt.close();
+				} catch (Exception ex) {
+					;
+				}
+
+				stmt = null;
+			}
+		}
+
+		return buildResultSet(fields, grantRows);
+	}
+
+	/**
+	 * Get a description of table columns available in a catalog.
+	 * <P>
+	 * Only column descriptions matching the catalog, schema, table and column
+	 * name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME
+	 * and ORDINAL_POSITION.
+	 * </p>
+	 * <P>
+	 * Each column description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li>
+	 * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
+	 * <li> <B>COLUMN_SIZE</B> int => column size. For char or date types this
+	 * is the maximum number of characters, for numeric or decimal types this is
+	 * precision. </li>
+	 * <li> <B>BUFFER_LENGTH</B> is not used. </li>
+	 * <li> <B>DECIMAL_DIGITS</B> int => the number of fractional digits </li>
+	 * <li> <B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2) </li>
+	 * <li> <B>NULLABLE</B> int => is NULL allowed?
+	 * <UL>
+	 * <li> columnNoNulls - might not allow NULL values </li>
+	 * <li> columnNullable - definitely allows NULL values </li>
+	 * <li> columnNullableUnknown - nullability unknown </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>REMARKS</B> String => comment describing column (may be null)
+	 * </li>
+	 * <li> <B>COLUMN_DEF</B> String => default value (may be null) </li>
+	 * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
+	 * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
+	 * <li> <B>CHAR_OCTET_LENGTH</B> int => for char types the maximum number
+	 * of bytes in the column </li>
+	 * <li> <B>ORDINAL_POSITION</B> int => index of column in table (starting
+	 * at 1) </li>
+	 * <li> <B>IS_NULLABLE</B> String => "NO" means column definitely does not
+	 * allow NULL values; "YES" means the column might allow NULL values. An
+	 * empty string means nobody knows. </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schemaPattern
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param tableNamePattern
+	 *            a table name pattern
+	 * @param columnNamePattern
+	 *            a column name pattern
+	 * @return ResultSet each row is a column description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getSearchStringEscape
+	 */
+	public java.sql.ResultSet getColumns(final String catalog,
+			final String schemaPattern, final String tableNamePattern,
+			String columnNamePattern) throws SQLException {
+
+		if (columnNamePattern == null) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				columnNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Column name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		final String colPattern = columnNamePattern;
+
+		Field[] fields = new Field[18];
+		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);
+		fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
+		fields[4] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
+		fields[5] = new Field("", "TYPE_NAME", Types.CHAR, 16);
+		fields[6] = new Field("", "COLUMN_SIZE", Types.INTEGER, Integer
+				.toString(Integer.MAX_VALUE).length());
+		fields[7] = new Field("", "BUFFER_LENGTH", Types.INTEGER, 10);
+		fields[8] = new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10);
+		fields[9] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10);
+		fields[10] = new Field("", "NULLABLE", Types.INTEGER, 10);
+		fields[11] = new Field("", "REMARKS", Types.CHAR, 0);
+		fields[12] = new Field("", "COLUMN_DEF", Types.CHAR, 0);
+		fields[13] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10);
+		fields[14] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10);
+		fields[15] = new Field("", "CHAR_OCTET_LENGTH", Types.INTEGER, Integer
+				.toString(Integer.MAX_VALUE).length());
+		fields[16] = new Field("", "ORDINAL_POSITION", Types.INTEGER, 10);
+		fields[17] = new Field("", "IS_NULLABLE", Types.CHAR, 3);
+
+		final ArrayList rows = new ArrayList();
+		final Statement stmt = this.conn.getMetadataSafeStatement();
+
+		try {
+
+			new IterateBlock(getCatalogIterator(catalog)) {
+				void forEach(Object catalogStr) throws SQLException {
+
+					ArrayList tableNameList = new ArrayList();
+
+					if (tableNamePattern == null) {
+						// Select from all tables
+						java.sql.ResultSet tables = null;
+
+						try {
+							tables = getTables(catalog, schemaPattern, "%",
+									new String[0]);
+
+							while (tables.next()) {
+								String tableNameFromList = tables
+										.getString("TABLE_NAME");
+								tableNameList.add(tableNameFromList);
+							}
+						} finally {
+							if (tables != null) {
+								try {
+									tables.close();
+								} catch (Exception sqlEx) {
+									AssertionFailedException
+											.shouldNotHappen(sqlEx);
+								}
+
+								tables = null;
+							}
+						}
+					} else {
+						java.sql.ResultSet tables = null;
+
+						try {
+							tables = getTables(catalog, schemaPattern,
+									tableNamePattern, new String[0]);
+
+							while (tables.next()) {
+								String tableNameFromList = tables
+										.getString("TABLE_NAME");
+								tableNameList.add(tableNameFromList);
+							}
+						} finally {
+							if (tables != null) {
+								try {
+									tables.close();
+								} catch (SQLException sqlEx) {
+									AssertionFailedException
+											.shouldNotHappen(sqlEx);
+								}
+
+								tables = null;
+							}
+						}
+					}
+
+					java.util.Iterator tableNames = tableNameList.iterator();
+
+					while (tableNames.hasNext()) {
+						String tableName = (String) tableNames.next();
+
+						ResultSet results = null;
+
+						try {
+							StringBuffer queryBuf = new StringBuffer("SHOW ");
+
+							if (conn.versionMeetsMinimum(4, 1, 0)) {
+								queryBuf.append("FULL ");
+							}
+
+							queryBuf.append("COLUMNS FROM ");
+							queryBuf.append(quotedId);
+							queryBuf.append(tableName);
+							queryBuf.append(quotedId);
+							queryBuf.append(" FROM ");
+							queryBuf.append(quotedId);
+							queryBuf.append(catalogStr.toString());
+							queryBuf.append(quotedId);
+							queryBuf.append(" LIKE '");
+							queryBuf.append(colPattern);
+							queryBuf.append("'");
+
+							// Return correct ordinals if column name pattern is
+							// not '%'
+							// Currently, MySQL doesn't show enough data to do
+							// this, so we do it the 'hard' way...Once _SYSTEM
+							// tables are in, this should be much easier
+							boolean fixUpOrdinalsRequired = false;
+							Map ordinalFixUpMap = null;
+
+							if (!colPattern.equals("%")) {
+								fixUpOrdinalsRequired = true;
+
+								StringBuffer fullColumnQueryBuf = new StringBuffer(
+										"SHOW ");
+
+								if (conn.versionMeetsMinimum(4, 1, 0)) {
+									fullColumnQueryBuf.append("FULL ");
+								}
+
+								fullColumnQueryBuf.append("COLUMNS FROM ");
+								fullColumnQueryBuf.append(quotedId);
+								fullColumnQueryBuf.append(tableName);
+								fullColumnQueryBuf.append(quotedId);
+								fullColumnQueryBuf.append(" FROM ");
+								fullColumnQueryBuf.append(quotedId);
+								fullColumnQueryBuf
+										.append(catalogStr.toString());
+								fullColumnQueryBuf.append(quotedId);
+
+								results = stmt.executeQuery(fullColumnQueryBuf
+										.toString());
+
+								ordinalFixUpMap = new HashMap();
+
+								int fullOrdinalPos = 1;
+
+								while (results.next()) {
+									String fullOrdColName = results
+											.getString("Field");
+
+									ordinalFixUpMap.put(fullOrdColName,
+											new Integer(fullOrdinalPos++));
+								}
+							}
+
+							results = stmt.executeQuery(queryBuf.toString());
+
+							int ordPos = 1;
+
+							while (results.next()) {
+								byte[][] rowVal = new byte[18][];
+								rowVal[0] = s2b(catalog); // TABLE_CAT
+								rowVal[1] = null; // TABLE_SCHEM (No schemas
+								// in MySQL)
+
+								rowVal[2] = s2b(tableName); // TABLE_NAME
+								rowVal[3] = results.getBytes("Field");
+
+								TypeDescriptor typeDesc = new TypeDescriptor(
+										results.getString("Type"), results
+												.getString("Null"));
+
+								rowVal[4] = Short.toString(typeDesc.dataType)
+										.getBytes();
+
+								// 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[9] = s2b(Integer
+										.toString(typeDesc.numPrecRadix));
+								rowVal[10] = s2b(Integer
+										.toString(typeDesc.nullability));
+
+								//
+								// Doesn't always have this field, depending on
+								// version
+								//
+								//
+								// REMARK column
+								//
+								try {
+									if (conn.versionMeetsMinimum(4, 1, 0)) {
+										rowVal[11] = results
+												.getBytes("Comment");
+									} else {
+										rowVal[11] = results.getBytes("Extra");
+									}
+								} catch (Exception E) {
+									rowVal[11] = new byte[0];
+								}
+
+								// COLUMN_DEF
+								rowVal[12] = results.getBytes("Default");
+
+								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
+
+								// ORDINAL_POSITION
+								if (!fixUpOrdinalsRequired) {
+									rowVal[16] = Integer.toString(ordPos++)
+											.getBytes();
+								} else {
+									String origColName = results
+											.getString("Field");
+									Integer realOrdinal = (Integer) ordinalFixUpMap
+											.get(origColName);
+
+									if (realOrdinal != null) {
+										rowVal[16] = realOrdinal.toString()
+												.getBytes();
+									} else {
+										throw SQLError.createSQLException(
+												"Can not find column in full column list to determine true ordinal position.",
+												SQLError.SQL_STATE_GENERAL_ERROR);
+									}
+								}
+
+								rowVal[17] = s2b(typeDesc.isNullable);
+
+								rows.add(rowVal);
+							}
+						} finally {
+							if (results != null) {
+								try {
+									results.close();
+								} catch (Exception ex) {
+									;
+								}
+
+								results = null;
+							}
+						}
+					}
+				}
+			}.doForAll();
+		} finally {
+			if (stmt != null) {
+				stmt.close();
+			}
+		}
+
+		java.sql.ResultSet results = buildResultSet(fields, rows);
+
+		return results;
+	}
+
+	/**
+	 * JDBC 2.0 Return the connection that produced this metadata object.
+	 * 
+	 * @return the connection that produced this metadata object.
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public java.sql.Connection getConnection() throws SQLException {
+		return this.conn;
+	}
+
+	/**
+	 * Get a description of the foreign key columns in the foreign key table
+	 * that reference the primary key columns of the primary key table (describe
+	 * how one table imports another's key.) This should normally return a
+	 * single foreign key/primary key pair (most tables only import a foreign
+	 * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
+	 * FKTABLE_NAME, and KEY_SEQ.
+	 * <P>
+	 * Each foreign key column description has the following columns:
+	 * <OL>
+	 * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be
+	 * null) </li>
+	 * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be
+	 * null) </li>
+	 * <li> <B>PKTABLE_NAME</B> String => primary key table name </li>
+	 * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li>
+	 * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
+	 * null) being exported (may be null) </li>
+	 * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
+	 * null) being exported (may be null) </li>
+	 * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported
+	 * </li>
+	 * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being
+	 * exported </li>
+	 * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
+	 * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
+	 * primary is updated:
+	 * <UL>
+	 * <li> importedKeyCascade - change imported key to agree with primary key
+	 * update </li>
+	 * <li> importedKeyRestrict - do not allow update of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been updated </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
+	 * primary is deleted.
+	 * <UL>
+	 * <li> importedKeyCascade - delete rows that import a deleted key </li>
+	 * <li> importedKeyRestrict - do not allow delete of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been deleted </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li>
+	 * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param primaryCatalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param primarySchema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param primaryTable
+	 *            a table name
+	 * @param foreignCatalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param foreignSchema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param foreignTable
+	 *            a table name
+	 * @return ResultSet each row is a foreign key column description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public java.sql.ResultSet getCrossReference(final String primaryCatalog,
+			final String primarySchema, final String primaryTable,
+			final String foreignCatalog, final String foreignSchema,
+			final String foreignTable) throws SQLException {
+		if (primaryTable == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		Field[] fields = new Field[14];
+		fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
+		fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
+		fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
+		fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
+		fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
+		fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
+		fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
+		fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
+		fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
+		fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
+		fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
+		fields[11] = new Field("", "FK_NAME", Types.CHAR, 0);
+		fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
+		fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
+
+		final ArrayList tuples = new ArrayList();
+
+		if (this.conn.versionMeetsMinimum(3, 23, 0)) {
+
+			final Statement stmt = this.conn.getMetadataSafeStatement();
+
+			try {
+
+				new IterateBlock(getCatalogIterator(foreignCatalog)) {
+					void forEach(Object catalogStr) throws SQLException {
+
+						ResultSet fkresults = null;
+
+						try {
+
+							/*
+							 * Get foreign key information for table
+							 */
+							if (conn.versionMeetsMinimum(3, 23, 50)) {
+								fkresults = extractForeignKeyFromCreateTable(
+										catalogStr.toString(), null);
+							} else {
+								StringBuffer queryBuf = new StringBuffer(
+										"SHOW TABLE STATUS FROM ");
+								queryBuf.append(quotedId);
+								queryBuf.append(catalogStr.toString());
+								queryBuf.append(quotedId);
+
+								fkresults = stmt.executeQuery(queryBuf
+										.toString());
+							}
+
+							String foreignTableWithCase = getTableNameWithCase(foreignTable);
+							String primaryTableWithCase = getTableNameWithCase(primaryTable);
+
+							/*
+							 * Parse imported foreign key information
+							 */
+
+							String dummy;
+
+							while (fkresults.next()) {
+								String tableType = fkresults.getString("Type");
+
+								if ((tableType != null)
+										&& (tableType
+												.equalsIgnoreCase("innodb") || tableType
+												.equalsIgnoreCase(SUPPORTS_FK))) {
+									String comment = fkresults.getString(
+											"Comment").trim();
+
+									if (comment != null) {
+										StringTokenizer commentTokens = new StringTokenizer(
+												comment, ";", false);
+
+										if (commentTokens.hasMoreTokens()) {
+											dummy = commentTokens.nextToken();
+
+											// Skip InnoDB comment
+										}
+
+										while (commentTokens.hasMoreTokens()) {
+											String keys = commentTokens
+													.nextToken();
+											LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keys);
+
+											int keySeq = 0;
+
+											Iterator referencingColumns = parsedInfo.localColumnsList
+													.iterator();
+											Iterator referencedColumns = parsedInfo.referencedColumnsList
+													.iterator();
+
+											while (referencingColumns.hasNext()) {
+												String referencingColumn = removeQuotedId(referencingColumns
+														.next().toString());
+
+												// one tuple for each table
+												// between
+												// parenthesis
+												byte[][] tuple = new byte[14][];
+												tuple[4] = ((foreignCatalog == null) ? null
+														: s2b(foreignCatalog));
+												tuple[5] = ((foreignSchema == null) ? null
+														: s2b(foreignSchema));
+												dummy = fkresults
+														.getString("Name"); // FKTABLE_NAME
+
+												if (dummy
+														.compareTo(foreignTableWithCase) != 0) {
+													continue;
+												}
+
+												tuple[6] = s2b(dummy);
+
+												tuple[7] = s2b(referencingColumn); // FKCOLUMN_NAME
+												tuple[0] = ((primaryCatalog == null) ? null
+														: s2b(primaryCatalog));
+												tuple[1] = ((primarySchema == null) ? null
+														: s2b(primarySchema));
+
+												// Skip foreign key if it
+												// doesn't refer to
+												// the right table
+												if (parsedInfo.referencedTable
+														.compareTo(primaryTableWithCase) != 0) {
+													continue;
+												}
+
+												tuple[2] = s2b(parsedInfo.referencedTable); // PKTABLE_NAME
+												tuple[3] = s2b(removeQuotedId(referencedColumns
+														.next().toString())); // PKCOLUMN_NAME
+												tuple[8] = Integer.toString(
+														keySeq).getBytes(); // KEY_SEQ
+
+												int[] actions = getForeignKeyActions(keys);
+
+												tuple[9] = Integer.toString(
+														actions[1]).getBytes();
+												tuple[10] = Integer.toString(
+														actions[0]).getBytes();
+												tuple[11] = null; // FK_NAME
+												tuple[12] = null; // PK_NAME
+												tuple[13] = Integer
+														.toString(
+																java.sql.DatabaseMetaData.importedKeyNotDeferrable)
+														.getBytes();
+												tuples.add(tuple);
+												keySeq++;
+											}
+										}
+									}
+								}
+							}
+
+						} finally {
+							if (fkresults != null) {
+								try {
+									fkresults.close();
+								} catch (Exception sqlEx) {
+									AssertionFailedException
+											.shouldNotHappen(sqlEx);
+								}
+
+								fkresults = null;
+							}
+						}
+					}
+				}.doForAll();
+			} finally {
+				if (stmt != null) {
+					stmt.close();
+				}
+			}
+		}
+
+		java.sql.ResultSet results = buildResultSet(fields, tuples);
+
+		return results;
+	}
+
+	/**
+	 * @see DatabaseMetaData#getDatabaseMajorVersion()
+	 */
+	public int getDatabaseMajorVersion() throws SQLException {
+		return this.conn.getServerMajorVersion();
+	}
+
+	/**
+	 * @see DatabaseMetaData#getDatabaseMinorVersion()
+	 */
+	public int getDatabaseMinorVersion() throws SQLException {
+		return this.conn.getServerMinorVersion();
+	}
+
+	/**
+	 * What's the name of this database product?
+	 * 
+	 * @return database product name
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getDatabaseProductName() throws SQLException {
+		return "MySQL";
+	}
+
+	/**
+	 * What's the version of this database product?
+	 * 
+	 * @return database version
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getDatabaseProductVersion() throws SQLException {
+		return this.conn.getServerVersion();
+	}
+
+	/**
+	 * What's the database's default transaction isolation level? The values are
+	 * defined in java.sql.Connection.
+	 * 
+	 * @return the default isolation level
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see Connection
+	 */
+	public int getDefaultTransactionIsolation() throws SQLException {
+		if (this.conn.supportsIsolationLevel()) {
+			return java.sql.Connection.TRANSACTION_READ_COMMITTED;
+		}
+
+		return java.sql.Connection.TRANSACTION_NONE;
+	}
+
+	/**
+	 * What's this JDBC driver's major version number?
+	 * 
+	 * @return JDBC driver major version
+	 */
+	public int getDriverMajorVersion() {
+		return NonRegisteringDriver.getMajorVersionInternal();
+	}
+
+	/**
+	 * What's this JDBC driver's minor version number?
+	 * 
+	 * @return JDBC driver minor version number
+	 */
+	public int getDriverMinorVersion() {
+		return NonRegisteringDriver.getMinorVersionInternal();
+	}
+
+	/**
+	 * What's the name of this JDBC driver?
+	 * 
+	 * @return JDBC driver name
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getDriverName() throws SQLException {
+		return "MySQL-AB JDBC Driver";
+	}
+
+	/**
+	 * What's the version of this JDBC driver?
+	 * 
+	 * @return JDBC driver version
+	 * @throws java.sql.SQLException
+	 *             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 $ )";
+	}
+
+	/**
+	 * Get a description of a foreign key columns that reference a table's
+	 * primary key columns (the foreign keys exported by a table). They are
+	 * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ.
+	 * <P>
+	 * Each foreign key column description has the following columns:
+	 * <OL>
+	 * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be
+	 * null) </li>
+	 * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be
+	 * null) </li>
+	 * <li> <B>PKTABLE_NAME</B> String => primary key table name </li>
+	 * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li>
+	 * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
+	 * null) being exported (may be null) </li>
+	 * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
+	 * null) being exported (may be null) </li>
+	 * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported
+	 * </li>
+	 * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being
+	 * exported </li>
+	 * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
+	 * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
+	 * primary is updated:
+	 * <UL>
+	 * <li> importedKeyCascade - change imported key to agree with primary key
+	 * update </li>
+	 * <li> importedKeyRestrict - do not allow update of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been updated </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
+	 * primary is deleted.
+	 * <UL>
+	 * <li> importedKeyCascade - delete rows that import a deleted key </li>
+	 * <li> importedKeyRestrict - do not allow delete of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been deleted </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li>
+	 * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @return ResultSet each row is a foreign key column description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getImportedKeys
+	 */
+	public java.sql.ResultSet getExportedKeys(String catalog, String schema,
+			final String table) throws SQLException {
+		if (table == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		Field[] fields = new Field[14];
+		fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
+		fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
+		fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
+		fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
+		fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
+		fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
+		fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
+		fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
+		fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
+		fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
+		fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
+		fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
+		fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
+		fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
+
+		final ArrayList rows = new ArrayList();
+
+		if (this.conn.versionMeetsMinimum(3, 23, 0)) {
+
+			final Statement stmt = this.conn.getMetadataSafeStatement();
+
+			try {
+
+				new IterateBlock(getCatalogIterator(catalog)) {
+					void forEach(Object catalogStr) throws SQLException {
+						ResultSet fkresults = null;
+
+						try {
+
+							/*
+							 * Get foreign key information for table
+							 */
+							if (conn.versionMeetsMinimum(3, 23, 50)) {
+								// we can use 'SHOW CREATE TABLE'
+
+								fkresults = extractForeignKeyFromCreateTable(
+										catalogStr.toString(), null);
+							} else {
+								StringBuffer queryBuf = new StringBuffer(
+										"SHOW TABLE STATUS FROM ");
+								queryBuf.append(quotedId);
+								queryBuf.append(catalogStr.toString());
+								queryBuf.append(quotedId);
+
+								fkresults = stmt.executeQuery(queryBuf
+										.toString());
+							}
+
+							// lower-case table name might be turned on
+							String tableNameWithCase = getTableNameWithCase(table);
+
+							/*
+							 * Parse imported foreign key information
+							 */
+
+							while (fkresults.next()) {
+								String tableType = fkresults.getString("Type");
+
+								if ((tableType != null)
+										&& (tableType
+												.equalsIgnoreCase("innodb") || tableType
+												.equalsIgnoreCase(SUPPORTS_FK))) {
+									String comment = fkresults.getString(
+											"Comment").trim();
+
+									if (comment != null) {
+										StringTokenizer commentTokens = new StringTokenizer(
+												comment, ";", false);
+
+										if (commentTokens.hasMoreTokens()) {
+											commentTokens.nextToken(); // Skip
+											// InnoDB
+											// comment
+
+											while (commentTokens
+													.hasMoreTokens()) {
+												String keys = commentTokens
+														.nextToken();
+												getExportKeyResults(
+														catalogStr.toString(),
+														tableNameWithCase,
+														keys,
+														rows,
+														fkresults
+																.getString("Name"));
+											}
+										}
+									}
+								}
+							}
+
+						} finally {
+							if (fkresults != null) {
+								try {
+									fkresults.close();
+								} catch (SQLException sqlEx) {
+									AssertionFailedException
+											.shouldNotHappen(sqlEx);
+								}
+
+								fkresults = null;
+							}
+						}
+					}
+				}.doForAll();
+			} finally {
+				if (stmt != null) {
+					stmt.close();
+				}
+			}
+		}
+
+		java.sql.ResultSet results = buildResultSet(fields, rows);
+
+		return results;
+	}
+
+	/**
+	 * Adds to the tuples list the exported keys of exportingTable based on the
+	 * keysComment from the 'show table status' sql command. KeysComment is that
+	 * part of the comment field that follows the "InnoDB free ...;" prefix.
+	 * 
+	 * @param catalog
+	 *            the database to use
+	 * @param exportingTable
+	 *            the table keys are being exported from
+	 * @param keysComment
+	 *            the comment from 'show table status'
+	 * @param tuples
+	 *            the rows to add results to
+	 * @param fkTableName
+	 *            the foreign key table name
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	private void getExportKeyResults(String catalog, String exportingTable,
+			String keysComment, List tuples, String fkTableName)
+			throws SQLException {
+		getResultsImpl(catalog, exportingTable, keysComment, tuples,
+				fkTableName, true);
+	}
+
+	/**
+	 * Get all the "extra" characters that can be used in unquoted identifier
+	 * names (those beyond a-z, 0-9 and _).
+	 * 
+	 * @return the string containing the extra characters
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getExtraNameCharacters() throws SQLException {
+		return "#@";
+	}
+
+	/**
+	 * Returns the DELETE and UPDATE foreign key actions from the given 'SHOW
+	 * TABLE STATUS' string, with the DELETE action being the first item in the
+	 * array, and the UPDATE action being the second.
+	 * 
+	 * @param commentString
+	 *            the comment from 'SHOW TABLE STATUS'
+	 * @return int[] [0] = delete action, [1] = update action
+	 */
+	private int[] getForeignKeyActions(String commentString) {
+		int[] actions = new int[] {
+				java.sql.DatabaseMetaData.importedKeyNoAction,
+				java.sql.DatabaseMetaData.importedKeyNoAction };
+
+		int lastParenIndex = commentString.lastIndexOf(")");
+
+		if (lastParenIndex != (commentString.length() - 1)) {
+			String cascadeOptions = commentString.substring(lastParenIndex + 1)
+					.trim().toUpperCase(Locale.ENGLISH);
+
+			actions[0] = getCascadeDeleteOption(cascadeOptions);
+			actions[1] = getCascadeUpdateOption(cascadeOptions);
+		}
+
+		return actions;
+	}
+
+	/**
+	 * What's the string used to quote SQL identifiers? This returns a space " "
+	 * if identifier quoting isn't supported. A JDBC compliant driver always
+	 * uses a double quote character.
+	 * 
+	 * @return the quoting string
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getIdentifierQuoteString() throws SQLException {
+		if (this.conn.supportsQuotedIdentifiers()) {
+			if (!this.conn.useAnsiQuotedIdentifiers()) {
+				return "`";
+			}
+
+			return "\"";
+		}
+
+		return " ";
+	}
+
+	/**
+	 * 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
+	 * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
+	 * <P>
+	 * Each primary key column description has the following columns:
+	 * <OL>
+	 * <li> <B>PKTABLE_CAT</B> String => primary key table catalog being
+	 * imported (may be null) </li>
+	 * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema being
+	 * imported (may be null) </li>
+	 * <li> <B>PKTABLE_NAME</B> String => primary key table name being imported
+	 * </li>
+	 * <li> <B>PKCOLUMN_NAME</B> String => primary key column name being
+	 * imported </li>
+	 * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
+	 * null) </li>
+	 * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
+	 * null) </li>
+	 * <li> <B>FKTABLE_NAME</B> String => foreign key table name </li>
+	 * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name </li>
+	 * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
+	 * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
+	 * primary is updated:
+	 * <UL>
+	 * <li> importedKeyCascade - change imported key to agree with primary key
+	 * update </li>
+	 * <li> importedKeyRestrict - do not allow update of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been updated </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
+	 * primary is deleted.
+	 * <UL>
+	 * <li> importedKeyCascade - delete rows that import a deleted key </li>
+	 * <li> importedKeyRestrict - do not allow delete of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been deleted </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>FK_NAME</B> String => foreign key name (may be null) </li>
+	 * <li> <B>PK_NAME</B> String => primary key name (may be null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @return ResultSet each row is a primary key column description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getExportedKeys
+	 */
+	public java.sql.ResultSet getImportedKeys(String catalog, String schema,
+			final String table) throws SQLException {
+		if (table == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		Field[] fields = new Field[14];
+		fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
+		fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
+		fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
+		fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
+		fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
+		fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
+		fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
+		fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
+		fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
+		fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
+		fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
+		fields[11] = new Field("", "FK_NAME", Types.CHAR, 255);
+		fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
+		fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
+
+		final ArrayList rows = new ArrayList();
+
+		if (this.conn.versionMeetsMinimum(3, 23, 0)) {
+
+			final Statement stmt = this.conn.getMetadataSafeStatement();
+
+			try {
+
+				new IterateBlock(getCatalogIterator(catalog)) {
+					void forEach(Object catalogStr) throws SQLException {
+						ResultSet fkresults = null;
+
+						try {
+
+							/*
+							 * Get foreign key information for table
+							 */
+							if (conn.versionMeetsMinimum(3, 23, 50)) {
+								// we can use 'SHOW CREATE TABLE'
+
+								fkresults = extractForeignKeyFromCreateTable(
+										catalogStr.toString(), table);
+							} else {
+								StringBuffer queryBuf = new StringBuffer(
+										"SHOW TABLE STATUS ");
+								queryBuf.append(" FROM ");
+								queryBuf.append(quotedId);
+								queryBuf.append(catalogStr.toString());
+								queryBuf.append(quotedId);
+								queryBuf.append(" LIKE '");
+								queryBuf.append(table);
+								queryBuf.append("'");
+
+								fkresults = stmt.executeQuery(queryBuf
+										.toString());
+							}
+
+							/*
+							 * Parse imported foreign key information
+							 */
+
+							while (fkresults.next()) {
+								String tableType = fkresults.getString("Type");
+
+								if ((tableType != null)
+										&& (tableType
+												.equalsIgnoreCase("innodb") || tableType
+												.equalsIgnoreCase(SUPPORTS_FK))) {
+									String comment = fkresults.getString(
+											"Comment").trim();
+
+									if (comment != null) {
+										StringTokenizer commentTokens = new StringTokenizer(
+												comment, ";", false);
+
+										if (commentTokens.hasMoreTokens()) {
+											commentTokens.nextToken(); // Skip
+											// InnoDB
+											// comment
+
+											while (commentTokens
+													.hasMoreTokens()) {
+												String keys = commentTokens
+														.nextToken();
+												getImportKeyResults(catalogStr
+														.toString(), table,
+														keys, rows);
+											}
+										}
+									}
+								}
+							}
+						} finally {
+							if (fkresults != null) {
+								try {
+									fkresults.close();
+								} catch (SQLException sqlEx) {
+									AssertionFailedException
+											.shouldNotHappen(sqlEx);
+								}
+
+								fkresults = null;
+							}
+						}
+					}
+				}.doForAll();
+			} finally {
+				if (stmt != null) {
+					stmt.close();
+				}
+			}
+		}
+
+		java.sql.ResultSet results = buildResultSet(fields, rows);
+
+		return results;
+	}
+
+	/**
+	 * Populates the tuples list with the imported keys of importingTable based
+	 * on the keysComment from the 'show table status' sql command. KeysComment
+	 * is that part of the comment field that follows the "InnoDB free ...;"
+	 * prefix.
+	 * 
+	 * @param catalog
+	 *            the database to use
+	 * @param importingTable
+	 *            the table keys are being imported to
+	 * @param keysComment
+	 *            the comment from 'show table status'
+	 * @param tuples
+	 *            the rows to add results to
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	private void getImportKeyResults(String catalog, String importingTable,
+			String keysComment, List tuples) throws SQLException {
+		getResultsImpl(catalog, importingTable, keysComment, tuples, null,
+				false);
+	}
+
+	/**
+	 * Get a description of a table's indices and statistics. They are ordered
+	 * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
+	 * <P>
+	 * Each index column description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>NON_UNIQUE</B> boolean => Can index values be non-unique? false
+	 * when TYPE is tableIndexStatistic </li>
+	 * <li> <B>INDEX_QUALIFIER</B> String => index catalog (may be null); null
+	 * when TYPE is tableIndexStatistic </li>
+	 * <li> <B>INDEX_NAME</B> String => index name; null when TYPE is
+	 * tableIndexStatistic </li>
+	 * <li> <B>TYPE</B> short => index type:
+	 * <UL>
+	 * <li> tableIndexStatistic - this identifies table statistics that are
+	 * returned in conjuction with a table's index descriptions </li>
+	 * <li> tableIndexClustered - this is a clustered index </li>
+	 * <li> tableIndexHashed - this is a hashed index </li>
+	 * <li> tableIndexOther - this is some other style of index </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>ORDINAL_POSITION</B> short => column sequence number within
+	 * index; zero when TYPE is tableIndexStatistic </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name; null when TYPE is
+	 * tableIndexStatistic </li>
+	 * <li> <B>ASC_OR_DESC</B> String => column sort sequence, "A" =>
+	 * ascending, "D" => descending, may be null if sort sequence is not
+	 * supported; null when TYPE is tableIndexStatistic </li>
+	 * <li> <B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then this
+	 * is the number of rows in the table; otherwise it is the number of unique
+	 * values in the index. </li>
+	 * <li> <B>PAGES</B> int => When TYPE is tableIndexStatisic then this is
+	 * the number of pages used for the table, otherwise it is the number of
+	 * pages used for the current index. </li>
+	 * <li> <B>FILTER_CONDITION</B> String => Filter condition, if any. (may be
+	 * null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @param unique
+	 *            when true, return only indices for unique values; when false,
+	 *            return indices regardless of whether unique or not
+	 * @param approximate
+	 *            when true, result is allowed to reflect approximate or out of
+	 *            data values; when false, results are requested to be accurate
+	 * @return ResultSet each row is an index column description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.ResultSet getIndexInfo(String catalog, String schema,
+			final String table, final boolean unique, boolean approximate)
+			throws SQLException {
+		/*
+		 * MySQL stores index information in the following fields: Table
+		 * Non_unique Key_name Seq_in_index Column_name Collation Cardinality
+		 * Sub_part
+		 */
+
+		Field[] fields = new Field[13];
+		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);
+		fields[3] = new Field("", "NON_UNIQUE", Types.CHAR, 4);
+		fields[4] = new Field("", "INDEX_QUALIFIER", Types.CHAR, 1);
+		fields[5] = new Field("", "INDEX_NAME", Types.CHAR, 32);
+		fields[6] = new Field("", "TYPE", Types.CHAR, 32);
+		fields[7] = new Field("", "ORDINAL_POSITION", Types.SMALLINT, 5);
+		fields[8] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
+		fields[9] = new Field("", "ASC_OR_DESC", Types.CHAR, 1);
+		fields[10] = new Field("", "CARDINALITY", Types.INTEGER, 10);
+		fields[11] = new Field("", "PAGES", Types.INTEGER, 10);
+		fields[12] = new Field("", "FILTER_CONDITION", Types.CHAR, 32);
+
+		final ArrayList rows = new ArrayList();
+		final Statement stmt = this.conn.getMetadataSafeStatement();
+
+		try {
+
+			new IterateBlock(getCatalogIterator(catalog)) {
+				void forEach(Object catalogStr) throws SQLException {
+
+					ResultSet results = null;
+
+					try {
+						StringBuffer queryBuf = new StringBuffer(
+								"SHOW INDEX FROM ");
+						queryBuf.append(quotedId);
+						queryBuf.append(table);
+						queryBuf.append(quotedId);
+						queryBuf.append(" FROM ");
+						queryBuf.append(quotedId);
+						queryBuf.append(catalogStr.toString());
+						queryBuf.append(quotedId);
+
+						try {
+							results = stmt.executeQuery(queryBuf.toString());
+						} catch (SQLException sqlEx) {
+							int errorCode = sqlEx.getErrorCode();
+
+							// If SQLState is 42S02, ignore this SQLException
+							// it means the table doesn't exist....
+							if (!"42S02".equals(sqlEx.getSQLState())) {
+								// Sometimes not mapped correctly for pre-4.1
+								// so use error code instead.
+								if (errorCode != MysqlErrorNumbers.ER_NO_SUCH_TABLE) {
+									throw sqlEx;
+								}
+							}
+						}
+
+						while (results != null && results.next()) {
+							byte[][] row = new byte[14][];
+							row[0] = ((catalogStr.toString() == null) ? new byte[0]
+									: s2b(catalogStr.toString()));
+							;
+							row[1] = null;
+							row[2] = results.getBytes("Table");
+
+							boolean indexIsUnique = results
+									.getInt("Non_unique") == 0;
+
+							row[3] = (!indexIsUnique ? s2b("true")
+									: s2b("false"));
+							row[4] = new byte[0];
+							row[5] = results.getBytes("Key_name");
+							row[6] = Integer.toString(
+									java.sql.DatabaseMetaData.tableIndexOther)
+									.getBytes();
+							row[7] = results.getBytes("Seq_in_index");
+							row[8] = results.getBytes("Column_name");
+							row[9] = results.getBytes("Collation");
+							row[10] = results.getBytes("Cardinality");
+							row[11] = s2b("0");
+							row[12] = null;
+
+							if (unique) {
+								if (indexIsUnique) {
+									rows.add(row);
+								}
+							} else {
+								// All rows match
+								rows.add(row);
+							}
+						}
+					} finally {
+						if (results != null) {
+							try {
+								results.close();
+							} catch (Exception ex) {
+								;
+							}
+
+							results = null;
+						}
+					}
+				}
+			}.doForAll();
+
+			java.sql.ResultSet indexInfo = buildResultSet(fields, rows);
+
+			return indexInfo;
+		} finally {
+			if (stmt != null) {
+				stmt.close();
+			}
+		}
+	}
+
+	/**
+	 * @see DatabaseMetaData#getJDBCMajorVersion()
+	 */
+	public int getJDBCMajorVersion() throws SQLException {
+		return 3;
+	}
+
+	/**
+	 * @see DatabaseMetaData#getJDBCMinorVersion()
+	 */
+	public int getJDBCMinorVersion() throws SQLException {
+		return 0;
+	}
+
+	/**
+	 * How many hex characters can you have in an inline binary literal?
+	 * 
+	 * @return max literal length
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxBinaryLiteralLength() throws SQLException {
+		return 16777208;
+	}
+
+	/**
+	 * What's the maximum length of a catalog name?
+	 * 
+	 * @return max name length in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxCatalogNameLength() throws SQLException {
+		return 32;
+	}
+
+	/**
+	 * What's the max length for a character literal?
+	 * 
+	 * @return max literal length
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxCharLiteralLength() throws SQLException {
+		return 16777208;
+	}
+
+	/**
+	 * What's the limit on column name length?
+	 * 
+	 * @return max literal length
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxColumnNameLength() throws SQLException {
+		return 64;
+	}
+
+	/**
+	 * What's the maximum number of columns in a "GROUP BY" clause?
+	 * 
+	 * @return max number of columns
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxColumnsInGroupBy() throws SQLException {
+		return 64;
+	}
+
+	/**
+	 * What's the maximum number of columns allowed in an index?
+	 * 
+	 * @return max columns
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxColumnsInIndex() throws SQLException {
+		return 16;
+	}
+
+	/**
+	 * What's the maximum number of columns in an "ORDER BY" clause?
+	 * 
+	 * @return max columns
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxColumnsInOrderBy() throws SQLException {
+		return 64;
+	}
+
+	/**
+	 * What's the maximum number of columns in a "SELECT" list?
+	 * 
+	 * @return max columns
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxColumnsInSelect() throws SQLException {
+		return 256;
+	}
+
+	/**
+	 * What's maximum number of columns in a table?
+	 * 
+	 * @return max columns
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxColumnsInTable() throws SQLException {
+		return 512;
+	}
+
+	/**
+	 * How many active connections can we have at a time to this database?
+	 * 
+	 * @return max connections
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxConnections() throws SQLException {
+		return 0;
+	}
+
+	/**
+	 * What's the maximum cursor name length?
+	 * 
+	 * @return max cursor name length in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxCursorNameLength() throws SQLException {
+		return 64;
+	}
+
+	/**
+	 * What's the maximum length of an index (in bytes)?
+	 * 
+	 * @return max index length in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxIndexLength() throws SQLException {
+		return 256;
+	}
+
+	/**
+	 * What's the maximum length of a procedure name?
+	 * 
+	 * @return max name length in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxProcedureNameLength() throws SQLException {
+		return 0;
+	}
+
+	/**
+	 * What's the maximum length of a single row?
+	 * 
+	 * @return max row size in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxRowSize() throws SQLException {
+		return Integer.MAX_VALUE - 8; // Max buffer size - HEADER
+	}
+
+	/**
+	 * What's the maximum length allowed for a schema name?
+	 * 
+	 * @return max name length in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxSchemaNameLength() throws SQLException {
+		return 0;
+	}
+
+	/**
+	 * What's the maximum length of a SQL statement?
+	 * 
+	 * @return max length in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxStatementLength() throws SQLException {
+		return MysqlIO.getMaxBuf() - 4; // Max buffer - header
+	}
+
+	/**
+	 * How many active statements can we have open at one time to this database?
+	 * 
+	 * @return the maximum
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxStatements() throws SQLException {
+		return 0;
+	}
+
+	/**
+	 * What's the maximum length of a table name?
+	 * 
+	 * @return max name length in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxTableNameLength() throws SQLException {
+		return 64;
+	}
+
+	/**
+	 * What's the maximum number of tables in a SELECT?
+	 * 
+	 * @return the maximum
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxTablesInSelect() throws SQLException {
+		return 256;
+	}
+
+	/**
+	 * What's the maximum length of a user name?
+	 * 
+	 * @return max name length in bytes
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public int getMaxUserNameLength() throws SQLException {
+		return 16;
+	}
+
+	/**
+	 * Get a comma separated list of math functions.
+	 * 
+	 * @return the list
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getNumericFunctions() throws SQLException {
+		return "ABS,ACOS,ASIN,ATAN,ATAN2,BIT_COUNT,CEILING,COS,"
+				+ "COT,DEGREES,EXP,FLOOR,LOG,LOG10,MAX,MIN,MOD,PI,POW,"
+				+ "POWER,RADIANS,RAND,ROUND,SIN,SQRT,TAN,TRUNCATE";
+	}
+
+	/**
+	 * Get a description of a table's primary key columns. They are ordered by
+	 * COLUMN_NAME.
+	 * <P>
+	 * Each column description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>KEY_SEQ</B> short => sequence number within primary key </li>
+	 * <li> <B>PK_NAME</B> String => primary key name (may be null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @return ResultSet each row is a primary key column description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.ResultSet getPrimaryKeys(String catalog, String schema,
+			final String table) throws SQLException {
+		Field[] fields = new Field[6];
+		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);
+		fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
+		fields[4] = new Field("", "KEY_SEQ", Types.SMALLINT, 5);
+		fields[5] = new Field("", "PK_NAME", Types.CHAR, 32);
+
+		if (table == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		final ArrayList rows = new ArrayList();
+		final Statement stmt = this.conn.getMetadataSafeStatement();
+
+		try {
+
+			new IterateBlock(getCatalogIterator(catalog)) {
+				void forEach(Object catalogStr) throws SQLException {
+					ResultSet rs = null;
+
+					try {
+
+						StringBuffer queryBuf = new StringBuffer(
+								"SHOW KEYS FROM ");
+						queryBuf.append(quotedId);
+						queryBuf.append(table);
+						queryBuf.append(quotedId);
+						queryBuf.append(" FROM ");
+						queryBuf.append(quotedId);
+						queryBuf.append(catalogStr.toString());
+						queryBuf.append(quotedId);
+
+						rs = stmt.executeQuery(queryBuf.toString());
+
+						ArrayList tuples = new ArrayList();
+						TreeMap sortMap = new TreeMap();
+
+						while (rs.next()) {
+							String keyType = rs.getString("Key_name");
+
+							if (keyType != null) {
+								if (keyType.equalsIgnoreCase("PRIMARY")
+										|| keyType.equalsIgnoreCase("PRI")) {
+									byte[][] tuple = new byte[6][];
+									tuple[0] = ((catalogStr.toString() == null) ? new byte[0]
+											: s2b(catalogStr.toString()));
+									tuple[1] = null;
+									tuple[2] = s2b(table);
+
+									String columnName = rs
+											.getString("Column_name");
+									tuple[3] = s2b(columnName);
+									tuple[4] = s2b(rs.getString("Seq_in_index"));
+									tuple[5] = s2b(keyType);
+									sortMap.put(columnName, tuple);
+								}
+							}
+						}
+
+						// Now pull out in column name sorted order
+						Iterator sortedIterator = sortMap.values().iterator();
+
+						while (sortedIterator.hasNext()) {
+							rows.add(sortedIterator.next());
+						}
+
+					} finally {
+						if (rs != null) {
+							try {
+								rs.close();
+							} catch (Exception ex) {
+								;
+							}
+
+							rs = null;
+						}
+					}
+				}
+			}.doForAll();
+		} finally {
+			if (stmt != null) {
+				stmt.close();
+			}
+		}
+
+		java.sql.ResultSet results = buildResultSet(fields, rows);
+
+		return results;
+	}
+
+	/**
+	 * Get a description of a catalog's stored procedure parameters and result
+	 * columns.
+	 * <P>
+	 * Only descriptions matching the schema, procedure and parameter name
+	 * criteria are returned. They are ordered by PROCEDURE_SCHEM and
+	 * PROCEDURE_NAME. Within this, the return value, if any, is first. Next are
+	 * the parameter descriptions in call order. The column descriptions follow
+	 * in column number order.
+	 * </p>
+	 * <P>
+	 * Each row in the ResultSet is a parameter desription or column description
+	 * with the following fields:
+	 * <OL>
+	 * <li> <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
+	 * </li>
+	 * <li> <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
+	 * </li>
+	 * <li> <B>PROCEDURE_NAME</B> String => procedure name </li>
+	 * <li> <B>COLUMN_NAME</B> String => column/parameter name </li>
+	 * <li> <B>COLUMN_TYPE</B> Short => kind of column/parameter:
+	 * <UL>
+	 * <li> procedureColumnUnknown - nobody knows </li>
+	 * <li> procedureColumnIn - IN parameter </li>
+	 * <li> procedureColumnInOut - INOUT parameter </li>
+	 * <li> procedureColumnOut - OUT parameter </li>
+	 * <li> procedureColumnReturn - procedure return value </li>
+	 * <li> procedureColumnResult - result column in ResultSet </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li>
+	 * <li> <B>TYPE_NAME</B> String => SQL type name </li>
+	 * <li> <B>PRECISION</B> int => precision </li>
+	 * <li> <B>LENGTH</B> int => length in bytes of data </li>
+	 * <li> <B>SCALE</B> short => scale </li>
+	 * <li> <B>RADIX</B> short => radix </li>
+	 * <li> <B>NULLABLE</B> short => can it contain NULL?
+	 * <UL>
+	 * <li> procedureNoNulls - does not allow NULL values </li>
+	 * <li> procedureNullable - allows NULL values </li>
+	 * <li> procedureNullableUnknown - nullability unknown </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>REMARKS</B> String => comment describing parameter/column </li>
+	 * </ol>
+	 * </p>
+	 * <P>
+	 * <B>Note:</B> Some databases may not return the column descriptions for a
+	 * procedure. Additional columns beyond REMARKS can be defined by the
+	 * database.
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schemaPattern
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param procedureNamePattern
+	 *            a procedure name pattern
+	 * @param columnNamePattern
+	 *            a column name pattern
+	 * @return ResultSet each row is a stored procedure parameter or column
+	 *         description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getSearchStringEscape
+	 */
+	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);
+		fields[1] = new Field("", "PROCEDURE_SCHEM", Types.CHAR, 0);
+		fields[2] = new Field("", "PROCEDURE_NAME", Types.CHAR, 0);
+		fields[3] = new Field("", "COLUMN_NAME", Types.CHAR, 0);
+		fields[4] = new Field("", "COLUMN_TYPE", Types.CHAR, 0);
+		fields[5] = new Field("", "DATA_TYPE", Types.SMALLINT, 0);
+		fields[6] = new Field("", "TYPE_NAME", Types.CHAR, 0);
+		fields[7] = new Field("", "PRECISION", Types.INTEGER, 0);
+		fields[8] = new Field("", "LENGTH", Types.INTEGER, 0);
+		fields[9] = new Field("", "SCALE", Types.SMALLINT, 0);
+		fields[10] = new Field("", "RADIX", Types.SMALLINT, 0);
+		fields[11] = new Field("", "NULLABLE", Types.SMALLINT, 0);
+		fields[12] = new Field("", "REMARKS", Types.CHAR, 0);
+
+		List proceduresToExtractList = new ArrayList();
+
+		if (supportsStoredProcedures()) {
+			if ((procedureNamePattern.indexOf("%") == -1)
+					&& (procedureNamePattern.indexOf("?") == -1)) {
+				proceduresToExtractList.add(procedureNamePattern);
+			} else {
+				
+				ResultSet procedureNameRs = null;
+
+				try {
+
+					procedureNameRs = getProcedures(catalog, schemaPattern,
+							procedureNamePattern);
+
+					while (procedureNameRs.next()) {
+						proceduresToExtractList.add(procedureNameRs
+								.getString(3));
+					}
+
+					// Required to be sorted in name-order by JDBC spec,
+					// in 'normal' case getProcedures takes care of this for us,
+					// but if system tables are inaccessible, we need to sort...
+					// so just do this to be safe...
+					Collections.sort(proceduresToExtractList);
+				} finally {
+					SQLException rethrowSqlEx = null;
+
+					if (procedureNameRs != null) {
+						try {
+							procedureNameRs.close();
+						} catch (SQLException sqlEx) {
+							rethrowSqlEx = sqlEx;
+						}
+					}
+					
+					if (rethrowSqlEx != null) {
+						throw rethrowSqlEx;
+					}
+				}
+			}
+		}
+
+		ArrayList resultRows = new ArrayList();
+
+		for (Iterator iter = proceduresToExtractList.iterator(); iter.hasNext();) {
+			String procName = (String) iter.next();
+
+			getCallStmtParameterTypes(catalog, procName, columnNamePattern,
+					resultRows);
+		}
+
+		return buildResultSet(fields, resultRows);
+	}
+
+	/**
+	 * Get a description of stored procedures available in a catalog.
+	 * <P>
+	 * Only procedure descriptions matching the schema and procedure name
+	 * criteria are returned. They are ordered by PROCEDURE_SCHEM, and
+	 * PROCEDURE_NAME.
+	 * </p>
+	 * <P>
+	 * Each procedure description has the the following columns:
+	 * <OL>
+	 * <li> <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
+	 * </li>
+	 * <li> <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
+	 * </li>
+	 * <li> <B>PROCEDURE_NAME</B> String => procedure name </li>
+	 * <li> reserved for future use </li>
+	 * <li> reserved for future use </li>
+	 * <li> reserved for future use </li>
+	 * <li> <B>REMARKS</B> String => explanatory comment on the procedure </li>
+	 * <li> <B>PROCEDURE_TYPE</B> short => kind of procedure:
+	 * <UL>
+	 * <li> procedureResultUnknown - May return a result </li>
+	 * <li> procedureNoResult - Does not return a result </li>
+	 * <li> procedureReturnsResult - Returns a result </li>
+	 * </ul>
+	 * </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schemaPattern
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param procedureNamePattern
+	 *            a procedure name pattern
+	 * @return ResultSet each row is a procedure description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getSearchStringEscape
+	 */
+	public java.sql.ResultSet getProcedures(String catalog,
+			String schemaPattern, String procedureNamePattern)
+			throws SQLException {
+		return getProceduresAndOrFunctions(catalog, schemaPattern,
+				procedureNamePattern, true, true);
+	}
+	
+	protected java.sql.ResultSet getProceduresAndOrFunctions(
+			String catalog,
+			String schemaPattern,
+			String procedureNamePattern,
+			final boolean returnProcedures,
+			final boolean returnFunctions) throws SQLException {
+		if ((procedureNamePattern == null)
+				|| (procedureNamePattern.length() == 0)) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				procedureNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Procedure name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		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()) {
+			final String procNamePattern = procedureNamePattern;
+
+			final Map procedureRowsOrderedByName = new TreeMap();
+
+			new IterateBlock(getCatalogIterator(catalog)) {
+				void forEach(Object catalogStr) throws SQLException {
+					String db = catalogStr.toString();
+
+					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");
+
+					try {
+						//
+						// Try using system tables first, as this is a little
+						// bit more efficient....
+						//
+
+						boolean hasTypeColumn = false;
+
+						if (db != null) {
+							proceduresStmt.setString(2, db);
+						} else {
+							proceduresStmt.setNull(2, Types.VARCHAR);
+						}
+
+						int nameIndex = 1;
+
+						if (proceduresStmt.getMaxRows() != 0) {
+							proceduresStmt.setMaxRows(0);
+						}
+
+						proceduresStmt.setString(1, procNamePattern);
+
+						try {
+							proceduresRs = proceduresStmt.executeQuery();
+							fromSelect = true;
+							needsClientFiltering = false;
+							hasTypeColumn = true;
+						} catch (SQLException sqlEx) {
+
+							//
+							// Okay, system tables aren't accessible, so use
+							// 'SHOW
+							// ....'....
+							//
+							proceduresStmt.close();
+
+							fromSelect = false;
+
+							if (conn.versionMeetsMinimum(5, 0, 1)) {
+								nameIndex = 2;
+							} else {
+								nameIndex = 1;
+							}
+
+							proceduresStmt = conn
+									.clientPrepareStatement("SHOW PROCEDURE STATUS LIKE ?");
+
+							if (proceduresStmt.getMaxRows() != 0) {
+								proceduresStmt.setMaxRows(0);
+							}
+
+							proceduresStmt.setString(1, procNamePattern);
+
+							proceduresRs = proceduresStmt.executeQuery();
+						}
+
+						if (returnProcedures) {
+							convertToJdbcProcedureList(fromSelect, db,
+								proceduresRs, needsClientFiltering, db,
+								procedureRowsOrderedByName, nameIndex);
+						}
+
+						if (!hasTypeColumn) {
+							// need to go after functions too...
+							if (proceduresStmt != null) {
+								proceduresStmt.close();
+							}
+
+							proceduresStmt = conn
+									.clientPrepareStatement("SHOW FUNCTION STATUS LIKE ?");
+
+							if (proceduresStmt.getMaxRows() != 0) {
+								proceduresStmt.setMaxRows(0);
+							}
+
+							proceduresStmt.setString(1, procNamePattern);
+
+							proceduresRs = proceduresStmt.executeQuery();
+
+							if (returnFunctions) {
+								convertToJdbcFunctionList(db, proceduresRs,
+									needsClientFiltering, db,
+									procedureRowsOrderedByName, nameIndex);
+							}
+						}
+
+						// Now, sort them
+
+						Iterator proceduresIter = procedureRowsOrderedByName
+								.values().iterator();
+
+						while (proceduresIter.hasNext()) {
+							procedureRows.add(proceduresIter.next());
+						}
+					} finally {
+						SQLException rethrowSqlEx = null;
+
+						if (proceduresRs != null) {
+							try {
+								proceduresRs.close();
+							} catch (SQLException sqlEx) {
+								rethrowSqlEx = sqlEx;
+							}
+						}
+
+						if (proceduresStmt != null) {
+							try {
+								proceduresStmt.close();
+							} catch (SQLException sqlEx) {
+								rethrowSqlEx = sqlEx;
+							}
+						}
+
+						if (rethrowSqlEx != null) {
+							throw rethrowSqlEx;
+						}
+					}
+				}
+			}.doForAll();
+		}
+
+		return buildResultSet(fields, procedureRows);
+	}
+
+	/**
+	 * What's the database vendor's preferred term for "procedure"?
+	 * 
+	 * @return the vendor term
+	 * @throws SQLException
+	 *             if an error occurs (don't know why it would in this case...)
+	 */
+	public String getProcedureTerm() throws SQLException {
+		return "PROCEDURE";
+	}
+
+	/**
+	 * @see DatabaseMetaData#getResultSetHoldability()
+	 */
+	public int getResultSetHoldability() throws SQLException {
+		return ResultSet.HOLD_CURSORS_OVER_COMMIT;
+	}
+
+	private void getResultsImpl(String catalog, String table,
+			String keysComment, List tuples, String fkTableName,
+			boolean isExport) throws SQLException {
+
+		LocalAndReferencedColumns parsedInfo = parseTableStatusIntoLocalAndReferencedColumns(keysComment);
+
+		if (isExport && !parsedInfo.referencedTable.equals(table)) {
+			return;
+		}
+
+		if (parsedInfo.localColumnsList.size() != parsedInfo.referencedColumnsList
+				.size()) {
+			throw SQLError.createSQLException(
+					"Error parsing foreign keys definition,"
+							+ "number of local and referenced columns is not the same.",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		Iterator localColumnNames = parsedInfo.localColumnsList.iterator();
+		Iterator referColumnNames = parsedInfo.referencedColumnsList.iterator();
+
+		int keySeqIndex = 1;
+
+		while (localColumnNames.hasNext()) {
+			byte[][] tuple = new byte[14][];
+			String lColumnName = removeQuotedId(localColumnNames.next()
+					.toString());
+			String rColumnName = removeQuotedId(referColumnNames.next()
+					.toString());
+			tuple[FKTABLE_CAT] = ((catalog == null) ? new byte[0]
+					: s2b(catalog));
+			tuple[FKTABLE_SCHEM] = null;
+			tuple[FKTABLE_NAME] = s2b((isExport) ? fkTableName : table);
+			tuple[FKCOLUMN_NAME] = s2b(lColumnName);
+			tuple[PKTABLE_CAT] = s2b(parsedInfo.referencedCatalog);
+			tuple[PKTABLE_SCHEM] = null;
+			tuple[PKTABLE_NAME] = s2b((isExport) ? table
+					: parsedInfo.referencedTable);
+			tuple[PKCOLUMN_NAME] = s2b(rColumnName);
+			tuple[KEY_SEQ] = s2b(Integer.toString(keySeqIndex++));
+
+			int[] actions = getForeignKeyActions(keysComment);
+
+			tuple[UPDATE_RULE] = s2b(Integer.toString(actions[1]));
+			tuple[DELETE_RULE] = s2b(Integer.toString(actions[0]));
+			tuple[FK_NAME] = s2b(parsedInfo.constraintName);
+			tuple[PK_NAME] = null; // not available from show table status
+			tuple[DEFERRABILITY] = s2b(Integer
+					.toString(java.sql.DatabaseMetaData.importedKeyNotDeferrable));
+			tuples.add(tuple);
+		}
+	}
+
+	/**
+	 * Get the schema names available in this database. The results are ordered
+	 * by schema name.
+	 * <P>
+	 * The schema column is:
+	 * <OL>
+	 * <li> <B>TABLE_SCHEM</B> String => schema name </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @return ResultSet each row has a single String column that is a schema
+	 *         name
+	 * @throws SQLException
+	 *             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);
+
+		ArrayList tuples = new ArrayList();
+		java.sql.ResultSet results = buildResultSet(fields, tuples);
+
+		return results;
+	}
+
+	/**
+	 * What's the database vendor's preferred term for "schema"?
+	 * 
+	 * @return the vendor term
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getSchemaTerm() throws SQLException {
+		return "";
+	}
+
+	/**
+	 * This is the string that can be used to escape '_' or '%' in the string
+	 * pattern style catalog search parameters.
+	 * <P>
+	 * The '_' character represents any single character.
+	 * </p>
+	 * <P>
+	 * The '%' character represents any sequence of zero or more characters.
+	 * </p>
+	 * 
+	 * @return the string used to escape wildcard characters
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getSearchStringEscape() throws SQLException {
+		return "\\";
+	}
+
+	/**
+	 * Get a comma separated list of all a database's SQL keywords that are NOT
+	 * also SQL92 keywords.
+	 * 
+	 * @return the list
+	 * @throws SQLException
+	 *             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
+		 */
+	}
+
+	/**
+	 * @see DatabaseMetaData#getSQLStateType()
+	 */
+	public int getSQLStateType() throws SQLException {
+		if (this.conn.versionMeetsMinimum(4, 1, 0)) {
+			return DatabaseMetaData.sqlStateSQL99;
+		}
+
+		if (this.conn.getUseSqlStateCodes()) {
+			return DatabaseMetaData.sqlStateSQL99;
+		}
+
+		return DatabaseMetaData.sqlStateXOpen;
+	}
+
+	/**
+	 * Get a comma separated list of string functions.
+	 * 
+	 * @return the list
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getStringFunctions() throws SQLException {
+		return "ASCII,BIN,BIT_LENGTH,CHAR,CHARACTER_LENGTH,CHAR_LENGTH,CONCAT,"
+				+ "CONCAT_WS,CONV,ELT,EXPORT_SET,FIELD,FIND_IN_SET,HEX,INSERT,"
+				+ "INSTR,LCASE,LEFT,LENGTH,LOAD_FILE,LOCATE,LOCATE,LOWER,LPAD,"
+				+ "LTRIM,MAKE_SET,MATCH,MID,OCT,OCTET_LENGTH,ORD,POSITION,"
+				+ "QUOTE,REPEAT,REPLACE,REVERSE,RIGHT,RPAD,RTRIM,SOUNDEX,"
+				+ "SPACE,STRCMP,SUBSTRING,SUBSTRING,SUBSTRING,SUBSTRING,"
+				+ "SUBSTRING_INDEX,TRIM,UCASE,UPPER";
+	}
+
+	/**
+	 * @see DatabaseMetaData#getSuperTables(String, String, String)
+	 */
+	public java.sql.ResultSet getSuperTables(String arg0, String arg1,
+			String arg2) throws SQLException {
+		Field[] fields = new Field[4];
+		fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32);
+		fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32);
+		fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 32);
+		fields[3] = new Field("", "SUPERTABLE_NAME", Types.CHAR, 32);
+
+		return buildResultSet(fields, new ArrayList());
+	}
+
+	/**
+	 * @see DatabaseMetaData#getSuperTypes(String, String, String)
+	 */
+	public java.sql.ResultSet getSuperTypes(String arg0, String arg1,
+			String arg2) throws SQLException {
+		Field[] fields = new Field[6];
+		fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 32);
+		fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 32);
+		fields[2] = new Field("", "TYPE_NAME", Types.CHAR, 32);
+		fields[3] = new Field("", "SUPERTYPE_CAT", Types.CHAR, 32);
+		fields[4] = new Field("", "SUPERTYPE_SCHEM", Types.CHAR, 32);
+		fields[5] = new Field("", "SUPERTYPE_NAME", Types.CHAR, 32);
+
+		return buildResultSet(fields, new ArrayList());
+	}
+
+	/**
+	 * Get a comma separated list of system functions.
+	 * 
+	 * @return the list
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getSystemFunctions() throws SQLException {
+		return "DATABASE,USER,SYSTEM_USER,SESSION_USER,PASSWORD,ENCRYPT,LAST_INSERT_ID,VERSION";
+	}
+
+	private String getTableNameWithCase(String table) {
+		String tableNameWithCase = (this.conn.lowerCaseTableNames() ? table
+				.toLowerCase() : table);
+
+		return tableNameWithCase;
+	}
+
+	/**
+	 * Get a description of the access rights for each table available in a
+	 * catalog.
+	 * <P>
+	 * Only privileges matching the schema and table name criteria are returned.
+	 * They are ordered by TABLE_SCHEM, TABLE_NAME, and PRIVILEGE.
+	 * </p>
+	 * <P>
+	 * Each privilige description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>GRANTOR</B> => grantor of access (may be null) </li>
+	 * <li> <B>GRANTEE</B> String => grantee of access </li>
+	 * <li> <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
+	 * REFRENCES, ...) </li>
+	 * <li> <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to
+	 * grant to others; "NO" if not; null if unknown </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schemaPattern
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param tableNamePattern
+	 *            a table name pattern
+	 * @return ResultSet each row is a table privilege description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getSearchStringEscape
+	 */
+	public java.sql.ResultSet getTablePrivileges(String catalog,
+			String schemaPattern, String tableNamePattern) throws SQLException {
+
+		if (tableNamePattern == null) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				tableNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Table name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		Field[] fields = new Field[7];
+		fields[0] = new Field("", "TABLE_CAT", Types.CHAR, 64);
+		fields[1] = new Field("", "TABLE_SCHEM", Types.CHAR, 1);
+		fields[2] = new Field("", "TABLE_NAME", Types.CHAR, 64);
+		fields[3] = new Field("", "GRANTOR", Types.CHAR, 77);
+		fields[4] = new Field("", "GRANTEE", Types.CHAR, 77);
+		fields[5] = new Field("", "PRIVILEGE", Types.CHAR, 64);
+		fields[6] = new Field("", "IS_GRANTABLE", Types.CHAR, 3);
+
+		StringBuffer grantQuery = new StringBuffer(
+				"SELECT host,db,table_name,grantor,user,table_priv from mysql.tables_priv ");
+		grantQuery.append(" WHERE ");
+
+		if ((catalog != null) && (catalog.length() != 0)) {
+			grantQuery.append(" db='");
+			grantQuery.append(catalog);
+			grantQuery.append("' AND ");
+		}
+
+		grantQuery.append("table_name like '");
+		grantQuery.append(tableNamePattern);
+		grantQuery.append("'");
+
+		ResultSet results = null;
+		ArrayList grantRows = new ArrayList();
+		Statement stmt = null;
+
+		try {
+			stmt = this.conn.createStatement();
+			stmt.setEscapeProcessing(false);
+
+			results = stmt.executeQuery(grantQuery.toString());
+
+			while (results.next()) {
+				String host = results.getString(1);
+				String db = results.getString(2);
+				String table = results.getString(3);
+				String grantor = results.getString(4);
+				String user = results.getString(5);
+
+				if ((user == null) || (user.length() == 0)) {
+					user = "%";
+				}
+
+				StringBuffer fullUser = new StringBuffer(user);
+
+				if ((host != null) && this.conn.getUseHostsInPrivileges()) {
+					fullUser.append("@");
+					fullUser.append(host);
+				}
+
+				String allPrivileges = results.getString(6);
+
+				if (allPrivileges != null) {
+					allPrivileges = allPrivileges.toUpperCase(Locale.ENGLISH);
+
+					StringTokenizer st = new StringTokenizer(allPrivileges, ",");
+
+					while (st.hasMoreTokens()) {
+						String privilege = st.nextToken().trim();
+
+						// Loop through every column in the table
+						java.sql.ResultSet columnResults = null;
+
+						try {
+							columnResults = getColumns(catalog, schemaPattern,
+									table, "%");
+
+							while (columnResults.next()) {
+								byte[][] tuple = new byte[8][];
+								tuple[0] = s2b(db);
+								tuple[1] = null;
+								tuple[2] = s2b(table);
+
+								if (grantor != null) {
+									tuple[3] = s2b(grantor);
+								} else {
+									tuple[3] = null;
+								}
+
+								tuple[4] = s2b(fullUser.toString());
+								tuple[5] = s2b(privilege);
+								tuple[6] = null;
+								grantRows.add(tuple);
+							}
+						} finally {
+							if (columnResults != null) {
+								try {
+									columnResults.close();
+								} catch (Exception ex) {
+									;
+								}
+							}
+						}
+					}
+				}
+			}
+		} finally {
+			if (results != null) {
+				try {
+					results.close();
+				} catch (Exception ex) {
+					;
+				}
+
+				results = null;
+			}
+
+			if (stmt != null) {
+				try {
+					stmt.close();
+				} catch (Exception ex) {
+					;
+				}
+
+				stmt = null;
+			}
+		}
+
+		return buildResultSet(fields, grantRows);
+	}
+
+	/**
+	 * Get a description of tables available in a catalog.
+	 * <P>
+	 * Only table descriptions matching the catalog, schema, table name and type
+	 * criteria are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and
+	 * TABLE_NAME.
+	 * </p>
+	 * <P>
+	 * Each table description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
+	 * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
+	 * "SYNONYM". </li>
+	 * <li> <B>REMARKS</B> String => explanatory comment on the table </li>
+	 * </ol>
+	 * </p>
+	 * <P>
+	 * <B>Note:</B> Some databases may not return information for all tables.
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schemaPattern
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param tableNamePattern
+	 *            a table name pattern
+	 * @param types
+	 *            a list of table types to include; null returns all types
+	 * @return ResultSet each row is a table description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 * @see #getSearchStringEscape
+	 */
+	public java.sql.ResultSet getTables(String catalog, String schemaPattern,
+			String tableNamePattern, final String[] types) throws SQLException {
+
+		if (tableNamePattern == null) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				tableNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Table name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		Field[] fields = new Field[5];
+		fields[0] = new Field("", "TABLE_CAT", java.sql.Types.VARCHAR, 255);
+		fields[1] = new Field("", "TABLE_SCHEM", java.sql.Types.VARCHAR, 0);
+		fields[2] = new Field("", "TABLE_NAME", java.sql.Types.VARCHAR, 255);
+		fields[3] = new Field("", "TABLE_TYPE", java.sql.Types.VARCHAR, 5);
+		fields[4] = new Field("", "REMARKS", java.sql.Types.VARCHAR, 0);
+
+		final ArrayList tuples = new ArrayList();
+
+		final Statement stmt = this.conn.getMetadataSafeStatement();
+
+		final String tableNamePat = tableNamePattern;
+
+		try {
+
+			new IterateBlock(getCatalogIterator(catalog)) {
+				void forEach(Object catalogStr) throws SQLException {
+					ResultSet results = null;
+
+					try {
+
+						if (!conn.versionMeetsMinimum(5, 0, 2)) {
+							try {
+								results = stmt
+									.executeQuery("SHOW TABLES FROM "
+											+ quotedId + catalogStr.toString()
+											+ quotedId + " LIKE '"
+											+ tableNamePat + "'");
+							} catch (SQLException sqlEx) {
+								if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) {
+									throw sqlEx;
+								}
+								
+								return;
+							}
+						} else {
+							try {
+								results = stmt
+									.executeQuery("SHOW FULL TABLES FROM "
+											+ quotedId + catalogStr.toString()
+											+ quotedId + " LIKE '"
+											+ tableNamePat + "'");
+							} catch (SQLException sqlEx) {
+								if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx.getSQLState())) {
+									throw sqlEx;
+								}
+								
+								return;
+							}
+						}
+
+						boolean shouldReportTables = false;
+						boolean shouldReportViews = false;
+
+						if (types == null || types.length == 0) {
+							shouldReportTables = true;
+							shouldReportViews = true;
+						} else {
+							for (int i = 0; i < types.length; i++) {
+								if ("TABLE".equalsIgnoreCase(types[i])) {
+									shouldReportTables = true;
+								}
+
+								if ("VIEW".equalsIgnoreCase(types[i])) {
+									shouldReportViews = true;
+								}
+							}
+						}
+
+						int typeColumnIndex = 0;
+						boolean hasTableTypes = false;
+
+						if (conn.versionMeetsMinimum(5, 0, 2)) {
+							try {
+								// Both column names have been in use in the
+								// source tree
+								// so far....
+								typeColumnIndex = results
+										.findColumn("table_type");
+								hasTableTypes = true;
+							} catch (SQLException sqlEx) {
+
+								// We should probably check SQLState here, but
+								// that
+								// can change depending on the server version
+								// and
+								// user properties, however, we'll get a 'true'
+								// SQLException when we actually try to find the
+								// 'Type' column
+								// 
+								try {
+									typeColumnIndex = results
+											.findColumn("Type");
+									hasTableTypes = true;
+								} catch (SQLException sqlEx2) {
+									hasTableTypes = false;
+								}
+							}
+						}
+
+						TreeMap tablesOrderedByName = null;
+						TreeMap viewsOrderedByName = null;
+
+						while (results.next()) {
+							byte[][] row = new byte[5][];
+							row[0] = (catalogStr.toString() == null) ? null
+									: s2b(catalogStr.toString());
+							row[1] = null;
+							row[2] = results.getBytes(1);
+							row[4] = new byte[0];
+
+							if (hasTableTypes) {
+								String tableType = results
+										.getString(typeColumnIndex);
+
+								if (("table".equalsIgnoreCase(tableType) || "base table"
+										.equalsIgnoreCase(tableType))
+										&& shouldReportTables) {
+									row[3] = TABLE_AS_BYTES;
+
+									if (tablesOrderedByName == null) {
+										tablesOrderedByName = new TreeMap();
+									}
+
+									tablesOrderedByName.put(results
+											.getString(1), row);
+								} else if ("view".equalsIgnoreCase(tableType)
+										&& shouldReportViews) {
+									row[3] = VIEW_AS_BYTES;
+
+									if (viewsOrderedByName == null) {
+										viewsOrderedByName = new TreeMap();
+									}
+
+									viewsOrderedByName.put(
+											results.getString(1), row);
+								} else if (!hasTableTypes) {
+									// punt?
+									row[3] = TABLE_AS_BYTES;
+
+									if (tablesOrderedByName == null) {
+										tablesOrderedByName = new TreeMap();
+									}
+
+									tablesOrderedByName.put(results
+											.getString(1), row);
+								}
+							} else {
+								if (shouldReportTables) {
+									// Pre-MySQL-5.0.1, tables only
+									row[3] = TABLE_AS_BYTES;
+
+									if (tablesOrderedByName == null) {
+										tablesOrderedByName = new TreeMap();
+									}
+
+									tablesOrderedByName.put(results
+											.getString(1), row);
+								}
+							}
+						}
+
+						// They are ordered by TABLE_TYPE,
+						// * TABLE_SCHEM and TABLE_NAME.
+
+						if (tablesOrderedByName != null) {
+							Iterator tablesIter = tablesOrderedByName.values()
+									.iterator();
+
+							while (tablesIter.hasNext()) {
+								tuples.add(tablesIter.next());
+							}
+						}
+
+						if (viewsOrderedByName != null) {
+							Iterator viewsIter = viewsOrderedByName.values()
+									.iterator();
+
+							while (viewsIter.hasNext()) {
+								tuples.add(viewsIter.next());
+							}
+						}
+
+					} finally {
+						if (results != null) {
+							try {
+								results.close();
+							} catch (Exception ex) {
+								;
+							}
+
+							results = null;
+						}
+
+					}
+				}
+			}.doForAll();
+		} finally {
+			if (stmt != null) {
+				stmt.close();
+			}
+		}
+
+		java.sql.ResultSet tables = buildResultSet(fields, tuples);
+
+		return tables;
+	}
+
+	/**
+	 * Get the table types available in this database. The results are ordered
+	 * by table type.
+	 * <P>
+	 * The table type is:
+	 * <OL>
+	 * <li> <B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
+	 * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
+	 * "SYNONYM". </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @return ResultSet each row has a single String column that is a table
+	 *         type
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.ResultSet getTableTypes() throws SQLException {
+		ArrayList tuples = new ArrayList();
+		Field[] fields = new Field[1];
+		fields[0] = new Field("", "TABLE_TYPE", Types.VARCHAR, 5);
+
+		byte[][] tableTypeRow = new byte[1][];
+		tableTypeRow[0] = TABLE_AS_BYTES;
+		tuples.add(tableTypeRow);
+
+		if (this.conn.versionMeetsMinimum(5, 0, 1)) {
+			byte[][] viewTypeRow = new byte[1][];
+			viewTypeRow[0] = VIEW_AS_BYTES;
+			tuples.add(viewTypeRow);
+		}
+
+		byte[][] tempTypeRow = new byte[1][];
+		tempTypeRow[0] = s2b("LOCAL TEMPORARY");
+		tuples.add(tempTypeRow);
+
+		return buildResultSet(fields, tuples);
+	}
+
+	/**
+	 * Get a comma separated list of time and date functions.
+	 * 
+	 * @return the list
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getTimeDateFunctions() throws SQLException {
+		return "DAYOFWEEK,WEEKDAY,DAYOFMONTH,DAYOFYEAR,MONTH,DAYNAME,"
+				+ "MONTHNAME,QUARTER,WEEK,YEAR,HOUR,MINUTE,SECOND,PERIOD_ADD,"
+				+ "PERIOD_DIFF,TO_DAYS,FROM_DAYS,DATE_FORMAT,TIME_FORMAT,"
+				+ "CURDATE,CURRENT_DATE,CURTIME,CURRENT_TIME,NOW,SYSDATE,"
+				+ "CURRENT_TIMESTAMP,UNIX_TIMESTAMP,FROM_UNIXTIME,"
+				+ "SEC_TO_TIME,TIME_TO_SEC";
+	}
+
+	/**
+	 * Get a description of all the standard SQL types supported by this
+	 * database. They are ordered by DATA_TYPE and then by how closely the data
+	 * type maps to the corresponding JDBC SQL type.
+	 * <P>
+	 * Each type description has the following columns:
+	 * <OL>
+	 * <li> <B>TYPE_NAME</B> String => Type name </li>
+	 * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
+	 * <li> <B>PRECISION</B> int => maximum precision </li>
+	 * <li> <B>LITERAL_PREFIX</B> String => prefix used to quote a literal (may
+	 * be null) </li>
+	 * <li> <B>LITERAL_SUFFIX</B> String => suffix used to quote a literal (may
+	 * be null) </li>
+	 * <li> <B>CREATE_PARAMS</B> String => parameters used in creating the type
+	 * (may be null) </li>
+	 * <li> <B>NULLABLE</B> short => can you use NULL for this type?
+	 * <UL>
+	 * <li> typeNoNulls - does not allow NULL values </li>
+	 * <li> typeNullable - allows NULL values </li>
+	 * <li> typeNullableUnknown - nullability unknown </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>CASE_SENSITIVE</B> boolean=> is it case sensitive? </li>
+	 * <li> <B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
+	 * <UL>
+	 * <li> typePredNone - No support </li>
+	 * <li> typePredChar - Only supported with WHERE .. LIKE </li>
+	 * <li> typePredBasic - Supported except for WHERE .. LIKE </li>
+	 * <li> typeSearchable - Supported for all WHERE .. </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned? </li>
+	 * <li> <B>FIXED_PREC_SCALE</B> boolean => can it be a money value? </li>
+	 * <li> <B>AUTO_INCREMENT</B> boolean => can it be used for an
+	 * auto-increment value? </li>
+	 * <li> <B>LOCAL_TYPE_NAME</B> String => localized version of type name
+	 * (may be null) </li>
+	 * <li> <B>MINIMUM_SCALE</B> short => minimum scale supported </li>
+	 * <li> <B>MAXIMUM_SCALE</B> short => maximum scale supported </li>
+	 * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
+	 * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
+	 * <li> <B>NUM_PREC_RADIX</B> int => usually 2 or 10 </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @return ResultSet each row is a SQL type description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	/**
+	 * Get a description of all the standard SQL types supported by this
+	 * database. They are ordered by DATA_TYPE and then by how closely the data
+	 * type maps to the corresponding JDBC SQL type.
+	 * <P>
+	 * Each type description has the following columns:
+	 * <OL>
+	 * <li> <B>TYPE_NAME</B> String => Type name </li>
+	 * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
+	 * <li> <B>PRECISION</B> int => maximum precision </li>
+	 * <li> <B>LITERAL_PREFIX</B> String => prefix used to quote a literal (may
+	 * be null) </li>
+	 * <li> <B>LITERAL_SUFFIX</B> String => suffix used to quote a literal (may
+	 * be null) </li>
+	 * <li> <B>CREATE_PARAMS</B> String => parameters used in creating the type
+	 * (may be null) </li>
+	 * <li> <B>NULLABLE</B> short => can you use NULL for this type?
+	 * <UL>
+	 * <li> typeNoNulls - does not allow NULL values </li>
+	 * <li> typeNullable - allows NULL values </li>
+	 * <li> typeNullableUnknown - nullability unknown </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>CASE_SENSITIVE</B> boolean=> is it case sensitive? </li>
+	 * <li> <B>SEARCHABLE</B> short => can you use "WHERE" based on this type:
+	 * <UL>
+	 * <li> typePredNone - No support </li>
+	 * <li> typePredChar - Only supported with WHERE .. LIKE </li>
+	 * <li> typePredBasic - Supported except for WHERE .. LIKE </li>
+	 * <li> typeSearchable - Supported for all WHERE .. </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>UNSIGNED_ATTRIBUTE</B> boolean => is it unsigned? </li>
+	 * <li> <B>FIXED_PREC_SCALE</B> boolean => can it be a money value? </li>
+	 * <li> <B>AUTO_INCREMENT</B> boolean => can it be used for an
+	 * auto-increment value? </li>
+	 * <li> <B>LOCAL_TYPE_NAME</B> String => localized version of type name
+	 * (may be null) </li>
+	 * <li> <B>MINIMUM_SCALE</B> short => minimum scale supported </li>
+	 * <li> <B>MAXIMUM_SCALE</B> short => maximum scale supported </li>
+	 * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
+	 * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
+	 * <li> <B>NUM_PREC_RADIX</B> int => usually 2 or 10 </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @return ResultSet each row is a SQL type description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.ResultSet getTypeInfo() throws SQLException {
+		Field[] fields = new Field[18];
+		fields[0] = new Field("", "TYPE_NAME", Types.CHAR, 32);
+		fields[1] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
+		fields[2] = new Field("", "PRECISION", Types.INTEGER, 10);
+		fields[3] = new Field("", "LITERAL_PREFIX", Types.CHAR, 4);
+		fields[4] = new Field("", "LITERAL_SUFFIX", Types.CHAR, 4);
+		fields[5] = new Field("", "CREATE_PARAMS", Types.CHAR, 32);
+		fields[6] = new Field("", "NULLABLE", Types.SMALLINT, 5);
+		fields[7] = new Field("", "CASE_SENSITIVE", Types.CHAR, 3);
+		fields[8] = new Field("", "SEARCHABLE", Types.SMALLINT, 3);
+		fields[9] = new Field("", "UNSIGNED_ATTRIBUTE", Types.CHAR, 3);
+		fields[10] = new Field("", "FIXED_PREC_SCALE", Types.CHAR, 3);
+		fields[11] = new Field("", "AUTO_INCREMENT", Types.CHAR, 3);
+		fields[12] = new Field("", "LOCAL_TYPE_NAME", Types.CHAR, 32);
+		fields[13] = new Field("", "MINIMUM_SCALE", Types.SMALLINT, 5);
+		fields[14] = new Field("", "MAXIMUM_SCALE", Types.SMALLINT, 5);
+		fields[15] = new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10);
+		fields[16] = new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10);
+		fields[17] = new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10);
+
+		byte[][] rowVal = null;
+		ArrayList tuples = new ArrayList();
+
+		/*
+		 * The following are ordered by java.sql.Types, and then by how closely
+		 * the MySQL type matches the JDBC Type (per spec)
+		 */
+		/*
+		 * MySQL Type: BIT (silently converted to TINYINT(1)) JDBC Type: BIT
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("BIT");
+		rowVal[1] = Integer.toString(java.sql.Types.BIT).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("1"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b(""); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("BIT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: BOOL (silently converted to TINYINT(1)) JDBC Type: BIT
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("BOOL");
+		rowVal[1] = Integer.toString(java.sql.Types.BIT).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("1"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b(""); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("BOOL"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: TINYINT JDBC Type: TINYINT
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("TINYINT");
+		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"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: BIGINT JDBC Type: BIGINT
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("BIGINT");
+		rowVal[1] = Integer.toString(java.sql.Types.BIGINT).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("19"); // 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("BIGINT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: LONG VARBINARY JDBC Type: LONGVARBINARY
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("LONG VARBINARY");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("16777215"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("LONG VARBINARY"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: MEDIUMBLOB JDBC Type: LONGVARBINARY
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("MEDIUMBLOB");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("16777215"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("MEDIUMBLOB"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: LONGBLOB JDBC Type: LONGVARBINARY
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("LONGBLOB");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = Integer.toString(Integer.MAX_VALUE).getBytes();
+
+		// Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("LONGBLOB"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: BLOB JDBC Type: LONGVARBINARY
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("BLOB");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("65535"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("BLOB"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: TINYBLOB JDBC Type: LONGVARBINARY
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("TINYBLOB");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARBINARY).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("255"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("TINYBLOB"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: VARBINARY (sliently converted to VARCHAR(M) BINARY) JDBC
+		 * Type: VARBINARY
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("VARBINARY");
+		rowVal[1] = Integer.toString(java.sql.Types.VARBINARY).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("255"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b("(M)"); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("VARBINARY"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: BINARY (silently converted to CHAR(M) BINARY) JDBC Type:
+		 * BINARY
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("BINARY");
+		rowVal[1] = Integer.toString(java.sql.Types.BINARY).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("255"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b("(M)"); // Create Params
+		rowVal[6] = Integer.toString(java.sql.DatabaseMetaData.typeNullable)
+				.getBytes();
+
+		// Nullable
+		rowVal[7] = s2b("true"); // Case Sensitive
+		rowVal[8] = Integer.toString(java.sql.DatabaseMetaData.typeSearchable)
+				.getBytes();
+
+		// Searchable
+		rowVal[9] = s2b("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("BINARY"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: LONG VARCHAR JDBC Type: LONGVARCHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("LONG VARCHAR");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("16777215"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("LONG VARCHAR"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: MEDIUMTEXT JDBC Type: LONGVARCHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("MEDIUMTEXT");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("16777215"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("MEDIUMTEXT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: LONGTEXT JDBC Type: LONGVARCHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("LONGTEXT");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = Integer.toString(Integer.MAX_VALUE).getBytes();
+
+		// Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("LONGTEXT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: TEXT JDBC Type: LONGVARCHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("TEXT");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("65535"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("TEXT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: TINYTEXT JDBC Type: LONGVARCHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("TINYTEXT");
+		rowVal[1] = Integer.toString(java.sql.Types.LONGVARCHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("255"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("TINYTEXT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: CHAR JDBC Type: CHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("CHAR");
+		rowVal[1] = Integer.toString(java.sql.Types.CHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("255"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b("(M)"); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("CHAR"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: NUMERIC (silently converted to DECIMAL) JDBC Type:
+		 * NUMERIC
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("NUMERIC");
+		rowVal[1] = Integer.toString(java.sql.Types.NUMERIC).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("17"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M[,D])] [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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("NUMERIC"); // Locale Type Name
+		rowVal[13] = s2b("-308"); // Minimum Scale
+		rowVal[14] = s2b("308"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: DECIMAL JDBC Type: DECIMAL
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("DECIMAL");
+		rowVal[1] = Integer.toString(java.sql.Types.DECIMAL).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("17"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M[,D])] [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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("DECIMAL"); // Locale Type Name
+		rowVal[13] = s2b("-308"); // Minimum Scale
+		rowVal[14] = s2b("308"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: INTEGER JDBC Type: INTEGER
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("INTEGER");
+		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)] [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("INTEGER"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: INT JDBC Type: INTEGER
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("INT");
+		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)] [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("INT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: MEDIUMINT JDBC Type: INTEGER
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("MEDIUMINT");
+		rowVal[1] = Integer.toString(java.sql.Types.INTEGER).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("7"); // 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("MEDIUMINT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: SMALLINT JDBC Type: SMALLINT
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("SMALLINT");
+		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)] [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("SMALLINT"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: FLOAT JDBC Type: REAL (this is the SINGLE PERCISION
+		 * floating point type)
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("FLOAT");
+		rowVal[1] = Integer.toString(java.sql.Types.REAL).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("10"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M,D)] [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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("FLOAT"); // Locale Type Name
+		rowVal[13] = s2b("-38"); // Minimum Scale
+		rowVal[14] = s2b("38"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: DOUBLE JDBC Type: DOUBLE
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("DOUBLE");
+		rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("17"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M,D)] [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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("DOUBLE"); // Locale Type Name
+		rowVal[13] = s2b("-308"); // Minimum Scale
+		rowVal[14] = s2b("308"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: DOUBLE PRECISION JDBC Type: DOUBLE
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("DOUBLE PRECISION");
+		rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("17"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M,D)] [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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("DOUBLE PRECISION"); // Locale Type Name
+		rowVal[13] = s2b("-308"); // Minimum Scale
+		rowVal[14] = s2b("308"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: REAL (does not map to Types.REAL) JDBC Type: DOUBLE
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("REAL");
+		rowVal[1] = Integer.toString(java.sql.Types.DOUBLE).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("17"); // Precision
+		rowVal[3] = s2b(""); // Literal Prefix
+		rowVal[4] = s2b(""); // Literal Suffix
+		rowVal[5] = s2b("[(M,D)] [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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("true"); // Auto Increment
+		rowVal[12] = s2b("REAL"); // Locale Type Name
+		rowVal[13] = s2b("-308"); // Minimum Scale
+		rowVal[14] = s2b("308"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: VARCHAR JDBC Type: VARCHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("VARCHAR");
+		rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("255"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b("(M)"); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("VARCHAR"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: ENUM JDBC Type: VARCHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("ENUM");
+		rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("65535"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("ENUM"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: SET JDBC Type: VARCHAR
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("SET");
+		rowVal[1] = Integer.toString(java.sql.Types.VARCHAR).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("64"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("SET"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: DATE JDBC Type: DATE
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("DATE");
+		rowVal[1] = Integer.toString(java.sql.Types.DATE).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("0"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("DATE"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: TIME JDBC Type: TIME
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("TIME");
+		rowVal[1] = Integer.toString(java.sql.Types.TIME).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("0"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("TIME"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: DATETIME JDBC Type: TIMESTAMP
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("DATETIME");
+		rowVal[1] = Integer.toString(java.sql.Types.TIMESTAMP).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("0"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b(""); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("DATETIME"); // 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(rowVal);
+
+		/*
+		 * MySQL Type: TIMESTAMP JDBC Type: TIMESTAMP
+		 */
+		rowVal = new byte[18][];
+		rowVal[0] = s2b("TIMESTAMP");
+		rowVal[1] = Integer.toString(java.sql.Types.TIMESTAMP).getBytes();
+
+		// JDBC Data type
+		rowVal[2] = s2b("0"); // Precision
+		rowVal[3] = s2b("'"); // Literal Prefix
+		rowVal[4] = s2b("'"); // Literal Suffix
+		rowVal[5] = s2b("[(M)]"); // 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("false"); // Unsignable
+		rowVal[10] = s2b("false"); // Fixed Prec Scale
+		rowVal[11] = s2b("false"); // Auto Increment
+		rowVal[12] = s2b("TIMESTAMP"); // 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(rowVal);
+
+		return buildResultSet(fields, tuples);
+	}
+
+	/**
+	 * JDBC 2.0 Get a description of the user-defined types defined in a
+	 * particular schema. Schema specific UDTs may have type JAVA_OBJECT,
+	 * STRUCT, or DISTINCT.
+	 * <P>
+	 * Only types matching the catalog, schema, type name and type criteria are
+	 * returned. They are ordered by DATA_TYPE, TYPE_SCHEM and TYPE_NAME. The
+	 * type name parameter may be a fully qualified name. In this case, the
+	 * catalog and schemaPattern parameters are ignored.
+	 * </p>
+	 * <P>
+	 * Each type description has the following columns:
+	 * <OL>
+	 * <li> <B>TYPE_CAT</B> String => the type's catalog (may be null) </li>
+	 * <li> <B>TYPE_SCHEM</B> String => type's schema (may be null) </li>
+	 * <li> <B>TYPE_NAME</B> String => type name </li>
+	 * <li> <B>CLASS_NAME</B> String => Java class name </li>
+	 * <li> <B>DATA_TYPE</B> String => type value defined in java.sql.Types.
+	 * One of JAVA_OBJECT, STRUCT, or DISTINCT </li>
+	 * <li> <B>REMARKS</B> String => explanatory comment on the type </li>
+	 * </ol>
+	 * </p>
+	 * <P>
+	 * <B>Note:</B> If the driver does not support UDTs then an empty result
+	 * set is returned.
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog; null
+	 *            means drop catalog name from the selection criteria
+	 * @param schemaPattern
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param typeNamePattern
+	 *            a type name pattern; may be a fully qualified name
+	 * @param types
+	 *            a list of user-named types to include (JAVA_OBJECT, STRUCT, or
+	 *            DISTINCT); null returns all types
+	 * @return ResultSet - each row is a type description
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.ResultSet getUDTs(String catalog, String schemaPattern,
+			String typeNamePattern, int[] types) throws SQLException {
+		Field[] fields = new Field[6];
+		fields[0] = new Field("", "TYPE_CAT", Types.VARCHAR, 32);
+		fields[1] = new Field("", "TYPE_SCHEM", Types.VARCHAR, 32);
+		fields[2] = new Field("", "TYPE_NAME", Types.VARCHAR, 32);
+		fields[3] = new Field("", "CLASS_NAME", Types.VARCHAR, 32);
+		fields[4] = new Field("", "DATA_TYPE", Types.VARCHAR, 32);
+		fields[5] = new Field("", "REMARKS", Types.VARCHAR, 32);
+
+		ArrayList tuples = new ArrayList();
+
+		return buildResultSet(fields, tuples);
+	}
+
+	/**
+	 * What's the url for this database?
+	 * 
+	 * @return the url or null if it can't be generated
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getURL() throws SQLException {
+		return this.conn.getURL();
+	}
+
+	/**
+	 * What's our user name as known to the database?
+	 * 
+	 * @return our database user name
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public String getUserName() throws SQLException {
+		if (this.conn.getUseHostsInPrivileges()) {
+			Statement stmt = null;
+			ResultSet rs = null;
+
+			try {
+				stmt = this.conn.createStatement();
+				stmt.setEscapeProcessing(false);
+
+				rs = stmt.executeQuery("SELECT USER()");
+				rs.next();
+
+				return rs.getString(1);
+			} finally {
+				if (rs != null) {
+					try {
+						rs.close();
+					} catch (Exception ex) {
+						AssertionFailedException.shouldNotHappen(ex);
+					}
+
+					rs = null;
+				}
+
+				if (stmt != null) {
+					try {
+						stmt.close();
+					} catch (Exception ex) {
+						AssertionFailedException.shouldNotHappen(ex);
+					}
+
+					stmt = null;
+				}
+			}
+		}
+
+		return this.conn.getUser();
+	}
+
+	/**
+	 * Get a description of a table's columns that are automatically updated
+	 * when any value in a row is updated. They are unordered.
+	 * <P>
+	 * Each column description has the following columns:
+	 * <OL>
+	 * <li> <B>SCOPE</B> short => is not used </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>DATA_TYPE</B> short => SQL data type from java.sql.Types </li>
+	 * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
+	 * <li> <B>COLUMN_SIZE</B> int => precision </li>
+	 * <li> <B>BUFFER_LENGTH</B> int => length of column value in bytes </li>
+	 * <li> <B>DECIMAL_DIGITS</B> short => scale </li>
+	 * <li> <B>PSEUDO_COLUMN</B> short => is this a pseudo column like an
+	 * Oracle ROWID
+	 * <UL>
+	 * <li> versionColumnUnknown - may or may not be pseudo column </li>
+	 * <li> versionColumnNotPseudo - is NOT a pseudo column </li>
+	 * <li> versionColumnPseudo - is a pseudo column </li>
+	 * </ul>
+	 * </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @return ResultSet each row is a column description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.ResultSet getVersionColumns(String catalog, String schema,
+			String table) throws SQLException {
+		Field[] fields = new Field[8];
+		fields[0] = new Field("", "SCOPE", Types.SMALLINT, 5);
+		fields[1] = new Field("", "COLUMN_NAME", Types.CHAR, 32);
+		fields[2] = new Field("", "DATA_TYPE", Types.SMALLINT, 5);
+		fields[3] = new Field("", "TYPE_NAME", Types.CHAR, 16);
+		fields[4] = new Field("", "COLUMN_SIZE", Types.CHAR, 16);
+		fields[5] = new Field("", "BUFFER_LENGTH", Types.CHAR, 16);
+		fields[6] = new Field("", "DECIMAL_DIGITS", Types.CHAR, 16);
+		fields[7] = new Field("", "PSEUDO_COLUMN", Types.SMALLINT, 5);
+
+		return buildResultSet(fields, new ArrayList());
+
+		// do TIMESTAMP columns count?
+	}
+
+	/**
+	 * JDBC 2.0 Determine whether or not a visible row insert can be detected by
+	 * calling ResultSet.rowInserted().
+	 * 
+	 * @param type
+	 *            set type, i.e. ResultSet.TYPE_XXX
+	 * @return true if changes are detected by the resultset type
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean insertsAreDetected(int type) throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Does a catalog appear at the start of a qualified table name? (Otherwise
+	 * it appears at the end)
+	 * 
+	 * @return true if it appears at the start
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean isCatalogAtStart() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is the database in read-only mode?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean isReadOnly() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * @see DatabaseMetaData#locatorsUpdateCopy()
+	 */
+	public boolean locatorsUpdateCopy() throws SQLException {
+		return !this.conn.getEmulateLocators();
+	}
+
+	/**
+	 * Are concatenations between NULL and non-NULL values NULL? A JDBC
+	 * compliant driver always returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean nullPlusNonNullIsNull() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Are NULL values sorted at the end regardless of sort order?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean nullsAreSortedAtEnd() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Are NULL values sorted at the start regardless of sort order?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean nullsAreSortedAtStart() throws SQLException {
+		return (this.conn.versionMeetsMinimum(4, 0, 2) && !this.conn
+				.versionMeetsMinimum(4, 0, 11));
+	}
+
+	/**
+	 * Are NULL values sorted high?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean nullsAreSortedHigh() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Are NULL values sorted low?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean nullsAreSortedLow() throws SQLException {
+		return !nullsAreSortedHigh();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param type
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean othersDeletesAreVisible(int type) throws SQLException {
+		return false;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param type
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean othersInsertsAreVisible(int type) throws SQLException {
+		return false;
+	}
+
+	/**
+	 * JDBC 2.0 Determine whether changes made by others are visible.
+	 * 
+	 * @param type
+	 *            set type, i.e. ResultSet.TYPE_XXX
+	 * @return true if changes are visible for the result set type
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean othersUpdatesAreVisible(int type) throws SQLException {
+		return false;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param type
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean ownDeletesAreVisible(int type) throws SQLException {
+		return false;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param type
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean ownInsertsAreVisible(int type) throws SQLException {
+		return false;
+	}
+
+	/**
+	 * JDBC 2.0 Determine whether a result set's own changes visible.
+	 * 
+	 * @param type
+	 *            set type, i.e. ResultSet.TYPE_XXX
+	 * @return true if changes are visible for the result set type
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean ownUpdatesAreVisible(int type) throws SQLException {
+		return false;
+	}
+
+	private LocalAndReferencedColumns parseTableStatusIntoLocalAndReferencedColumns(
+			String keysComment) throws SQLException {
+		// keys will equal something like this:
+		// (parent_service_id child_service_id) REFER
+		// ds/subservices(parent_service_id child_service_id)
+		//
+		// simple-columned keys: (m) REFER
+		// airline/tt(a)
+		//
+		// multi-columned keys : (m n) REFER
+		// airline/vv(a b)
+		//
+		// parse of the string into three phases:
+		// 1: parse the opening parentheses to determine how many results there
+		// will be
+		// 2: read in the schema name/table name
+		// 3: parse the closing parentheses
+
+		String columnsDelimitter = ","; // what version did this change in?
+
+		char quoteChar = this.quotedId.length() == 0 ? 0 : this.quotedId
+				.charAt(0);
+
+		int indexOfOpenParenLocalColumns = StringUtils
+				.indexOfIgnoreCaseRespectQuotes(0, keysComment, "(", quoteChar,
+						true);
+
+		if (indexOfOpenParenLocalColumns == -1) {
+			throw SQLError.createSQLException("Error parsing foreign keys definition,"
+					+ " couldn't find start of local columns list.",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		String constraintName = removeQuotedId(keysComment.substring(0,
+				indexOfOpenParenLocalColumns).trim());
+		keysComment = keysComment.substring(indexOfOpenParenLocalColumns,
+				keysComment.length());
+
+		String keysCommentTrimmed = keysComment.trim();
+
+		int indexOfCloseParenLocalColumns = StringUtils
+				.indexOfIgnoreCaseRespectQuotes(0, keysCommentTrimmed, ")",
+						quoteChar, true);
+
+		if (indexOfCloseParenLocalColumns == -1) {
+			throw SQLError.createSQLException("Error parsing foreign keys definition,"
+					+ " couldn't find end of local columns list.",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		String localColumnNamesString = keysCommentTrimmed.substring(1,
+				indexOfCloseParenLocalColumns);
+
+		int indexOfRefer = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
+				keysCommentTrimmed, "REFER ", this.quotedId.charAt(0), true);
+
+		if (indexOfRefer == -1) {
+			throw SQLError.createSQLException("Error parsing foreign keys definition,"
+					+ " couldn't find start of referenced tables list.",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		int indexOfOpenParenReferCol = StringUtils
+				.indexOfIgnoreCaseRespectQuotes(indexOfRefer,
+						keysCommentTrimmed, "(", quoteChar, false);
+
+		if (indexOfOpenParenReferCol == -1) {
+			throw SQLError.createSQLException("Error parsing foreign keys definition,"
+					+ " couldn't find start of referenced columns list.",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		String referCatalogTableString = keysCommentTrimmed.substring(
+				indexOfRefer + "REFER ".length(), indexOfOpenParenReferCol);
+
+		int indexOfSlash = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
+				referCatalogTableString, "/", this.quotedId.charAt(0), false);
+
+		if (indexOfSlash == -1) {
+			throw SQLError.createSQLException("Error parsing foreign keys definition,"
+					+ " couldn't find name of referenced catalog.",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		String referCatalog = removeQuotedId(referCatalogTableString.substring(
+				0, indexOfSlash));
+		String referTable = removeQuotedId(referCatalogTableString.substring(
+				indexOfSlash + 1).trim());
+
+		int indexOfCloseParenRefer = StringUtils
+				.indexOfIgnoreCaseRespectQuotes(indexOfOpenParenReferCol,
+						keysCommentTrimmed, ")", quoteChar, true);
+
+		if (indexOfCloseParenRefer == -1) {
+			throw SQLError.createSQLException("Error parsing foreign keys definition,"
+					+ " couldn't find end of referenced columns list.",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		String referColumnNamesString = keysCommentTrimmed.substring(
+				indexOfOpenParenReferCol + 1, indexOfCloseParenRefer);
+
+		List referColumnsList = StringUtils.split(referColumnNamesString,
+				columnsDelimitter, this.quotedId, this.quotedId, false);
+		List localColumnsList = StringUtils.split(localColumnNamesString,
+				columnsDelimitter, this.quotedId, this.quotedId, false);
+
+		return new LocalAndReferencedColumns(localColumnsList,
+				referColumnsList, constraintName, referCatalog, referTable);
+	}
+
+	private String removeQuotedId(String s) {
+		if (s == null) {
+			return null;
+		}
+
+		if (this.quotedId.equals("")) {
+			return s;
+		}
+
+		s = s.trim();
+
+		int frontOffset = 0;
+		int backOffset = s.length();
+		int quoteLength = this.quotedId.length();
+
+		if (s.startsWith(this.quotedId)) {
+			frontOffset = quoteLength;
+		}
+
+		if (s.endsWith(this.quotedId)) {
+			backOffset -= quoteLength;
+		}
+
+		return s.substring(frontOffset, backOffset);
+	}
+
+	/**
+	 * Converts the given string to bytes, using the connection's character
+	 * encoding, or if not available, the JVM default encoding.
+	 * 
+	 * @param s
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 */
+	private byte[] s2b(String s) throws SQLException {
+		return StringUtils.s2b(s, this.conn);
+	}
+	
+
+	/**
+	 * Does the database store mixed case unquoted SQL identifiers in lower
+	 * case?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean storesLowerCaseIdentifiers() throws SQLException {
+		return this.conn.lowerCaseTableNames();
+	}
+
+	/**
+	 * Does the database store mixed case quoted SQL identifiers in lower case?
+	 * A JDBC compliant driver will always return false.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean storesLowerCaseQuotedIdentifiers() throws SQLException {
+		return this.conn.lowerCaseTableNames();
+	}
+
+	/**
+	 * Does the database store mixed case unquoted SQL identifiers in mixed
+	 * case?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean storesMixedCaseIdentifiers() throws SQLException {
+		return !this.conn.lowerCaseTableNames();
+	}
+
+	/**
+	 * Does the database store mixed case quoted SQL identifiers in mixed case?
+	 * A JDBC compliant driver will always return false.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean storesMixedCaseQuotedIdentifiers() throws SQLException {
+		return !this.conn.lowerCaseTableNames();
+	}
+
+	/**
+	 * Does the database store mixed case unquoted SQL identifiers in upper
+	 * case?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean storesUpperCaseIdentifiers() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Does the database store mixed case quoted SQL identifiers in upper case?
+	 * A JDBC compliant driver will always return true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean storesUpperCaseQuotedIdentifiers() throws SQLException {
+		return true; // not actually true, but required by JDBC spec!?
+	}
+
+	/**
+	 * Is "ALTER TABLE" with add column supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsAlterTableWithAddColumn() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is "ALTER TABLE" with drop column supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsAlterTableWithDropColumn() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is the ANSI92 entry level SQL grammar supported? All JDBC compliant
+	 * drivers must return true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsANSI92EntryLevelSQL() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is the ANSI92 full SQL grammar supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsANSI92FullSQL() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Is the ANSI92 intermediate SQL grammar supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsANSI92IntermediateSQL() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * JDBC 2.0 Return true if the driver supports batch updates, else return
+	 * false.
+	 * 
+	 * @return DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsBatchUpdates() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Can a catalog name be used in a data manipulation statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsCatalogsInDataManipulation() throws SQLException {
+		// Servers before 3.22 could not do this
+		return this.conn.versionMeetsMinimum(3, 22, 0);
+	}
+
+	/**
+	 * Can a catalog name be used in a index definition statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsCatalogsInIndexDefinitions() throws SQLException {
+		// Servers before 3.22 could not do this
+		return this.conn.versionMeetsMinimum(3, 22, 0);
+	}
+
+	/**
+	 * Can a catalog name be used in a privilege definition statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException {
+		// Servers before 3.22 could not do this
+		return this.conn.versionMeetsMinimum(3, 22, 0);
+	}
+
+	/**
+	 * Can a catalog name be used in a procedure call statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsCatalogsInProcedureCalls() throws SQLException {
+		// Servers before 3.22 could not do this
+		return this.conn.versionMeetsMinimum(3, 22, 0);
+	}
+
+	/**
+	 * Can a catalog name be used in a table definition statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsCatalogsInTableDefinitions() throws SQLException {
+		// Servers before 3.22 could not do this
+		return this.conn.versionMeetsMinimum(3, 22, 0);
+	}
+
+	/**
+	 * Is column aliasing supported?
+	 * <P>
+	 * If so, the SQL AS clause can be used to provide names for computed
+	 * columns or to provide alias names for columns as required. A JDBC
+	 * compliant driver always returns true.
+	 * </p>
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsColumnAliasing() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is the CONVERT function between SQL types supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsConvert() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Is CONVERT between the given SQL types supported?
+	 * 
+	 * @param fromType
+	 *            the type to convert from
+	 * @param toType
+	 *            the type to convert to
+	 * @return true if so
+	 * @throws SQLException
+	 *             if an error occurs
+	 * @see Types
+	 */
+	public boolean supportsConvert(int fromType, int toType)
+			throws SQLException {
+		switch (fromType) {
+		/*
+		 * The char/binary types can be converted to pretty much anything.
+		 */
+		case java.sql.Types.CHAR:
+		case java.sql.Types.VARCHAR:
+		case java.sql.Types.LONGVARCHAR:
+		case java.sql.Types.BINARY:
+		case java.sql.Types.VARBINARY:
+		case java.sql.Types.LONGVARBINARY:
+
+			switch (toType) {
+			case java.sql.Types.DECIMAL:
+			case java.sql.Types.NUMERIC:
+			case java.sql.Types.REAL:
+			case java.sql.Types.TINYINT:
+			case java.sql.Types.SMALLINT:
+			case java.sql.Types.INTEGER:
+			case java.sql.Types.BIGINT:
+			case java.sql.Types.FLOAT:
+			case java.sql.Types.DOUBLE:
+			case java.sql.Types.CHAR:
+			case java.sql.Types.VARCHAR:
+			case java.sql.Types.LONGVARCHAR:
+			case java.sql.Types.BINARY:
+			case java.sql.Types.VARBINARY:
+			case java.sql.Types.LONGVARBINARY:
+			case java.sql.Types.OTHER:
+			case java.sql.Types.DATE:
+			case java.sql.Types.TIME:
+			case java.sql.Types.TIMESTAMP:
+				return true;
+
+			default:
+				return false;
+			}
+
+		/*
+		 * We don't handle the BIT type yet.
+		 */
+		case java.sql.Types.BIT:
+			return false;
+
+		/*
+		 * The numeric types. Basically they can convert among themselves, and
+		 * with char/binary types.
+		 */
+		case java.sql.Types.DECIMAL:
+		case java.sql.Types.NUMERIC:
+		case java.sql.Types.REAL:
+		case java.sql.Types.TINYINT:
+		case java.sql.Types.SMALLINT:
+		case java.sql.Types.INTEGER:
+		case java.sql.Types.BIGINT:
+		case java.sql.Types.FLOAT:
+		case java.sql.Types.DOUBLE:
+
+			switch (toType) {
+			case java.sql.Types.DECIMAL:
+			case java.sql.Types.NUMERIC:
+			case java.sql.Types.REAL:
+			case java.sql.Types.TINYINT:
+			case java.sql.Types.SMALLINT:
+			case java.sql.Types.INTEGER:
+			case java.sql.Types.BIGINT:
+			case java.sql.Types.FLOAT:
+			case java.sql.Types.DOUBLE:
+			case java.sql.Types.CHAR:
+			case java.sql.Types.VARCHAR:
+			case java.sql.Types.LONGVARCHAR:
+			case java.sql.Types.BINARY:
+			case java.sql.Types.VARBINARY:
+			case java.sql.Types.LONGVARBINARY:
+				return true;
+
+			default:
+				return false;
+			}
+
+		/* MySQL doesn't support a NULL type. */
+		case java.sql.Types.NULL:
+			return false;
+
+		/*
+		 * With this driver, this will always be a serialized object, so the
+		 * char/binary types will work.
+		 */
+		case java.sql.Types.OTHER:
+
+			switch (toType) {
+			case java.sql.Types.CHAR:
+			case java.sql.Types.VARCHAR:
+			case java.sql.Types.LONGVARCHAR:
+			case java.sql.Types.BINARY:
+			case java.sql.Types.VARBINARY:
+			case java.sql.Types.LONGVARBINARY:
+				return true;
+
+			default:
+				return false;
+			}
+
+		/* Dates can be converted to char/binary types. */
+		case java.sql.Types.DATE:
+
+			switch (toType) {
+			case java.sql.Types.CHAR:
+			case java.sql.Types.VARCHAR:
+			case java.sql.Types.LONGVARCHAR:
+			case java.sql.Types.BINARY:
+			case java.sql.Types.VARBINARY:
+			case java.sql.Types.LONGVARBINARY:
+				return true;
+
+			default:
+				return false;
+			}
+
+		/* Time can be converted to char/binary types */
+		case java.sql.Types.TIME:
+
+			switch (toType) {
+			case java.sql.Types.CHAR:
+			case java.sql.Types.VARCHAR:
+			case java.sql.Types.LONGVARCHAR:
+			case java.sql.Types.BINARY:
+			case java.sql.Types.VARBINARY:
+			case java.sql.Types.LONGVARBINARY:
+				return true;
+
+			default:
+				return false;
+			}
+
+		/*
+		 * Timestamp can be converted to char/binary types and date/time types
+		 * (with loss of precision).
+		 */
+		case java.sql.Types.TIMESTAMP:
+
+			switch (toType) {
+			case java.sql.Types.CHAR:
+			case java.sql.Types.VARCHAR:
+			case java.sql.Types.LONGVARCHAR:
+			case java.sql.Types.BINARY:
+			case java.sql.Types.VARBINARY:
+			case java.sql.Types.LONGVARBINARY:
+			case java.sql.Types.TIME:
+			case java.sql.Types.DATE:
+				return true;
+
+			default:
+				return false;
+			}
+
+		/* We shouldn't get here! */
+		default:
+			return false; // not sure
+		}
+	}
+
+	/**
+	 * Is the ODBC Core SQL grammar supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsCoreSQLGrammar() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Are correlated subqueries supported? A JDBC compliant driver always
+	 * returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsCorrelatedSubqueries() throws SQLException {
+		return this.conn.versionMeetsMinimum(4, 1, 0);
+	}
+
+	/**
+	 * Are both data definition and data manipulation statements within a
+	 * transaction supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsDataDefinitionAndDataManipulationTransactions()
+			throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Are only data manipulation statements within a transaction supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsDataManipulationTransactionsOnly()
+			throws SQLException {
+		return false;
+	}
+
+	/**
+	 * If table correlation names are supported, are they restricted to be
+	 * different from the names of the tables? A JDBC compliant driver always
+	 * returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsDifferentTableCorrelationNames() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Are expressions in "ORDER BY" lists supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsExpressionsInOrderBy() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is the ODBC Extended SQL grammar supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsExtendedSQLGrammar() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Are full nested outer joins supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsFullOuterJoins() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * JDBC 3.0
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean supportsGetGeneratedKeys() {
+		return true;
+	}
+
+	/**
+	 * Is some form of "GROUP BY" clause supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsGroupBy() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Can a "GROUP BY" clause add columns not in the SELECT provided it
+	 * specifies all the columns in the SELECT?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsGroupByBeyondSelect() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Can a "GROUP BY" clause use columns not in the SELECT?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsGroupByUnrelated() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is the SQL Integrity Enhancement Facility supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsIntegrityEnhancementFacility() throws SQLException {
+		if (!this.conn.getOverrideSupportsIntegrityEnhancementFacility()) {
+			return false;
+		} 
+		
+		return true;
+	}
+
+	/**
+	 * Is the escape character in "LIKE" clauses supported? A JDBC compliant
+	 * driver always returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsLikeEscapeClause() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is there limited support for outer joins? (This will be true if
+	 * supportFullOuterJoins is true.)
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsLimitedOuterJoins() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is the ODBC Minimum SQL grammar supported? All JDBC compliant drivers
+	 * must return true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsMinimumSQLGrammar() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Does the database support mixed case unquoted SQL identifiers?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsMixedCaseIdentifiers() throws SQLException {
+		return !this.conn.lowerCaseTableNames();
+	}
+
+	/**
+	 * Does the database support mixed case quoted SQL identifiers? A JDBC
+	 * compliant driver will always return true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException {
+		return !this.conn.lowerCaseTableNames();
+	}
+
+	/**
+	 * @see DatabaseMetaData#supportsMultipleOpenResults()
+	 */
+	public boolean supportsMultipleOpenResults() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Are multiple ResultSets from a single execute supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsMultipleResultSets() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can we have multiple transactions open at once (on different
+	 * connections)?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsMultipleTransactions() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * @see DatabaseMetaData#supportsNamedParameters()
+	 */
+	public boolean supportsNamedParameters() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can columns be defined as non-nullable? A JDBC compliant driver always
+	 * returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsNonNullableColumns() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Can cursors remain open across commits?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see Connection#disableAutoClose
+	 */
+	public boolean supportsOpenCursorsAcrossCommit() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can cursors remain open across rollbacks?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             if an error occurs
+	 * @see Connection#disableAutoClose
+	 */
+	public boolean supportsOpenCursorsAcrossRollback() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can statements remain open across commits?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             if an error occurs
+	 * @see Connection#disableAutoClose
+	 */
+	public boolean supportsOpenStatementsAcrossCommit() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can statements remain open across rollbacks?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             if an error occurs
+	 * @see Connection#disableAutoClose
+	 */
+	public boolean supportsOpenStatementsAcrossRollback() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can an "ORDER BY" clause use columns not in the SELECT?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsOrderByUnrelated() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Is some form of outer join supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsOuterJoins() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is positioned DELETE supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsPositionedDelete() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Is positioned UPDATE supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsPositionedUpdate() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * JDBC 2.0 Does the database support the concurrency type in combination
+	 * with the given result set type?
+	 * 
+	 * @param type
+	 *            defined in java.sql.ResultSet
+	 * @param concurrency
+	 *            type defined in java.sql.ResultSet
+	 * @return true if so
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 * @see Connection
+	 */
+	public boolean supportsResultSetConcurrency(int type, int concurrency)
+			throws SQLException {
+		switch (type) {
+		case ResultSet.TYPE_SCROLL_INSENSITIVE:
+			if ((concurrency == ResultSet.CONCUR_READ_ONLY)
+					|| (concurrency == ResultSet.CONCUR_UPDATABLE)) {
+				return true;
+			} else {
+				throw SQLError.createSQLException(
+						"Illegal arguments to supportsResultSetConcurrency()",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		case ResultSet.TYPE_FORWARD_ONLY:
+			if ((concurrency == ResultSet.CONCUR_READ_ONLY)
+					|| (concurrency == ResultSet.CONCUR_UPDATABLE)) {
+				return true;
+			} else {
+				throw SQLError.createSQLException(
+						"Illegal arguments to supportsResultSetConcurrency()",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		case ResultSet.TYPE_SCROLL_SENSITIVE:
+			return false;
+		default:
+			throw SQLError.createSQLException(
+					"Illegal arguments to supportsResultSetConcurrency()",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+	}
+
+	/**
+	 * @see DatabaseMetaData#supportsResultSetHoldability(int)
+	 */
+	public boolean supportsResultSetHoldability(int holdability)
+			throws SQLException {
+		return (holdability == ResultSet.HOLD_CURSORS_OVER_COMMIT);
+	}
+
+	/**
+	 * JDBC 2.0 Does the database support the given result set type?
+	 * 
+	 * @param type
+	 *            defined in java.sql.ResultSet
+	 * @return true if so
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 * @see Connection
+	 */
+	public boolean supportsResultSetType(int type) throws SQLException {
+		return (type == ResultSet.TYPE_SCROLL_INSENSITIVE);
+	}
+
+	/**
+	 * @see DatabaseMetaData#supportsSavepoints()
+	 */
+	public boolean supportsSavepoints() throws SQLException {
+
+		return (this.conn.versionMeetsMinimum(4, 0, 14) || this.conn
+				.versionMeetsMinimum(4, 1, 1));
+	}
+
+	/**
+	 * Can a schema name be used in a data manipulation statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSchemasInDataManipulation() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can a schema name be used in an index definition statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSchemasInIndexDefinitions() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can a schema name be used in a privilege definition statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can a schema name be used in a procedure call statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSchemasInProcedureCalls() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Can a schema name be used in a table definition statement?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSchemasInTableDefinitions() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Is SELECT for UPDATE supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSelectForUpdate() throws SQLException {
+		return this.conn.versionMeetsMinimum(4, 0, 0);
+	}
+
+	/**
+	 * @see DatabaseMetaData#supportsStatementPooling()
+	 */
+	public boolean supportsStatementPooling() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Are stored procedure calls using the stored procedure escape syntax
+	 * supported?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsStoredProcedures() throws SQLException {
+		return this.conn.versionMeetsMinimum(5, 0, 0);
+	}
+
+	/**
+	 * Are subqueries in comparison expressions supported? A JDBC compliant
+	 * driver always returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSubqueriesInComparisons() throws SQLException {
+		return this.conn.versionMeetsMinimum(4, 1, 0);
+	}
+
+	/**
+	 * Are subqueries in exists expressions supported? A JDBC compliant driver
+	 * always returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSubqueriesInExists() throws SQLException {
+		return this.conn.versionMeetsMinimum(4, 1, 0);
+	}
+
+	/**
+	 * Are subqueries in "in" statements supported? A JDBC compliant driver
+	 * always returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSubqueriesInIns() throws SQLException {
+		return this.conn.versionMeetsMinimum(4, 1, 0);
+	}
+
+	/**
+	 * Are subqueries in quantified expressions supported? A JDBC compliant
+	 * driver always returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsSubqueriesInQuantifieds() throws SQLException {
+		return this.conn.versionMeetsMinimum(4, 1, 0);
+	}
+
+	/**
+	 * Are table correlation names supported? A JDBC compliant driver always
+	 * returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsTableCorrelationNames() throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Does the database support the given transaction isolation level?
+	 * 
+	 * @param level
+	 *            the values are defined in java.sql.Connection
+	 * @return true if so
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see Connection
+	 */
+	public boolean supportsTransactionIsolationLevel(int level)
+			throws SQLException {
+		if (this.conn.supportsIsolationLevel()) {
+			switch (level) {
+			case java.sql.Connection.TRANSACTION_READ_COMMITTED:
+			case java.sql.Connection.TRANSACTION_READ_UNCOMMITTED:
+			case java.sql.Connection.TRANSACTION_REPEATABLE_READ:
+			case java.sql.Connection.TRANSACTION_SERIALIZABLE:
+				return true;
+
+			default:
+				return false;
+			}
+		}
+
+		return false;
+	}
+
+	/**
+	 * Are transactions supported? If not, commit is a noop and the isolation
+	 * level is TRANSACTION_NONE.
+	 * 
+	 * @return true if transactions are supported
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsTransactions() throws SQLException {
+		return this.conn.supportsTransactions();
+	}
+
+	/**
+	 * Is SQL UNION supported? A JDBC compliant driver always returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsUnion() throws SQLException {
+		return this.conn.versionMeetsMinimum(4, 0, 0);
+	}
+
+	/**
+	 * Is SQL UNION ALL supported? A JDBC compliant driver always returns true.
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean supportsUnionAll() throws SQLException {
+		return this.conn.versionMeetsMinimum(4, 0, 0);
+	}
+
+	/**
+	 * JDBC 2.0 Determine whether or not a visible row update can be detected by
+	 * calling ResultSet.rowUpdated().
+	 * 
+	 * @param type
+	 *            set type, i.e. ResultSet.TYPE_XXX
+	 * @return true if changes are detected by the resultset type
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public boolean updatesAreDetected(int type) throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Does the database use a file for each table?
+	 * 
+	 * @return true if the database uses a local file for each table
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean usesLocalFilePerTable() throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Does the database store tables in a local file?
+	 * 
+	 * @return true if so
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public boolean usesLocalFiles() throws SQLException {
+		return false;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1173 @@
+/*
+ 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;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+
+/**
+ * DatabaseMetaData implementation that uses INFORMATION_SCHEMA available in
+ * MySQL-5.0 and newer.
+ * 
+ * The majority of the queries in this code were built for Connector/OO.org by
+ * Georg Richter (georg_at_mysql.com).
+ */
+public class DatabaseMetaDataUsingInfoSchema extends DatabaseMetaData {
+
+	public DatabaseMetaDataUsingInfoSchema(Connection connToSet,
+			String databaseToSet) {
+		super(connToSet, databaseToSet);
+	}
+
+	private ResultSet executeMetadataQuery(PreparedStatement pStmt)
+			throws SQLException {
+		ResultSet rs = pStmt.executeQuery();
+		((com.mysql.jdbc.ResultSet) rs).setOwningStatement(null);
+
+		return rs;
+	}
+
+	/**
+	 * Get a description of the access rights for a table's columns.
+	 * <P>
+	 * Only privileges matching the column name criteria are returned. They are
+	 * ordered by COLUMN_NAME and PRIVILEGE.
+	 * </p>
+	 * <P>
+	 * Each privilige description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>GRANTOR</B> => grantor of access (may be null) </li>
+	 * <li> <B>GRANTEE</B> String => grantee of access </li>
+	 * <li> <B>PRIVILEGE</B> String => name of access (SELECT, INSERT, UPDATE,
+	 * REFRENCES, ...) </li>
+	 * <li> <B>IS_GRANTABLE</B> String => "YES" if grantee is permitted to
+	 * grant to others; "NO" if not; null if unknown </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @param columnNamePattern
+	 *            a column name pattern
+	 * @return ResultSet each row is a column privilege description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getSearchStringEscape
+	 */
+	public java.sql.ResultSet getColumnPrivileges(String catalog,
+			String schema, String table, String columnNamePattern)
+			throws SQLException {
+		if (columnNamePattern == null) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				columnNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Column name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		if (catalog == null) {
+			if (!this.conn.getNullCatalogMeansCurrent()) {
+				throw SQLError.createSQLException("'catalog' parameter can not be null",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			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_NAME =? AND COLUMN_NAME LIKE ? ORDER BY " 
+			 + "COLUMN_NAME, PRIVILEGE_TYPE";
+		
+		PreparedStatement pStmt = null;
+		
+		try {
+			pStmt = prepareMetaDataSafeStatement(sql);
+			pStmt.setString(1, catalog);
+			pStmt.setString(2, table);
+			pStmt.setString(3, columnNamePattern);
+			
+			ResultSet rs = executeMetadataQuery(pStmt);
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "COLUMN_NAME", Types.CHAR, 64),
+					new Field("", "GRANTOR", Types.CHAR, 77),
+					new Field("", "GRANTEE", Types.CHAR, 77),
+					new Field("", "PRIVILEGE", Types.CHAR, 64),
+					new Field("", "IS_GRANTABLE", Types.CHAR, 3)});
+			
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Get a description of table columns available in a catalog.
+	 * <P>
+	 * Only column descriptions matching the catalog, schema, table and column
+	 * name criteria are returned. They are ordered by TABLE_SCHEM, TABLE_NAME
+	 * and ORDINAL_POSITION.
+	 * </p>
+	 * <P>
+	 * Each column description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>DATA_TYPE</B> short => SQL type from java.sql.Types </li>
+	 * <li> <B>TYPE_NAME</B> String => Data source dependent type name </li>
+	 * <li> <B>COLUMN_SIZE</B> int => column size. For char or date types this
+	 * is the maximum number of characters, for numeric or decimal types this is
+	 * precision. </li>
+	 * <li> <B>BUFFER_LENGTH</B> is not used. </li>
+	 * <li> <B>DECIMAL_DIGITS</B> int => the number of fractional digits </li>
+	 * <li> <B>NUM_PREC_RADIX</B> int => Radix (typically either 10 or 2) </li>
+	 * <li> <B>NULLABLE</B> int => is NULL allowed?
+	 * <UL>
+	 * <li> columnNoNulls - might not allow NULL values </li>
+	 * <li> columnNullable - definitely allows NULL values </li>
+	 * <li> columnNullableUnknown - nullability unknown </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>REMARKS</B> String => comment describing column (may be null)
+	 * </li>
+	 * <li> <B>COLUMN_DEF</B> String => default value (may be null) </li>
+	 * <li> <B>SQL_DATA_TYPE</B> int => unused </li>
+	 * <li> <B>SQL_DATETIME_SUB</B> int => unused </li>
+	 * <li> <B>CHAR_OCTET_LENGTH</B> int => for char types the maximum number
+	 * of bytes in the column </li>
+	 * <li> <B>ORDINAL_POSITION</B> int => index of column in table (starting
+	 * at 1) </li>
+	 * <li> <B>IS_NULLABLE</B> String => "NO" means column definitely does not
+	 * allow NULL values; "YES" means the column might allow NULL values. An
+	 * empty string means nobody knows. </li>
+	 * </ol>
+	 * </p>
+	 */
+	public ResultSet getColumns(String catalog, String schemaPattern,
+			String tableName, String columnNamePattern) throws SQLException {
+		if (columnNamePattern == null) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				columnNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Column name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		if (catalog == null) {
+			if (!this.conn.getNullCatalogMeansCurrent()) {
+				throw SQLError.createSQLException("'catalog' parameter can not be null",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			catalog = this.database;
+		}
+
+		StringBuffer sqlBuf = new StringBuffer("SELECT "
+				+ "TABLE_SCHEMA AS TABLE_CAT, " + "NULL AS TABLE_SCHEM,"
+				+ "TABLE_NAME," + "COLUMN_NAME,");
+		MysqlDefs.appendJdbcTypeMappingQuery(sqlBuf, "DATA_TYPE");
+
+		sqlBuf.append(" AS DATA_TYPE, ");
+
+		if (conn.getCapitalizeTypeNames()) {
+			sqlBuf.append("UPPER(CASE WHEN LOCATE('unsigned', COLUMN_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') ELSE DATA_TYPE END) AS TYPE_NAME,");
+		} else {
+			sqlBuf.append("CASE WHEN LOCATE('unsigned', COLUMN_TYPE) != 0 AND LOCATE('unsigned', DATA_TYPE) = 0 THEN CONCAT(DATA_TYPE, ' unsigned') ELSE DATA_TYPE END AS TYPE_NAME,");
+		}
+
+		sqlBuf
+				.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,"
+						+ "NUMERIC_SCALE AS DECIMAL_DIGITS,"
+						+ "10 AS NUM_PREC_RADIX,"
+						+ "NULL 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,"
+						+ "ORDINAL_POSITION,"
+						+ "IS_NULLABLE "
+						+ "FROM INFORMATION_SCHEMA.COLUMNS WHERE "
+						+ "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);
+			pStmt.setString(2, tableName);
+			pStmt.setString(3, columnNamePattern);
+
+			ResultSet rs = executeMetadataQuery(pStmt);
+
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "COLUMN_NAME", Types.CHAR, 32),
+					new Field("", "DATA_TYPE", Types.SMALLINT, 5),
+					new Field("", "TYPE_NAME", Types.CHAR, 16),
+					new Field("", "COLUMN_SIZE", Types.INTEGER, Integer
+							.toString(Integer.MAX_VALUE).length()),
+					new Field("", "BUFFER_LENGTH", Types.INTEGER, 10),
+					new Field("", "DECIMAL_DIGITS", Types.INTEGER, 10),
+					new Field("", "NUM_PREC_RADIX", Types.INTEGER, 10),
+					new Field("", "NULLABLE", Types.INTEGER, 10),
+					new Field("", "REMARKS", Types.CHAR, 0),
+					new Field("", "COLUMN_DEF", Types.CHAR, 0),
+					new Field("", "SQL_DATA_TYPE", Types.INTEGER, 10),
+					new Field("", "SQL_DATETIME_SUB", Types.INTEGER, 10),
+					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) });
+
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Get a description of the foreign key columns in the foreign key table
+	 * that reference the primary key columns of the primary key table (describe
+	 * how one table imports another's key.) This should normally return a
+	 * single foreign key/primary key pair (most tables only import a foreign
+	 * key from a table once.) They are ordered by FKTABLE_CAT, FKTABLE_SCHEM,
+	 * FKTABLE_NAME, and KEY_SEQ.
+	 * <P>
+	 * Each foreign key column description has the following columns:
+	 * <OL>
+	 * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be
+	 * null) </li>
+	 * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be
+	 * null) </li>
+	 * <li> <B>PKTABLE_NAME</B> String => primary key table name </li>
+	 * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li>
+	 * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
+	 * null) being exported (may be null) </li>
+	 * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
+	 * null) being exported (may be null) </li>
+	 * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported
+	 * </li>
+	 * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being
+	 * exported </li>
+	 * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
+	 * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
+	 * primary is updated:
+	 * <UL>
+	 * <li> importedKeyCascade - change imported key to agree with primary key
+	 * update </li>
+	 * <li> importedKeyRestrict - do not allow update of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been updated </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
+	 * primary is deleted.
+	 * <UL>
+	 * <li> importedKeyCascade - delete rows that import a deleted key </li>
+	 * <li> importedKeyRestrict - do not allow delete of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been deleted </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li>
+	 * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param primaryCatalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param primarySchema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param primaryTable
+	 *            a table name
+	 * @param foreignCatalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param foreignSchema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param foreignTable
+	 *            a table name
+	 * @return ResultSet each row is a foreign key column description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public java.sql.ResultSet getCrossReference(String primaryCatalog,
+			String primarySchema, String primaryTable, String foreignCatalog,
+			String foreignSchema, String foreignTable) throws SQLException {
+		if (primaryTable == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if (primaryCatalog == null) {
+			if (!this.conn.getNullCatalogMeansCurrent()) {
+				throw SQLError.createSQLException("'catalog' parameter can not be null",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			primaryCatalog = this.database;
+		}
+
+		if (foreignCatalog == null) {
+			if (!this.conn.getNullCatalogMeansCurrent()) {
+				throw SQLError.createSQLException("'catalog' parameter can not be null",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			foreignCatalog = this.database;
+		}
+
+		Field[] fields = new Field[14];
+		fields[0] = new Field("", "PKTABLE_CAT", Types.CHAR, 255);
+		fields[1] = new Field("", "PKTABLE_SCHEM", Types.CHAR, 0);
+		fields[2] = new Field("", "PKTABLE_NAME", Types.CHAR, 255);
+		fields[3] = new Field("", "PKCOLUMN_NAME", Types.CHAR, 32);
+		fields[4] = new Field("", "FKTABLE_CAT", Types.CHAR, 255);
+		fields[5] = new Field("", "FKTABLE_SCHEM", Types.CHAR, 0);
+		fields[6] = new Field("", "FKTABLE_NAME", Types.CHAR, 255);
+		fields[7] = new Field("", "FKCOLUMN_NAME", Types.CHAR, 32);
+		fields[8] = new Field("", "KEY_SEQ", Types.SMALLINT, 2);
+		fields[9] = new Field("", "UPDATE_RULE", Types.SMALLINT, 2);
+		fields[10] = new Field("", "DELETE_RULE", Types.SMALLINT, 2);
+		fields[11] = new Field("", "FK_NAME", Types.CHAR, 0);
+		fields[12] = new Field("", "PK_NAME", Types.CHAR, 0);
+		fields[13] = new Field("", "DEFERRABILITY", Types.INTEGER, 2);
+
+		String sql = "SELECT "
+				+ "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT,"
+				+ "NULL AS PKTABLE_SCHEM,"
+				+ "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME,"
+				+ "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME,"
+				+ "A.TABLE_SCHEMA AS FKTABLE_CAT,"
+				+ "NULL AS FKTABLE_SCHEM,"
+				+ "A.TABLE_NAME AS FKTABLE_NAME, "
+				+ "A.COLUMN_NAME AS FKCOLUMN_NAME, "
+				+ "A.ORDINAL_POSITION AS KEY_SEQ,"
+				+ importedKeyRestrict
+				+ " AS UPDATE_RULE,"
+				+ importedKeyRestrict
+				+ " AS DELETE_RULE,"
+				+ "A.CONSTRAINT_NAME AS FK_NAME,"
+				+ "NULL AS PK_NAME,"
+				+ importedKeyNotDeferrable
+				+ " AS DEFERRABILITY "
+				+ "FROM "
+				+ "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A,"
+				+ "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B "
+				+ "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 "
+				+ "A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION";
+
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = prepareMetaDataSafeStatement(sql);
+			pStmt.setString(1, primaryCatalog);
+			pStmt.setString(2, primaryTable);
+			pStmt.setString(3, foreignCatalog);
+			pStmt.setString(4, foreignTable);
+
+			ResultSet rs = executeMetadataQuery(pStmt);
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "PKCOLUMN_NAME", Types.CHAR, 32),
+					new Field("", "FKTABLE_CAT", Types.CHAR, 255),
+					new Field("", "FKTABLE_SCHEM", Types.CHAR, 0),
+					new Field("", "FKTABLE_NAME", Types.CHAR, 255),
+					new Field("", "FKCOLUMN_NAME", Types.CHAR, 32),
+					new Field("", "KEY_SEQ", Types.SMALLINT, 2),
+					new Field("", "UPDATE_RULE", Types.SMALLINT, 2),
+					new Field("", "DELETE_RULE", Types.SMALLINT, 2),
+					new Field("", "FK_NAME", Types.CHAR, 0),
+					new Field("", "PK_NAME", Types.CHAR, 0),
+					new Field("", "DEFERRABILITY", Types.INTEGER, 2) });
+
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Get a description of a foreign key columns that reference a table's
+	 * primary key columns (the foreign keys exported by a table). They are
+	 * ordered by FKTABLE_CAT, FKTABLE_SCHEM, FKTABLE_NAME, and KEY_SEQ.
+	 * <P>
+	 * Each foreign key column description has the following columns:
+	 * <OL>
+	 * <li> <B>PKTABLE_CAT</B> String => primary key table catalog (may be
+	 * null) </li>
+	 * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema (may be
+	 * null) </li>
+	 * <li> <B>PKTABLE_NAME</B> String => primary key table name </li>
+	 * <li> <B>PKCOLUMN_NAME</B> String => primary key column name </li>
+	 * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
+	 * null) being exported (may be null) </li>
+	 * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
+	 * null) being exported (may be null) </li>
+	 * <li> <B>FKTABLE_NAME</B> String => foreign key table name being exported
+	 * </li>
+	 * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name being
+	 * exported </li>
+	 * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
+	 * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
+	 * primary is updated:
+	 * <UL>
+	 * <li> importedKeyCascade - change imported key to agree with primary key
+	 * update </li>
+	 * <li> importedKeyRestrict - do not allow update of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been updated </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
+	 * primary is deleted.
+	 * <UL>
+	 * <li> importedKeyCascade - delete rows that import a deleted key </li>
+	 * <li> importedKeyRestrict - do not allow delete of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been deleted </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>FK_NAME</B> String => foreign key identifier (may be null) </li>
+	 * <li> <B>PK_NAME</B> String => primary key identifier (may be null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @return ResultSet each row is a foreign key column description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getImportedKeys
+	 */
+	public java.sql.ResultSet getExportedKeys(String catalog, String schema,
+			String table) throws SQLException {
+		// TODO: Can't determine actions using INFORMATION_SCHEMA yet...
+
+		if (table == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if (catalog == null) {
+			if (!this.conn.getNullCatalogMeansCurrent()) {
+				throw SQLError.createSQLException("'catalog' parameter can not be null",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			catalog = this.database;
+		}
+
+		String sql = "SELECT "
+				+ "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT,"
+				+ "NULL AS PKTABLE_SCHEM,"
+				+ "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME, "
+				+ "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME, "
+				+ "A.TABLE_SCHEMA AS FKTABLE_CAT,"
+				+ "NULL AS FKTABLE_SCHEM,"
+				+ "A.TABLE_NAME AS FKTABLE_NAME,"
+				+ "A.COLUMN_NAME AS FKCOLUMN_NAME, "
+				+ "A.ORDINAL_POSITION AS KEY_SEQ,"
+				+ importedKeyRestrict
+				+ " AS UPDATE_RULE,"
+				+ importedKeyRestrict
+				+ " AS DELETE_RULE,"
+				+ "A.CONSTRAINT_NAME AS FK_NAME,"
+				+ "NULL AS PK_NAME,"
+				+ importedKeyNotDeferrable
+				+ " AS DEFERRABILITY "
+				+ "FROM "
+				+ "INFORMATION_SCHEMA.KEY_COLUMN_USAGE A,"
+				+ "INFORMATION_SCHEMA.TABLE_CONSTRAINTS B "
+				+ "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=? "
+				+ "ORDER BY A.TABLE_SCHEMA, A.TABLE_NAME, A.ORDINAL_POSITION";
+
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = prepareMetaDataSafeStatement(sql);
+			pStmt.setString(1, catalog);
+			pStmt.setString(2, table);
+
+			ResultSet rs = executeMetadataQuery(pStmt);
+
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "PKCOLUMN_NAME", Types.CHAR, 32),
+					new Field("", "FKTABLE_CAT", Types.CHAR, 255),
+					new Field("", "FKTABLE_SCHEM", Types.CHAR, 0),
+					new Field("", "FKTABLE_NAME", Types.CHAR, 255),
+					new Field("", "FKCOLUMN_NAME", Types.CHAR, 32),
+					new Field("", "KEY_SEQ", Types.SMALLINT, 2),
+					new Field("", "UPDATE_RULE", Types.SMALLINT, 2),
+					new Field("", "DELETE_RULE", Types.SMALLINT, 2),
+					new Field("", "FK_NAME", Types.CHAR, 255),
+					new Field("", "PK_NAME", Types.CHAR, 0),
+					new Field("", "DEFERRABILITY", Types.INTEGER, 2) });
+
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+
+	}
+
+	/*
+	 * 
+	 * 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 );
+	 */
+
+	/**
+	 * 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
+	 * are ordered by PKTABLE_CAT, PKTABLE_SCHEM, PKTABLE_NAME, and KEY_SEQ.
+	 * <P>
+	 * Each primary key column description has the following columns:
+	 * <OL>
+	 * <li> <B>PKTABLE_CAT</B> String => primary key table catalog being
+	 * imported (may be null) </li>
+	 * <li> <B>PKTABLE_SCHEM</B> String => primary key table schema being
+	 * imported (may be null) </li>
+	 * <li> <B>PKTABLE_NAME</B> String => primary key table name being imported
+	 * </li>
+	 * <li> <B>PKCOLUMN_NAME</B> String => primary key column name being
+	 * imported </li>
+	 * <li> <B>FKTABLE_CAT</B> String => foreign key table catalog (may be
+	 * null) </li>
+	 * <li> <B>FKTABLE_SCHEM</B> String => foreign key table schema (may be
+	 * null) </li>
+	 * <li> <B>FKTABLE_NAME</B> String => foreign key table name </li>
+	 * <li> <B>FKCOLUMN_NAME</B> String => foreign key column name </li>
+	 * <li> <B>KEY_SEQ</B> short => sequence number within foreign key </li>
+	 * <li> <B>UPDATE_RULE</B> short => What happens to foreign key when
+	 * primary is updated:
+	 * <UL>
+	 * <li> importedKeyCascade - change imported key to agree with primary key
+	 * update </li>
+	 * <li> importedKeyRestrict - do not allow update of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been updated </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>DELETE_RULE</B> short => What happens to the foreign key when
+	 * primary is deleted.
+	 * <UL>
+	 * <li> importedKeyCascade - delete rows that import a deleted key </li>
+	 * <li> importedKeyRestrict - do not allow delete of primary key if it has
+	 * been imported </li>
+	 * <li> importedKeySetNull - change imported key to NULL if its primary key
+	 * has been deleted </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>FK_NAME</B> String => foreign key name (may be null) </li>
+	 * <li> <B>PK_NAME</B> String => primary key name (may be null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @return ResultSet each row is a primary key column description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getExportedKeys
+	 */
+	public java.sql.ResultSet getImportedKeys(String catalog, String schema,
+			String table) throws SQLException {
+		if (table == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if (catalog == null) {
+			if (!this.conn.getNullCatalogMeansCurrent()) {
+				throw SQLError.createSQLException("'catalog' parameter can not be null",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+			catalog = this.database;
+		}
+
+		String sql = "SELECT "
+				+ "A.REFERENCED_TABLE_SCHEMA AS PKTABLE_CAT,"
+				+ "NULL AS PKTABLE_SCHEM,"
+				+ "A.REFERENCED_TABLE_NAME AS PKTABLE_NAME,"
+				+ "A.REFERENCED_COLUMN_NAME AS PKCOLUMN_NAME,"
+				+ "A.TABLE_SCHEMA AS FKTABLE_CAT,"
+				+ "NULL AS FKTABLE_SCHEM,"
+				+ "A.TABLE_NAME AS FKTABLE_NAME, "
+				+ "A.COLUMN_NAME AS FKCOLUMN_NAME, "
+				+ "A.ORDINAL_POSITION AS KEY_SEQ,"
+				+ importedKeyRestrict
+				+ " AS UPDATE_RULE,"
+				+ importedKeyRestrict
+				+ " AS DELETE_RULE,"
+				+ "A.CONSTRAINT_NAME AS FK_NAME,"
+				+ "NULL 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 "
+				+ "A.REFERENCED_TABLE_SCHEMA, A.REFERENCED_TABLE_NAME, "
+				+ "A.ORDINAL_POSITION";
+
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = prepareMetaDataSafeStatement(sql);
+			pStmt.setString(1, catalog);
+			pStmt.setString(2, table);
+			pStmt.setString(3, table);
+
+			ResultSet rs = executeMetadataQuery(pStmt);
+
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "PKCOLUMN_NAME", Types.CHAR, 32),
+					new Field("", "FKTABLE_CAT", Types.CHAR, 255),
+					new Field("", "FKTABLE_SCHEM", Types.CHAR, 0),
+					new Field("", "FKTABLE_NAME", Types.CHAR, 255),
+					new Field("", "FKCOLUMN_NAME", Types.CHAR, 32),
+					new Field("", "KEY_SEQ", Types.SMALLINT, 2),
+					new Field("", "UPDATE_RULE", Types.SMALLINT, 2),
+					new Field("", "DELETE_RULE", Types.SMALLINT, 2),
+					new Field("", "FK_NAME", Types.CHAR, 255),
+					new Field("", "PK_NAME", Types.CHAR, 0),
+					new Field("", "DEFERRABILITY", Types.INTEGER, 2) });
+
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Get a description of a table's indices and statistics. They are ordered
+	 * by NON_UNIQUE, TYPE, INDEX_NAME, and ORDINAL_POSITION.
+	 * <P>
+	 * Each index column description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>NON_UNIQUE</B> boolean => Can index values be non-unique? false
+	 * when TYPE is tableIndexStatistic </li>
+	 * <li> <B>INDEX_QUALIFIER</B> String => index catalog (may be null); null
+	 * when TYPE is tableIndexStatistic </li>
+	 * <li> <B>INDEX_NAME</B> String => index name; null when TYPE is
+	 * tableIndexStatistic </li>
+	 * <li> <B>TYPE</B> short => index type:
+	 * <UL>
+	 * <li> tableIndexStatistic - this identifies table statistics that are
+	 * returned in conjuction with a table's index descriptions </li>
+	 * <li> tableIndexClustered - this is a clustered index </li>
+	 * <li> tableIndexHashed - this is a hashed index </li>
+	 * <li> tableIndexOther - this is some other style of index </li>
+	 * </ul>
+	 * </li>
+	 * <li> <B>ORDINAL_POSITION</B> short => column sequence number within
+	 * index; zero when TYPE is tableIndexStatistic </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name; null when TYPE is
+	 * tableIndexStatistic </li>
+	 * <li> <B>ASC_OR_DESC</B> String => column sort sequence, "A" =>
+	 * ascending, "D" => descending, may be null if sort sequence is not
+	 * supported; null when TYPE is tableIndexStatistic </li>
+	 * <li> <B>CARDINALITY</B> int => When TYPE is tableIndexStatisic then this
+	 * is the number of rows in the table; otherwise it is the number of unique
+	 * values in the index. </li>
+	 * <li> <B>PAGES</B> int => When TYPE is tableIndexStatisic then this is
+	 * the number of pages used for the table, otherwise it is the number of
+	 * pages used for the current index. </li>
+	 * <li> <B>FILTER_CONDITION</B> String => Filter condition, if any. (may be
+	 * null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @param unique
+	 *            when true, return only indices for unique values; when false,
+	 *            return indices regardless of whether unique or not
+	 * @param approximate
+	 *            when true, result is allowed to reflect approximate or out of
+	 *            data values; when false, results are requested to be accurate
+	 * @return ResultSet each row is an index column description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public ResultSet getIndexInfo(String catalog, String schema, String table,
+			boolean unique, boolean approximate) throws SQLException {
+		StringBuffer sqlBuf = new StringBuffer("SELECT "
+				+ "TABLE_SCHEMA AS TABLE_CAT, " + "NULL AS TABLE_SCHEM,"
+				+ "TABLE_NAME," + "NON_UNIQUE,"
+				+ "TABLE_SCHEMA AS INDEX_QUALIFIER," + "INDEX_NAME,"
+				+ tableIndexOther + " AS TYPE,"
+				+ "SEQ_IN_INDEX AS ORDINAL_POSITION," + "COLUMN_NAME,"
+				+ "COLLATION AS ASC_OR_DESC," + "CARDINALITY,"
+				+ "NULL AS PAGES," + "NULL AS FILTER_CONDITION "
+				+ "FROM INFORMATION_SCHEMA.STATISTICS WHERE "
+				+ "TABLE_SCHEMA LIKE ? AND " + "TABLE_NAME LIKE ?");
+
+		if (unique) {
+			sqlBuf.append(" AND NON_UNIQUE=0 ");
+		}
+
+		sqlBuf.append("ORDER BY NON_UNIQUE, INDEX_NAME, SEQ_IN_INDEX");
+
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = prepareMetaDataSafeStatement(sqlBuf.toString());
+
+			pStmt.setString(1, catalog);
+			pStmt.setString(2, table);
+
+			ResultSet rs = executeMetadataQuery(pStmt);
+
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "NON_UNIQUE", Types.CHAR, 4),
+					new Field("", "INDEX_QUALIFIER", Types.CHAR, 1),
+					new Field("", "INDEX_NAME", Types.CHAR, 32),
+					new Field("", "TYPE", Types.CHAR, 32),
+					new Field("", "ORDINAL_POSITION", Types.SMALLINT, 5),
+					new Field("", "COLUMN_NAME", Types.CHAR, 32),
+					new Field("", "ASC_OR_DESC", Types.CHAR, 1),
+					new Field("", "CARDINALITY", Types.INTEGER, 10),
+					new Field("", "PAGES", Types.INTEGER, 10),
+					new Field("", "FILTER_CONDITION", Types.CHAR, 32) });
+
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Get a description of a table's primary key columns. They are ordered by
+	 * COLUMN_NAME.
+	 * <P>
+	 * Each column description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>COLUMN_NAME</B> String => column name </li>
+	 * <li> <B>KEY_SEQ</B> short => sequence number within primary key </li>
+	 * <li> <B>PK_NAME</B> String => primary key name (may be null) </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schema
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param table
+	 *            a table name
+	 * @return ResultSet each row is a primary key column description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public java.sql.ResultSet getPrimaryKeys(String catalog, String schema,
+			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);
+			}
+
+			catalog = this.database;
+		}
+
+		if (table == null) {
+			throw SQLError.createSQLException("Table not specified.",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, NULL AS TABLE_SCHEM, TABLE_NAME, "
+				+ "COLUMN_NAME, SEQ_IN_INDEX AS KEY_SEQ, 'PRIMARY' AS PK_NAME FROM INFORMATION_SCHEMA.STATISTICS "
+				+ "WHERE TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND "
+				+ "INDEX_NAME='PRIMARY' ORDER BY TABLE_SCHEMA, TABLE_NAME, INDEX_NAME, SEQ_IN_INDEX";
+
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = prepareMetaDataSafeStatement(sql);
+
+			pStmt.setString(1, catalog);
+			pStmt.setString(2, table);
+
+			ResultSet rs = executeMetadataQuery(pStmt);
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "COLUMN_NAME", Types.CHAR, 32),
+					new Field("", "KEY_SEQ", Types.SMALLINT, 5),
+					new Field("", "PK_NAME", Types.CHAR, 32) });
+
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Get a description of stored procedures available in a catalog.
+	 * <P>
+	 * Only procedure descriptions matching the schema and procedure name
+	 * criteria are returned. They are ordered by PROCEDURE_SCHEM, and
+	 * PROCEDURE_NAME.
+	 * </p>
+	 * <P>
+	 * Each procedure description has the the following columns:
+	 * <OL>
+	 * <li> <B>PROCEDURE_CAT</B> String => procedure catalog (may be null)
+	 * </li>
+	 * <li> <B>PROCEDURE_SCHEM</B> String => procedure schema (may be null)
+	 * </li>
+	 * <li> <B>PROCEDURE_NAME</B> String => procedure name </li>
+	 * <li> reserved for future use </li>
+	 * <li> reserved for future use </li>
+	 * <li> reserved for future use </li>
+	 * <li> <B>REMARKS</B> String => explanatory comment on the procedure </li>
+	 * <li> <B>PROCEDURE_TYPE</B> short => kind of procedure:
+	 * <UL>
+	 * <li> procedureResultUnknown - May return a result </li>
+	 * <li> procedureNoResult - Does not return a result </li>
+	 * <li> procedureReturnsResult - Returns a result </li>
+	 * </ul>
+	 * </li>
+	 * </ol>
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schemaPattern
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param procedureNamePattern
+	 *            a procedure name pattern
+	 * @return ResultSet each row is a procedure description
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * @see #getSearchStringEscape
+	 */
+	public ResultSet getProcedures(String catalog, String schemaPattern,
+			String procedureNamePattern) throws SQLException {
+
+		if ((procedureNamePattern == null)
+				|| (procedureNamePattern.length() == 0)) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				procedureNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Procedure name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		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);
+			}
+
+			catalog = null;
+			db = null;
+		}
+
+		String sql = "SELECT ROUTINE_SCHEMA AS PROCEDURE_CAT, "
+				+ "NULL AS PROCEDURE_SCHEM, "
+				+ "ROUTINE_NAME AS PROCEDURE_NAME, " + "NULL AS RESERVED_1, "
+				+ "NULL AS RESERVED_2, " + "NULL AS RESERVED_3, "
+				+ "ROUTINE_COMMENT AS REMARKS, "
+				+ "CASE WHEN ROUTINE_TYPE = 'PROCEDURE' THEN "
+				+ procedureNoResult + " WHEN ROUTINE_TYPE='FUNCTION' THEN "
+				+ procedureReturnsResult + " ELSE " + procedureResultUnknown
+				+ " END AS PROCEDURE_TYPE "
+				+ "FROM INFORMATION_SCHEMA.ROUTINES WHERE "
+				+ "ROUTINE_SCHEMA LIKE ? AND ROUTINE_NAME LIKE ? "
+				+ "ORDER BY ROUTINE_SCHEMA, ROUTINE_NAME";
+
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = prepareMetaDataSafeStatement(sql);
+			pStmt.setString(1, db);
+			pStmt.setString(2, procedureNamePattern);
+
+			ResultSet rs = executeMetadataQuery(pStmt);
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "reserved1", Types.CHAR, 0),
+					new Field("", "reserved2", Types.CHAR, 0),
+					new Field("", "reserved3", Types.CHAR, 0),
+					new Field("", "REMARKS", Types.CHAR, 0),
+					new Field("", "PROCEDURE_TYPE", Types.SMALLINT, 0) });
+
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Get a description of tables available in a catalog.
+	 * <P>
+	 * Only table descriptions matching the catalog, schema, table name and type
+	 * criteria are returned. They are ordered by TABLE_TYPE, TABLE_SCHEM and
+	 * TABLE_NAME.
+	 * </p>
+	 * <P>
+	 * Each table description has the following columns:
+	 * <OL>
+	 * <li> <B>TABLE_CAT</B> String => table catalog (may be null) </li>
+	 * <li> <B>TABLE_SCHEM</B> String => table schema (may be null) </li>
+	 * <li> <B>TABLE_NAME</B> String => table name </li>
+	 * <li> <B>TABLE_TYPE</B> String => table type. Typical types are "TABLE",
+	 * "VIEW", "SYSTEM TABLE", "GLOBAL TEMPORARY", "LOCAL TEMPORARY", "ALIAS",
+	 * "SYNONYM". </li>
+	 * <li> <B>REMARKS</B> String => explanatory comment on the table </li>
+	 * </ol>
+	 * </p>
+	 * <P>
+	 * <B>Note:</B> Some databases may not return information for all tables.
+	 * </p>
+	 * 
+	 * @param catalog
+	 *            a catalog name; "" retrieves those without a catalog
+	 * @param schemaPattern
+	 *            a schema name pattern; "" retrieves those without a schema
+	 * @param tableNamePattern
+	 *            a table name pattern
+	 * @param types
+	 *            a list of table types to include; null returns all types
+	 * @return ResultSet each row is a table description
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 * @see #getSearchStringEscape
+	 */
+	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);
+			}
+
+			catalog = this.database;
+		}
+
+		if (tableNamePattern == null) {
+			if (this.conn.getNullNamePatternMatchesAll()) {
+				tableNamePattern = "%";
+			} else {
+				throw SQLError.createSQLException(
+						"Table name pattern can not be NULL or empty.",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+
+		PreparedStatement pStmt = null;
+
+		String sql = "SELECT TABLE_SCHEMA AS TABLE_CAT, "
+				+ "NULL AS TABLE_SCHEM, TABLE_NAME, "
+				+ "CASE WHEN TABLE_TYPE='BASE TABLE' THEN 'TABLE' WHEN TABLE_TYPE='TEMPORARY' THEN 'LOCAL_TEMPORARY' ELSE TABLE_TYPE END AS TABLE_TYPE, "
+				+ "TABLE_COMMENT AS REMARKS "
+				+ "FROM INFORMATION_SCHEMA.TABLES WHERE "
+				+ "TABLE_SCHEMA LIKE ? AND TABLE_NAME LIKE ? AND TABLE_TYPE IN (?,?,?) "
+				+ "ORDER BY TABLE_TYPE, TABLE_SCHEMA, TABLE_NAME";
+		try {
+			pStmt = prepareMetaDataSafeStatement(sql);
+			pStmt.setString(1, catalog);
+			pStmt.setString(2, tableNamePattern);
+
+			// This overloading of IN (...) allows us to cache this
+			// prepared statement
+			if (types == null || types.length == 0) {
+				pStmt.setString(3, "BASE TABLE");
+				pStmt.setString(4, "VIEW");
+				pStmt.setString(5, "TEMPORARY");
+			} else {
+				pStmt.setNull(3, Types.VARCHAR);
+				pStmt.setNull(4, Types.VARCHAR);
+				pStmt.setNull(5, Types.VARCHAR);
+
+				for (int i = 0; i < types.length; i++) {
+					if ("TABLE".equalsIgnoreCase(types[i])) {
+						pStmt.setString(3, "BASE TABLE");
+					}
+
+					if ("VIEW".equalsIgnoreCase(types[i])) {
+						pStmt.setString(4, "VIEW");
+					}
+
+					if ("LOCAL TEMPORARY".equalsIgnoreCase(types[i])) {
+						pStmt.setString(5, "TEMPORARY");
+					}
+				}
+			}
+
+			ResultSet rs = executeMetadataQuery(pStmt);
+
+			((com.mysql.jdbc.ResultSet) 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),
+					new Field("", "TABLE_NAME", java.sql.Types.VARCHAR, 255),
+					new Field("", "TABLE_TYPE", java.sql.Types.VARCHAR, 5),
+					new Field("", "REMARKS", java.sql.Types.VARCHAR, 0) });
+
+			return rs;
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	private PreparedStatement prepareMetaDataSafeStatement(String sql)
+			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);
+
+		if (pStmt.getMaxRows() != 0) {
+			pStmt.setMaxRows(0);
+		}
+
+		pStmt.setHoldResultsOpenOverClose(true);
+
+		return pStmt;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DocsConnectionPropsHelper.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DocsConnectionPropsHelper.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/DocsConnectionPropsHelper.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,20 @@
+/*
+ * Created on Jan 12, 2004
+ *
+ * To change the template for this generated file go to
+ * Window - Preferences - Java - Code Generation - Code and Comments
+ */
+package com.mysql.jdbc;
+
+/**
+ * @author mmatthew
+ * 
+ * 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 static void main(String[] args) throws Exception {
+		System.out.println(new DocsConnectionPropsHelper().exposeAsXml());
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Driver.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Driver.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Driver.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,80 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+/**
+ * The Java SQL framework allows for multiple database drivers. Each driver
+ * should supply a class that implements the Driver interface
+ * 
+ * <p>
+ * The DriverManager will try to load as many drivers as it can find and then
+ * for any given connection request, it will ask each driver in turn to try to
+ * connect to the target URL.
+ * 
+ * <p>
+ * It is strongly recommended that each Driver class should be small and
+ * standalone so that the Driver class can be loaded and queried without
+ * bringing in vast quantities of supporting code.
+ * 
+ * <p>
+ * When a Driver class is loaded, it should create an instance of itself and
+ * register it with the DriverManager. This means that a user can load and
+ * register a driver by doing Class.forName("foo.bah.Driver")
+ * 
+ * @see org.gjt.mm.mysql.Connection
+ * @see java.sql.Driver
+ * @author Mark Matthews
+ * @version $Id: Driver.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class Driver extends NonRegisteringDriver implements java.sql.Driver {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	//
+	// Register ourselves with the DriverManager
+	//
+	static {
+		try {
+			java.sql.DriverManager.registerDriver(new Driver());
+		} catch (SQLException E) {
+			throw new RuntimeException("Can't register driver!");
+		}
+	}
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Construct a new driver and register it with DriverManager
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs.
+	 */
+	public Driver() throws SQLException {
+		// Required for Class.forName().newInstance()
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeProcessor.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeProcessor.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeProcessor.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,681 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+
+/**
+ * EscapeProcessor performs all escape code processing as outlined in the JDBC
+ * spec by JavaSoft.
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.TimeZone;
+
+class EscapeProcessor {
+	private static Map JDBC_CONVERT_TO_MYSQL_TYPE_MAP;
+
+	private static Map JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP;
+
+	static {
+		Map tempMap = new HashMap();
+
+		tempMap.put("BIGINT", "0 + ?");
+		tempMap.put("BINARY", "BINARY");
+		tempMap.put("BIT", "0 + ?");
+		tempMap.put("CHAR", "CHAR");
+		tempMap.put("DATE", "DATE");
+		tempMap.put("DECIMAL", "0.0 + ?");
+		tempMap.put("DOUBLE", "0.0 + ?");
+		tempMap.put("FLOAT", "0.0 + ?");
+		tempMap.put("INTEGER", "0 + ?");
+		tempMap.put("LONGVARBINARY", "BINARY");
+		tempMap.put("LONGVARCHAR", "CONCAT(?)");
+		tempMap.put("REAL", "0.0 + ?");
+		tempMap.put("SMALLINT", "CONCAT(?)");
+		tempMap.put("TIME", "TIME");
+		tempMap.put("TIMESTAMP", "DATETIME");
+		tempMap.put("TINYINT", "CONCAT(?)");
+		tempMap.put("VARBINARY", "BINARY");
+		tempMap.put("VARCHAR", "CONCAT(?)");
+
+		JDBC_CONVERT_TO_MYSQL_TYPE_MAP = Collections.unmodifiableMap(tempMap);
+
+		tempMap = new HashMap(JDBC_CONVERT_TO_MYSQL_TYPE_MAP);
+
+		tempMap.put("BINARY", "CONCAT(?)");
+		tempMap.put("CHAR", "CONCAT(?)");
+		tempMap.remove("DATE");
+		tempMap.put("LONGVARBINARY", "CONCAT(?)");
+		tempMap.remove("TIME");
+		tempMap.remove("TIMESTAMP");
+		tempMap.put("VARBINARY", "CONCAT(?)");
+
+		JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP = Collections
+				.unmodifiableMap(tempMap);
+
+	}
+
+	/**
+	 * Escape process one string
+	 * 
+	 * @param sql
+	 *            the SQL to escape process.
+	 * 
+	 * @return the SQL after it has been escape processed.
+	 * 
+	 * @throws java.sql.SQLException
+	 *             DOCUMENT ME!
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public static final Object escapeSQL(String sql,
+			boolean serverSupportsConvertFn, 
+			Connection conn) throws java.sql.SQLException {
+		boolean replaceEscapeSequence = false;
+		String escapeSequence = null;
+
+		if (sql == null) {
+			return null;
+		}
+
+		/*
+		 * Short circuit this code if we don't have a matching pair of "{}". -
+		 * Suggested by Ryan Gustafason
+		 */
+		int beginBrace = sql.indexOf('{');
+		int nextEndBrace = (beginBrace == -1) ? (-1) : sql.indexOf('}',
+				beginBrace);
+
+		if (nextEndBrace == -1) {
+			return sql;
+		}
+
+		StringBuffer newSql = new StringBuffer();
+
+		EscapeTokenizer escapeTokenizer = new EscapeTokenizer(sql);
+
+		byte usesVariables = Statement.USES_VARIABLES_FALSE;
+		boolean callingStoredFunction = false;
+
+		while (escapeTokenizer.hasMoreTokens()) {
+			String token = escapeTokenizer.nextToken();
+
+			if (token.length() != 0) {
+				if (token.charAt(0) == '{') { // It's an escape code
+
+					if (!token.endsWith("}")) {
+						throw SQLError.createSQLException("Not a valid escape sequence: "
+								+ token);
+					}
+
+					if (token.length() > 2) {
+						int nestedBrace = token.indexOf('{', 2);
+
+						if (nestedBrace != -1) {
+							StringBuffer buf = new StringBuffer(token
+									.substring(0, 1));
+
+							Object remainingResults = escapeSQL(token
+									.substring(1, token.length() - 1),
+									serverSupportsConvertFn, conn);
+
+							String remaining = null;
+
+							if (remainingResults instanceof String) {
+								remaining = (String) remainingResults;
+							} else {
+								remaining = ((EscapeProcessorResult) remainingResults).escapedSql;
+
+								if (usesVariables != Statement.USES_VARIABLES_TRUE) {
+									usesVariables = ((EscapeProcessorResult) remainingResults).usesVariables;
+								}
+							}
+
+							buf.append(remaining);
+
+							buf.append('}');
+
+							token = buf.toString();
+						}
+					}
+
+					// nested escape code
+					// Compare to tokens with _no_ whitespace
+					String collapsedToken = removeWhitespace(token);
+
+					/*
+					 * Process the escape code
+					 */
+					if (StringUtils.startsWithIgnoreCase(collapsedToken,
+							"{escape")) {
+						try {
+							StringTokenizer st = new StringTokenizer(token,
+									" '");
+							st.nextToken(); // eat the "escape" token
+							escapeSequence = st.nextToken();
+
+							if (escapeSequence.length() < 3) {
+								throw SQLError.createSQLException(
+										"Syntax error for escape sequence '"
+												+ token + "'", "42000");
+							}
+
+							escapeSequence = escapeSequence.substring(1,
+									escapeSequence.length() - 1);
+							replaceEscapeSequence = true;
+						} catch (java.util.NoSuchElementException e) {
+							throw SQLError.createSQLException(
+									"Syntax error for escape sequence '"
+											+ token + "'", "42000");
+						}
+					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
+							"{fn")) {
+						int startPos = token.toLowerCase().indexOf("fn ") + 3;
+						int endPos = token.length() - 1; // no }
+
+						String fnToken = token.substring(startPos, endPos);
+
+						// We need to handle 'convert' by ourselves
+
+						if (StringUtils.startsWithIgnoreCaseAndWs(fnToken,
+								"convert")) {
+							newSql.append(processConvertToken(fnToken,
+									serverSupportsConvertFn));
+						} else {
+							// just pass functions right to the DB
+							newSql.append(fnToken);
+						}
+					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
+							"{d")) {
+						int startPos = token.indexOf('\'') + 1;
+						int endPos = token.lastIndexOf('\''); // no }
+
+						if ((startPos == -1) || (endPos == -1)) {
+							throw SQLError.createSQLException(
+									"Syntax error for DATE escape sequence '"
+											+ token + "'", "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");
+						}
+
+						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();
+								} 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);
+									
+									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);
+									
+										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("'");	
+									
+								
+								} 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");
+						}
+					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
+							"{t")) {
+						int startPos = token.indexOf('\'') + 1;
+						int endPos = token.lastIndexOf('\''); // no }
+
+						if ((startPos == -1) || (endPos == -1)) {
+							throw SQLError.createSQLException(
+									"Syntax error for TIME escape sequence '"
+											+ token + "'", "42000");
+						}
+
+						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;
+								
+								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);
+									
+									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 (java.util.NoSuchElementException e) {
+							throw SQLError.createSQLException(
+									"Syntax error for escape sequence '"
+											+ argument + "'", "42000");
+						}
+					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
+							"{call")
+							|| StringUtils.startsWithIgnoreCase(collapsedToken,
+									"{?=call")) {
+
+						int startPos = StringUtils.indexOfIgnoreCase(token,
+								"CALL") + 5;
+						int endPos = token.length() - 1;
+		
+						if (StringUtils.startsWithIgnoreCase(collapsedToken,
+								"{?=call")) {
+							callingStoredFunction = true;
+							newSql.append("SELECT ");
+							newSql.append(token.substring(startPos, endPos));
+						} else {
+							callingStoredFunction = false;
+							newSql.append("CALL ");
+							newSql.append(token.substring(startPos, endPos));
+						}
+						
+						for (int i = endPos - 1; i >= startPos; i--) {
+							char c = token.charAt(i);
+							
+							if (Character.isWhitespace(c)) {
+								continue;
+							}
+							
+							if (c != ')') {
+								newSql.append("()");  // handle no-parenthesis no-arg call not supported
+			                                         // by MySQL parser
+							}
+							
+							break;
+						}
+					} else if (StringUtils.startsWithIgnoreCase(collapsedToken,
+							"{oj")) {
+						// MySQL already handles this escape sequence
+						// because of ODBC. Cool.
+						newSql.append(token);
+					}
+				} else {
+					newSql.append(token); // it's just part of the query
+				}
+			}
+		}
+
+		String escapedSql = newSql.toString();
+
+		//
+		// FIXME: Let MySQL do this, however requires
+		// lightweight parsing of statement
+		//
+		if (replaceEscapeSequence) {
+			String currentSql = escapedSql;
+
+			while (currentSql.indexOf(escapeSequence) != -1) {
+				int escapePos = currentSql.indexOf(escapeSequence);
+				String lhs = currentSql.substring(0, escapePos);
+				String rhs = currentSql.substring(escapePos + 1, currentSql
+						.length());
+				currentSql = lhs + "\\" + rhs;
+			}
+
+			escapedSql = currentSql;
+		}
+
+		EscapeProcessorResult epr = new EscapeProcessorResult();
+		epr.escapedSql = escapedSql;
+		epr.callingStoredFunction = callingStoredFunction;
+
+		if (usesVariables != Statement.USES_VARIABLES_TRUE) {
+			if (escapeTokenizer.sawVariableUse()) {
+				epr.usesVariables = Statement.USES_VARIABLES_TRUE;
+			} else {
+				epr.usesVariables = Statement.USES_VARIABLES_FALSE;
+			}
+		}
+
+		return epr;
+	}
+
+	/**
+	 * Re-writes {fn convert (expr, type)} as cast(expr AS type)
+	 * 
+	 * @param functionToken
+	 * @return
+	 * @throws SQLException
+	 */
+	private static String processConvertToken(String functionToken,
+			boolean serverSupportsConvertFn) throws SQLException {
+		// The JDBC spec requires these types:
+		//
+		// BIGINT
+		// BINARY
+		// BIT
+		// CHAR
+		// DATE
+		// DECIMAL
+		// DOUBLE
+		// FLOAT
+		// INTEGER
+		// LONGVARBINARY
+		// LONGVARCHAR
+		// REAL
+		// SMALLINT
+		// TIME
+		// TIMESTAMP
+		// TINYINT
+		// VARBINARY
+		// VARCHAR
+
+		// MySQL supports these types:
+		//
+		// BINARY
+		// CHAR
+		// DATE
+		// DATETIME
+		// SIGNED (integer)
+		// UNSIGNED (integer)
+		// TIME
+
+		int firstIndexOfParen = functionToken.indexOf("(");
+
+		if (firstIndexOfParen == -1) {
+			throw SQLError.createSQLException(
+					"Syntax error while processing {fn convert (... , ...)} token, missing opening parenthesis in token '"
+							+ functionToken + "'.",
+					SQLError.SQL_STATE_SYNTAX_ERROR);
+		}
+
+		int tokenLength = functionToken.length();
+
+		int indexOfComma = functionToken.lastIndexOf(",");
+
+		if (indexOfComma == -1) {
+			throw SQLError.createSQLException(
+					"Syntax error while processing {fn convert (... , ...)} token, missing comma in token '"
+							+ functionToken + "'.",
+					SQLError.SQL_STATE_SYNTAX_ERROR);
+		}
+
+		int indexOfCloseParen = functionToken.indexOf(')', indexOfComma);
+
+		if (indexOfCloseParen == -1) {
+			throw SQLError.createSQLException(
+					"Syntax error while processing {fn convert (... , ...)} token, missing closing parenthesis in token '"
+							+ functionToken + "'.",
+					SQLError.SQL_STATE_SYNTAX_ERROR);
+
+		}
+
+		String expression = functionToken.substring(firstIndexOfParen + 1,
+				indexOfComma);
+		String type = functionToken.substring(indexOfComma + 1,
+				indexOfCloseParen);
+
+		String newType = null;
+
+		String trimmedType = type.trim();
+
+		if (StringUtils.startsWithIgnoreCase(trimmedType, "SQL_")) {
+			trimmedType = trimmedType.substring(4, trimmedType.length());
+		}
+
+		if (serverSupportsConvertFn) {
+			newType = (String) JDBC_CONVERT_TO_MYSQL_TYPE_MAP.get(trimmedType
+					.toUpperCase(Locale.ENGLISH));
+		} else {
+			newType = (String) JDBC_NO_CONVERT_TO_MYSQL_EXPRESSION_MAP
+					.get(trimmedType.toUpperCase(Locale.ENGLISH));
+
+			// We need a 'special' check here to give a better error message. If
+			// we're in this
+			// block, the version of MySQL we're connected to doesn't support
+			// CAST/CONVERT,
+			// so we can't re-write some data type conversions
+			// (date,time,timestamp, datetime)
+
+			if (newType == null) {
+				throw SQLError.createSQLException(
+						"Can't find conversion re-write for type '"
+								+ type
+								+ "' that is applicable for this server version while processing escape tokens.",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		}
+
+		if (newType == null) {
+			throw SQLError.createSQLException("Unsupported conversion type '"
+					+ type.trim() + "' found while processing escape token.",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		int replaceIndex = newType.indexOf("?");
+
+		if (replaceIndex != -1) {
+			StringBuffer convertRewrite = new StringBuffer(newType.substring(0,
+					replaceIndex));
+			convertRewrite.append(expression);
+			convertRewrite.append(newType.substring(replaceIndex + 1, newType
+					.length()));
+
+			return convertRewrite.toString();
+		} else {
+
+			StringBuffer castRewrite = new StringBuffer("CAST(");
+			castRewrite.append(expression);
+			castRewrite.append(" AS ");
+			castRewrite.append(newType);
+			castRewrite.append(")");
+
+			return castRewrite.toString();
+		}
+	}
+
+	/**
+	 * Removes all whitespace from the given String. We use this to make escape
+	 * token comparison white-space ignorant.
+	 * 
+	 * @param toCollapse
+	 *            the string to remove the whitespace from
+	 * 
+	 * @return a string with _no_ whitespace.
+	 */
+	private static String removeWhitespace(String toCollapse) {
+		if (toCollapse == null) {
+			return null;
+		}
+
+		int length = toCollapse.length();
+
+		StringBuffer collapsed = new StringBuffer(length);
+
+		for (int i = 0; i < length; i++) {
+			char c = toCollapse.charAt(i);
+
+			if (!Character.isWhitespace(c)) {
+				collapsed.append(c);
+			}
+		}
+
+		return collapsed.toString();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeProcessorResult.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeProcessorResult.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeProcessorResult.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+/**
+ * Wraps output from EscapeProcessor, to help prevent multiple passes over the
+ * query string, to detect characters such as '@' (defining/using a variable),
+ * which are used further up the call stack to handle failover.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: EscapeProcessorResult.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+class EscapeProcessorResult {
+	boolean callingStoredFunction = false;
+
+	String escapedSql;
+
+	byte usesVariables = Statement.USES_VARIABLES_FALSE;
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeTokenizer.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeTokenizer.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/EscapeTokenizer.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,194 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+/**
+ * EscapeTokenizer breaks up an SQL statement into SQL and escape code parts.
+ * 
+ * @author Mark Matthews
+ */
+public class EscapeTokenizer {
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	private int bracesLevel = 0;
+
+	private boolean emittingEscapeCode = false;
+
+	private boolean inComment = false;
+
+	private boolean inQuotes = false;
+
+	private char lastChar = 0;
+
+	private char lastLastChar = 0;
+
+	private int pos = 0;
+
+	private char quoteChar = 0;
+
+	private boolean sawVariableUse = false;
+
+	private String source = null;
+
+	private int sourceLength = 0;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new EscapeTokenizer object.
+	 * 
+	 * @param s
+	 *            the string to tokenize
+	 */
+	public EscapeTokenizer(String s) {
+		this.source = s;
+		this.sourceLength = s.length();
+		this.pos = 0;
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Does this tokenizer have more tokens available?
+	 * 
+	 * @return if this tokenizer has more tokens available
+	 */
+	public synchronized boolean hasMoreTokens() {
+		return (this.pos < this.sourceLength);
+	}
+
+	/**
+	 * Returns the next token
+	 * 
+	 * @return the next token.
+	 */
+	public synchronized String nextToken() {
+		StringBuffer tokenBuf = new StringBuffer();
+
+		if (this.emittingEscapeCode) {
+			tokenBuf.append("{"); //$NON-NLS-1$
+			this.emittingEscapeCode = false;
+		}
+
+		for (; this.pos < this.sourceLength; this.pos++) {
+			char c = this.source.charAt(this.pos);
+
+			// Detect variable usage
+
+			if (!this.inQuotes && c == '@') {
+				this.sawVariableUse = true;
+			}
+
+			if (c == '\'' || c == '"') {
+				if (this.inQuotes && c == quoteChar) {
+					if (this.pos + 1 < this.sourceLength) {
+						if (this.source.charAt(this.pos + 1) == quoteChar) {
+							// Doubled-up quote escape
+							tokenBuf.append(quoteChar);
+							tokenBuf.append(quoteChar);
+							this.pos++;
+							continue;
+						}
+					}
+				}
+				if (this.lastChar != '\\') {
+					if (this.inQuotes) {
+						if (this.quoteChar == c) {
+							this.inQuotes = false;
+						}
+					} else {
+						this.inQuotes = true;
+						this.quoteChar = c;
+					}
+				} else if (this.lastLastChar == '\\') {
+					if (this.inQuotes) {
+						if (this.quoteChar == c) {
+							this.inQuotes = false;
+						}
+					} else {
+						this.inQuotes = true;
+						this.quoteChar = c;
+					}
+				}
+
+				tokenBuf.append(c);
+			} else if (c == '-') {
+				if ((this.lastChar == '-')
+						&& ((this.lastLastChar != '\\') & !this.inQuotes)) {
+					this.inComment = true;
+				}
+
+				tokenBuf.append(c);
+			} else if ((c == '\n') || (c == '\r')) {
+				this.inComment = false;
+
+				tokenBuf.append(c);
+			} else if (c == '{') {
+				if (this.inQuotes || this.inComment) {
+					tokenBuf.append(c);
+				} else {
+					this.bracesLevel++;
+
+					if (this.bracesLevel == 1) {
+						this.pos++;
+						this.emittingEscapeCode = true;
+
+						return tokenBuf.toString();
+					}
+
+					tokenBuf.append(c);
+				}
+			} else if (c == '}') {
+				tokenBuf.append(c);
+
+				if (!this.inQuotes && !this.inComment) {
+					this.lastChar = c;
+
+					this.bracesLevel--;
+
+					if (this.bracesLevel == 0) {
+						this.pos++;
+
+						return tokenBuf.toString();
+					}
+				}
+			} else {
+				tokenBuf.append(c);
+			}
+
+			this.lastLastChar = this.lastChar;
+			this.lastChar = c;
+		}
+
+		return tokenBuf.toString();
+	}
+
+	boolean sawVariableUse() {
+		return this.sawVariableUse;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ExportControlled.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ExportControlled.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ExportControlled.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,94 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.IOException;
+
+/**
+ * Holds functionality that falls under export-control regulations.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: ExportControlled.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
+ *          Exp $
+ */
+public class ExportControlled {
+	protected static boolean enabled() {
+		// we may wish to un-static-ify this class
+		// this static method call may be removed entirely by the compiler
+		return true;
+	}
+
+	/**
+	 * Converts the socket being used in the given MysqlIO to an SSLSocket by
+	 * performing the SSL/TLS handshake.
+	 * 
+	 * @param mysqlIO
+	 *            the MysqlIO instance containing the socket to convert to an
+	 *            SSLSocket.
+	 * 
+	 * @throws CommunicationsException
+	 *             if the handshake fails, or if this distribution of
+	 *             Connector/J doesn't contain the SSL crytpo hooks needed to
+	 *             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();
+
+		try {
+			mysqlIO.mysqlConnection = sslFact.createSocket(
+					mysqlIO.mysqlConnection, mysqlIO.host, mysqlIO.port, true);
+
+			// need to force TLSv1, or else JSSE tries to do a SSLv2 handshake
+			// which MySQL doesn't understand
+			((javax.net.ssl.SSLSocket) mysqlIO.mysqlConnection)
+					.setEnabledProtocols(new String[] { "TLSv1" }); //$NON-NLS-1$
+			((javax.net.ssl.SSLSocket) mysqlIO.mysqlConnection)
+					.startHandshake();
+
+			if (mysqlIO.connection.getUseUnbufferedInput()) {
+				mysqlIO.mysqlInput = mysqlIO.mysqlConnection.getInputStream();
+			} else {
+				mysqlIO.mysqlInput = new BufferedInputStream(
+						mysqlIO.mysqlConnection.getInputStream(), 16384);
+			}
+
+			mysqlIO.mysqlOutput = new BufferedOutputStream(
+					mysqlIO.mysqlConnection.getOutputStream(), 16384);
+
+			mysqlIO.mysqlOutput.flush();
+		} catch (IOException ioEx) {
+			throw new CommunicationsException(mysqlIO.connection,
+					mysqlIO.lastPacketSentTimeMs, ioEx);
+		}
+	}
+
+	private ExportControlled() { /* prevent instantiation */
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Field.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Field.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Field.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,889 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.UnsupportedEncodingException;
+import java.sql.SQLException;
+import java.sql.Types;
+
+/**
+ * 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 $
+ */
+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;
+
+	private String charsetName = null;
+
+	private int colDecimals;
+
+	private short colFlag;
+
+	private String collationName = null;
+
+	private Connection connection = null;
+
+	private String databaseName = null;
+
+	private int databaseNameLength = -1;
+
+	// database name info
+	private int databaseNameStart = -1;
+
+	private int defaultValueLength = -1;
+
+	// default value info - from COM_LIST_FIELDS execution
+	private int defaultValueStart = -1;
+
+	private String fullName = null;
+
+	private String fullOriginalName = null;
+
+	private boolean isImplicitTempTable = false;
+
+	private long length; // Internal length of the field;
+
+	private int mysqlType = -1; // the MySQL type
+
+	private String name; // The Field name
+
+	private int nameLength;
+
+	private int nameStart;
+
+	private String originalColumnName = null;
+
+	private int originalColumnNameLength = -1;
+
+	// column name info (before aliasing)
+	private int originalColumnNameStart = -1;
+
+	private String originalTableName = null;
+
+	private int originalTableNameLength = -1;
+
+	// table name info (before aliasing)
+	private int originalTableNameStart = -1;
+
+	private int precisionAdjustFactor = 0;
+
+	private int sqlType = -1; // the java.sql.Type
+
+	private String tableName; // The Name of the Table
+
+	private int tableNameLength;
+
+	private int tableNameStart;
+	
+	private boolean useOldNameMetadata = false;
+
+	private boolean isSingleBit;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Constructor used when communicating with 4.1 and newer servers
+	 */
+	Field(Connection conn, byte[] buffer, int databaseNameStart,
+			int databaseNameLength, int tableNameStart, int tableNameLength,
+			int originalTableNameStart, int originalTableNameLength,
+			int nameStart, int nameLength, int originalColumnNameStart,
+			int originalColumnNameLength, long length, int mysqlType,
+			short colFlag, int colDecimals, int defaultValueStart,
+			int defaultValueLength, int charsetIndex) throws SQLException {
+		this.connection = conn;
+		this.buffer = buffer;
+		this.nameStart = nameStart;
+		this.nameLength = nameLength;
+		this.tableNameStart = tableNameStart;
+		this.tableNameLength = tableNameLength;
+		this.length = length;
+		this.colFlag = colFlag;
+		this.colDecimals = colDecimals;
+		this.mysqlType = mysqlType;
+
+		// 4.1 field info...
+		this.databaseNameStart = databaseNameStart;
+		this.databaseNameLength = databaseNameLength;
+
+		this.originalTableNameStart = originalTableNameStart;
+		this.originalTableNameLength = originalTableNameLength;
+
+		this.originalColumnNameStart = originalColumnNameStart;
+		this.originalColumnNameLength = originalColumnNameLength;
+
+		this.defaultValueStart = defaultValueStart;
+		this.defaultValueLength = defaultValueLength;
+
+		// If we're not running 4.1 or newer, use the connection's
+		// charset
+		this.charsetIndex = charsetIndex;
+
+		
+		// Map MySqlTypes to java.sql Types
+		this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
+		
+		// Re-map to 'real' blob type, if we're a BLOB
+
+		if (this.mysqlType == MysqlDefs.FIELD_TYPE_BLOB) {
+			if (this.charsetIndex == 63 || 
+					!this.connection.versionMeetsMinimum(4, 1, 0)) {
+				setBlobTypeBasedOnLength();
+				this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
+			} else {
+				// *TEXT masquerading as blob
+				this.mysqlType = MysqlDefs.FIELD_TYPE_VAR_STRING;
+				this.sqlType = Types.LONGVARCHAR;
+			}
+		}
+
+		if (this.sqlType == Types.TINYINT && this.length == 1
+				&& this.connection.getTinyInt1isBit()) {
+			// Adjust for pseudo-boolean
+			if (conn.getTinyInt1isBit()) {
+				if (conn.getTransformedBitIsBoolean()) {
+					this.sqlType = Types.BOOLEAN;
+				} else {
+					this.sqlType = Types.BIT;
+				}
+			}
+
+		}
+		
+		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 && 
+					isBinary &&
+					this.charsetIndex == 63) {
+				if (this.isOpaqueBinary()) {
+					this.sqlType = Types.VARBINARY;
+				}
+			} 
+			
+			if (this.connection.versionMeetsMinimum(4, 1, 0) &&
+					this.mysqlType == MysqlDefs.FIELD_TYPE_STRING && 
+					isBinary && this.charsetIndex == 63) {
+				//
+				// Okay, this is a hack, but there's currently no way
+				// to easily distinguish something like DATE_FORMAT( ..)
+				// from the "BINARY" column type, other than looking
+				// at the original column name.
+				//
+				
+				if (isOpaqueBinary()) {
+					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
+				}
+			}
+	
+			//
+			// Handle TEXT type (special case), Fix proposed by Peter McKeown
+			//
+			if ((this.sqlType == java.sql.Types.LONGVARBINARY) && !isBinary) {
+				this.sqlType = java.sql.Types.LONGVARCHAR;
+			} else if ((this.sqlType == java.sql.Types.VARBINARY) && !isBinary) {
+				this.sqlType = java.sql.Types.VARCHAR;
+			}
+			
+			checkForImplicitTemporaryTable();
+		} else {
+			this.charsetName = "US-ASCII";
+		}
+
+		//
+		// Handle odd values for 'M' for floating point/decimal numbers
+		//
+		if (!isUnsigned()) {
+			switch (this.mysqlType) {
+			case MysqlDefs.FIELD_TYPE_DECIMAL:
+			case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
+				this.precisionAdjustFactor = -1;
+
+				break;
+			case MysqlDefs.FIELD_TYPE_DOUBLE:
+			case MysqlDefs.FIELD_TYPE_FLOAT:
+				this.precisionAdjustFactor = 1;
+
+				break;
+			}
+		} else {
+			switch (this.mysqlType) {
+			case MysqlDefs.FIELD_TYPE_DOUBLE:
+			case MysqlDefs.FIELD_TYPE_FLOAT:
+				this.precisionAdjustFactor = 1;
+
+				break;
+			}
+		}
+	}
+
+	/**
+	 * Constructor used when communicating with pre 4.1 servers
+	 */
+	Field(Connection 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,
+				nameStart, nameLength, -1, -1, length, mysqlType, colFlag,
+				colDecimals, -1, -1, NO_CHARSET_INFO);
+	}
+
+	/**
+	 * Constructor used by DatabaseMetaData methods.
+	 */
+	Field(String tableName, String columnName, int jdbcType, int length) {
+		this.tableName = tableName;
+		this.name = columnName;
+		this.length = length;
+		this.sqlType = jdbcType;
+		this.colFlag = 0;
+		this.colDecimals = 0;
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	private void checkForImplicitTemporaryTable() {
+		this.isImplicitTempTable = this.tableNameLength > 5
+				&& this.buffer[tableNameStart] == (byte) '#'
+				&& this.buffer[tableNameStart + 1] == (byte) 's'
+				&& this.buffer[tableNameStart + 2] == (byte) 'q'
+				&& this.buffer[tableNameStart + 3] == (byte) 'l'
+				&& this.buffer[tableNameStart + 4] == (byte) '_';
+	}
+
+	/**
+	 * Returns the character set (if known) for this field.
+	 * 
+	 * @return the character set
+	 */
+	public String getCharacterSet() throws SQLException {
+		return this.charsetName;
+	}
+
+	public synchronized String getCollation() throws SQLException {
+		if (this.collationName == null) {
+			if (this.connection != null) {
+				if (this.connection.versionMeetsMinimum(4, 1, 0)) {
+					java.sql.DatabaseMetaData dbmd = this.connection
+							.getMetaData();
+
+					String quotedIdStr = dbmd.getIdentifierQuoteString();
+
+					if (" ".equals(quotedIdStr)) { //$NON-NLS-1$
+						quotedIdStr = ""; //$NON-NLS-1$
+					}
+
+					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);
+
+						java.sql.Statement collationStmt = null;
+						java.sql.ResultSet collationRs = null;
+
+						try {
+							collationStmt = this.connection.createStatement();
+
+							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$
+
+									break;
+								}
+							}
+						} finally {
+							if (collationRs != null) {
+								collationRs.close();
+								collationRs = null;
+							}
+
+							if (collationStmt != null) {
+								collationStmt.close();
+								collationStmt = null;
+							}
+						}
+
+					}
+				}
+
+			}
+
+		}
+
+		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 {
+		if ((this.databaseName == null) && (this.databaseNameStart != -1)
+				&& (this.databaseNameLength != -1)) {
+			this.databaseName = getStringFromBytes(this.databaseNameStart,
+					this.databaseNameLength);
+		}
+
+		return this.databaseName;
+	}
+
+	int getDecimals() {
+		return this.colDecimals;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public String getFullName() throws SQLException {
+		if (this.fullName == null) {
+			StringBuffer fullNameBuf = new StringBuffer(getTableName().length()
+					+ 1 + getName().length());
+			fullNameBuf.append(this.tableName);
+
+			// much faster to append a char than a String
+			fullNameBuf.append('.');
+			fullNameBuf.append(this.name);
+			this.fullName = fullNameBuf.toString();
+			fullNameBuf = null;
+		}
+
+		return this.fullName;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public String getFullOriginalName() throws SQLException {
+		getOriginalName();
+
+		if (this.originalColumnName == null) {
+			return null; // we don't have this information
+		}
+
+		if (this.fullName == null) {
+			StringBuffer fullOriginalNameBuf = new StringBuffer(
+					getOriginalTableName().length() + 1
+							+ getOriginalName().length());
+			fullOriginalNameBuf.append(this.originalTableName);
+
+			// much faster to append a char than a String
+			fullOriginalNameBuf.append('.');
+			fullOriginalNameBuf.append(this.originalColumnName);
+			this.fullOriginalName = fullOriginalNameBuf.toString();
+			fullOriginalNameBuf = null;
+		}
+
+		return this.fullOriginalName;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public long getLength() {
+		return this.length;
+	}
+
+	public int getMaxBytesPerCharacter() throws SQLException {
+		return this.connection.getMaxBytesPerChar(getCharacterSet());
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public int getMysqlType() {
+		return this.mysqlType;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public String getName() throws SQLException {
+		if (this.name == null) {
+			this.name = getStringFromBytes(this.nameStart, this.nameLength);
+		}
+
+		return this.name;
+	}
+
+	public String getNameNoAliases() throws SQLException {
+		if (this.useOldNameMetadata) {
+			return getName();
+		}
+		
+		if (this.connection != null && 
+				this.connection.versionMeetsMinimum(4, 1, 0)) {
+			return getOriginalName();
+		}
+		
+		return getName();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public String getOriginalName() throws SQLException {
+		if ((this.originalColumnName == null)
+				&& (this.originalColumnNameStart != -1)
+				&& (this.originalColumnNameLength != -1)) {
+			this.originalColumnName = getStringFromBytes(
+					this.originalColumnNameStart, this.originalColumnNameLength);
+		}
+
+		return this.originalColumnName;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public String getOriginalTableName() throws SQLException {
+		if ((this.originalTableName == null)
+				&& (this.originalTableNameStart != -1)
+				&& (this.originalTableNameLength != -1)) {
+			this.originalTableName = getStringFromBytes(
+					this.originalTableNameStart, this.originalTableNameLength);
+		}
+
+		return this.originalTableName;
+	}
+
+	/**
+	 * 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() {
+		return this.precisionAdjustFactor;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public int getSQLType() {
+		return this.sqlType;
+	}
+
+	/**
+	 * Create a string with the correct charset encoding from the byte-buffer
+	 * that contains the data for this field
+	 */
+	private String getStringFromBytes(int stringStart, int stringLength)
+			throws SQLException {
+		if ((stringStart == -1) || (stringLength == -1)) {
+			return null;
+		}
+
+		String stringVal = null;
+
+		if (this.connection != null) {
+			if (this.connection.getUseUnicode()) {
+				String encoding = this.connection.getCharacterSetMetadata();
+
+				if (encoding == null) {
+					encoding = connection.getEncoding();
+				}
+
+				if (encoding != null) {
+					SingleByteCharsetConverter converter = null;
+
+					if (this.connection != null) {
+						converter = this.connection
+								.getCharsetConverter(encoding);
+					}
+
+					if (converter != null) { // we have a converter
+						stringVal = converter.toString(this.buffer,
+								stringStart, stringLength);
+					} else {
+						// we have no converter, use JVM converter
+						byte[] stringBytes = new byte[stringLength];
+
+						int endIndex = stringStart + stringLength;
+						int pos = 0;
+
+						for (int i = stringStart; i < endIndex; i++) {
+							stringBytes[pos++] = this.buffer[i];
+						}
+
+						try {
+							stringVal = new String(stringBytes, encoding);
+						} catch (UnsupportedEncodingException ue) {
+							throw new RuntimeException(Messages
+									.getString("Field.12") + encoding //$NON-NLS-1$
+									+ Messages.getString("Field.13")); //$NON-NLS-1$
+						}
+					}
+				} else {
+					// we have no encoding, use JVM standard charset
+					stringVal = StringUtils.toAsciiString(this.buffer,
+							stringStart, stringLength);
+				}
+			} else {
+				// we are not using unicode, so use JVM standard charset
+				stringVal = StringUtils.toAsciiString(this.buffer, stringStart,
+						stringLength);
+			}
+		} else {
+			// we don't have a connection, so punt
+			stringVal = StringUtils.toAsciiString(this.buffer, stringStart,
+					stringLength);
+		}
+
+		return stringVal;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public String getTable() throws SQLException {
+		return getTableName();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public String getTableName() throws SQLException {
+		if (this.tableName == null) {
+			this.tableName = getStringFromBytes(this.tableNameStart,
+					this.tableNameLength);
+		}
+
+		return this.tableName;
+	}
+
+	public String getTableNameNoAliases() throws SQLException {
+		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() {
+		return ((this.colFlag & AUTO_INCREMENT_FLAG) > 0);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isBinary() {
+		return ((this.colFlag & 128) > 0);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isBlob() {
+		return ((this.colFlag & 16) > 0);
+	}
+
+	/**
+	 * Is this field owned by a server-created temporary table?
+	 * 
+	 * @return
+	 */
+	private boolean isImplicitTemporaryTable() {
+		return this.isImplicitTempTable;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isMultipleKey() {
+		return ((this.colFlag & 8) > 0);
+	}
+
+	boolean isNotNull() {
+		return ((this.colFlag & 1) > 0);
+	}
+
+	boolean isOpaqueBinary() throws SQLException {
+
+		//
+		// Detect CHAR(n) CHARACTER SET BINARY which is a synonym for
+		// fixed-length binary types
+		//
+
+		if (this.charsetIndex == 63 && isBinary()
+				&& (this.getMysqlType() == MysqlDefs.FIELD_TYPE_STRING ||
+				this.getMysqlType() == MysqlDefs.FIELD_TYPE_VAR_STRING)) {
+
+			if (this.originalTableNameLength == 0) {
+				return false; // Probably from function
+			}
+
+			// Okay, queries resolved by temp tables also have this 'signature',
+			// check for that
+
+			return !isImplicitTemporaryTable();
+		}
+
+		return (this.connection.versionMeetsMinimum(4, 1, 0) && "binary"
+				.equalsIgnoreCase(getCharacterSet()));
+
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isPrimaryKey() {
+		return ((this.colFlag & 2) > 0);
+	}
+
+	/**
+	 * Is this field _definitely_ not writable?
+	 * 
+	 * @return true if this field can not be written to in an INSERT/UPDATE
+	 *         statement.
+	 */
+	boolean isReadOnly() throws SQLException {
+		if (this.connection.versionMeetsMinimum(4, 1, 0)) {
+			String orgColumnName = getOriginalName();
+			String orgTableName = getOriginalTableName();
+
+			return !(orgColumnName != null && orgColumnName.length() > 0
+					&& orgTableName != null && orgTableName.length() > 0);
+		}
+
+		return false; // we can't tell definitively in this case.
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isUniqueKey() {
+		return ((this.colFlag & 4) > 0);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isUnsigned() {
+		return ((this.colFlag & 32) > 0);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isZeroFill() {
+		return ((this.colFlag & 64) > 0);
+	}
+
+	//
+	// MySQL only has one protocol-level BLOB type that it exposes
+	// which is FIELD_TYPE_BLOB, although we can divine what the
+	// actual type is by the length reported ...
+	//
+	private void setBlobTypeBasedOnLength() {
+		if (this.length == MysqlDefs.LENGTH_TINYBLOB) {
+			this.mysqlType = MysqlDefs.FIELD_TYPE_TINY_BLOB;
+		} else if (this.length == MysqlDefs.LENGTH_BLOB) {
+			this.mysqlType = MysqlDefs.FIELD_TYPE_BLOB;
+		} else if (this.length == MysqlDefs.LENGTH_MEDIUMBLOB) {
+			this.mysqlType = MysqlDefs.FIELD_TYPE_MEDIUM_BLOB;
+		} else if (this.length == MysqlDefs.LENGTH_LONGBLOB) {
+			this.mysqlType = MysqlDefs.FIELD_TYPE_LONG_BLOB;
+		}
+	}
+	
+	private boolean isNativeNumericType() {
+		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 ||
+				this.mysqlType == MysqlDefs.FIELD_TYPE_DATETIME ||
+				this.mysqlType == MysqlDefs.FIELD_TYPE_TIME ||
+				this.mysqlType == MysqlDefs.FIELD_TYPE_TIMESTAMP);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param conn
+	 *            DOCUMENT ME!
+	 */
+	public void setConnection(Connection conn) {
+		this.connection = conn;
+
+		this.charsetName = this.connection.getEncoding();
+	}
+
+	void setMysqlType(int type) {
+		this.mysqlType = type;
+		this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType);
+	}
+
+	protected void setUseOldNameMetadata(boolean useOldNameMetadata) {
+		this.useOldNameMetadata = useOldNameMetadata;
+	}
+
+	public String toString() {
+		try {
+			StringBuffer asString = new StringBuffer();
+			asString.append(super.toString());
+
+			asString.append("\n  catalog: ");
+			asString.append(this.getDatabaseName());
+			asString.append("\n  table name: ");
+			asString.append(this.getTableName());
+			asString.append("\n  original table name: ");
+			asString.append(this.getOriginalTableName());
+			asString.append("\n  column name: ");
+			asString.append(this.getName());
+			asString.append("\n  original column name: ");
+			asString.append(this.getOriginalName());
+			asString.append("\n  MySQL data type: ");
+			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));
+			}
+
+			return asString.toString();
+		} catch (Throwable t) {
+			return super.toString();
+		}
+	}
+
+	protected boolean isSingleBit() {
+		return this.isSingleBit;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/LicenseConfiguration.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/LicenseConfiguration.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/LicenseConfiguration.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,59 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+import java.util.Map;
+
+/**
+ * Used in commercially-licensed clients that require connections to
+ * commercially-licensed servers as part of the licensing terms.
+ * 
+ * @author Mark Matthews
+ * @version $Id: LicenseConfiguration.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+class LicenseConfiguration {
+
+	/**
+	 * Used in commercially-licensed clients that require connections to
+	 * commercially-licensed servers as part of the licensing terms.
+	 * 
+	 * @param serverVariables
+	 *            a Map of the output of 'show variables' from the server we're
+	 *            connecting to.
+	 * 
+	 * @throws SQLException
+	 *             if commercial license is required, but not found
+	 */
+	static void checkLicenseType(Map serverVariables) throws SQLException {
+		// This is a GPL build, so we don't check anything...
+	}
+
+	private LicenseConfiguration() {
+		// this is a static utility class
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/LocalizedErrorMessages.properties
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/LocalizedErrorMessages.properties	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/LocalizedErrorMessages.properties	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,404 @@
+#
+# Fixed
+#
+
+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.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}.
+ResultSet.Bad_format_for_Column=Bad format for {0} ''{1}'' in column {2} ({3}).
+ResultSet.Bad_format_for_number=Bad format for number ''{0}'' in column {1}.
+ResultSet.Illegal_operation_on_empty_result_set=Illegal operation on empty result set.
+
+Statement.0=Connection is closed.
+Statement.2=Unsupported character encoding ''{0}''
+Statement.5=Illegal value for setFetchDirection().
+Statement.7=Illegal value for setFetchSize().
+Statement.11=Illegal value for setMaxFieldSize().
+Statement.13=Can not set max field size > max allowed packet of {0} bytes.
+Statement.15=setMaxRows() out of range. 
+Statement.19=Illegal flag for getMoreResults(int).
+Statement.21=Illegal value for setQueryTimeout().
+Statement.27=Connection is read-only. 
+Statement.28=Queries leading to data modification are not allowed.
+Statement.34=Connection is read-only. 
+Statement.35=Queries leading to data modification are not allowed.
+Statement.40=Can not issue INSERT/UPDATE/DELETE with executeQuery().
+Statement.42=Connection is read-only. 
+Statement.43=Queries leading to data modification are not allowed.
+Statement.46=Can not issue SELECT via executeUpdate().
+Statement.49=No operations allowed after statement closed.
+Statement.57=Can not issue data manipulation statements with executeQuery().
+Statement.59=Can not issue NULL query.
+Statement.61=Can not issue empty query.
+Statement.63=Statement not closed explicitly. You should call close() on created
+Statement.64=Statement instances from your code to be more efficient.
+
+UpdatableResultSet.1=Can not call deleteRow() when on insert row.
+UpdatableResultSet.2=Can not call deleteRow() on empty result set.
+UpdatableResultSet.3=Before start of result set. Can not call deleteRow().
+UpdatableResultSet.4=After end of result set. Can not call deleteRow().
+UpdatableResultSet.7=Not on insert row.
+UpdatableResultSet.8=Can not call refreshRow() when on insert row.
+UpdatableResultSet.9=Can not call refreshRow() on empty result set.
+UpdatableResultSet.10=Before start of result set. Can not call refreshRow().
+UpdatableResultSet.11=After end of result set. Can not call refreshRow().
+UpdatableResultSet.12=refreshRow() called on row that has been deleted or had primary key changed.
+UpdatableResultSet.34=Updatable result set created, but never updated. You should only create updatable result sets when you want to update/insert/delete values using the updateRow(), deleteRow() and insertRow() methods.
+UpdatableResultSet.39=Unsupported character encoding ''{0}''.
+UpdatableResultSet.43=Can not create updatable result sets when there is no currently selected database and MySQL server version < 4.1.
+
+#
+# Possible re-names
+#
+
+ResultSet.Query_generated_no_fields_for_ResultSet_57=Query generated no fields for ResultSet
+ResultSet.Illegal_value_for_fetch_direction_64=Illegal value for fetch direction
+ResultSet.Value_must_be_between_0_and_getMaxRows()_66=Value must be between 0 and getMaxRows()
+ResultSet.Query_generated_no_fields_for_ResultSet_99=Query generated no fields for ResultSet
+ResultSet.Cannot_absolute_position_to_row_0_110=Cannot absolute position to row 0
+ResultSet.Operation_not_allowed_after_ResultSet_closed_144=Operation not allowed after ResultSet closed
+ResultSet.Before_start_of_result_set_146=Before start of result set
+ResultSet.After_end_of_result_set_148=After end of result set
+ResultSet.Query_generated_no_fields_for_ResultSet_133=Query generated no fields for ResultSet
+ResultSet.ResultSet_is_from_UPDATE._No_Data_115=ResultSet is from UPDATE. No Data.
+ResultSet.N/A_159=N/A
+
+#
+# To fix
+#
+
+ResultSet.Invalid_value_for_getFloat()_-____68=Invalid value for getFloat() - \'
+ResultSet.Invalid_value_for_getInt()_-____74=Invalid value for getInt() - \'
+ResultSet.Invalid_value_for_getLong()_-____79=Invalid value for getLong() - \'
+ResultSet.Invalid_value_for_getFloat()_-____200=Invalid value for getFloat() - \'
+ResultSet.___in_column__201=\' in column 
+ResultSet.Invalid_value_for_getInt()_-____206=Invalid value for getInt() - \'
+ResultSet.___in_column__207=\' in column 
+ResultSet.Invalid_value_for_getLong()_-____211=Invalid value for getLong() - \'
+ResultSet.___in_column__212=\' in column 
+ResultSet.Invalid_value_for_getShort()_-____217=Invalid value for getShort() - \'
+ResultSet.___in_column__218=\' in column 
+
+ResultSet.Class_not_found___91=Class not found: 
+ResultSet._while_reading_serialized_object_92=\ while reading serialized object
+
+ResultSet.Invalid_value_for_getShort()_-____96=Invalid value for getShort() - \'
+ResultSet.Unsupported_character_encoding____101=Unsupported character encoding \'
+
+ResultSet.Malformed_URL____104=Malformed URL \'
+ResultSet.Malformed_URL____107=Malformed URL \'
+ResultSet.Malformed_URL____141=Malformed URL \'
+
+ResultSet.Column____112=Column \'
+ResultSet.___not_found._113=\' not found.
+
+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.
+
+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.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]
+ResultSet.Bad_format_for_Date____180=Bad format for Date \'
+
+ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__223=Timestamp too small to convert to Time value in column 
+ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__227=Precision lost converting TIMESTAMP to Time with getTime() on column 
+ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__230=Precision lost converting DATETIME to Time with getTime() on column 
+ResultSet.Bad_format_for_Time____233=Bad format for Time \'
+ResultSet.___in_column__234=\' in column 
+ResultSet.Bad_format_for_Timestamp____244=Bad format for Timestamp \'
+ResultSet.___in_column__245=\' in column 
+ResultSet.Cannot_convert_value____249=Cannot convert value \'
+ResultSet.___from_column__250=\' from column 
+ResultSet._)_to_TIMESTAMP._252=\ ) to TIMESTAMP.
+ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257=Timestamp too small to convert to Time value in column 
+ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261=Precision lost converting TIMESTAMP to Time with getTime() on column 
+ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264=Precision lost converting DATETIME to Time with getTime() on column 
+ResultSet.Bad_format_for_Time____267=Bad format for Time \'
+ResultSet.___in_column__268=\' in column 
+ResultSet.Bad_format_for_Timestamp____278=Bad format for Timestamp \'
+ResultSet.___in_column__279=\' in column 
+ResultSet.Cannot_convert_value____283=Cannot convert value \'
+ResultSet.___from_column__284=\' from column 
+ResultSet._)_to_TIMESTAMP._286=\ ) to TIMESTAMP.
+CallableStatement.2=Parameter name can not be NULL or zero-length.
+CallableStatement.3=No parameter named '
+CallableStatement.4='
+CallableStatement.5=Parameter named '
+CallableStatement.6=' is not an OUT parameter
+CallableStatement.7=No output parameters registered.
+CallableStatement.8=No output parameters returned by procedure.
+CallableStatement.9=Parameter number 
+CallableStatement.10=\ is not an OUT parameter
+CallableStatement.11=Parameter index of 
+CallableStatement.12=\ is out of range (1, 
+CallableStatement.13=)
+CallableStatement.14=Can not use streaming result sets with callable statements that have output parameters
+CallableStatement.1=Unable to retrieve metadata for procedure.
+CallableStatement.0=Parameter name can not be 
+CallableStatement.15=null.
+CallableStatement.16=empty.
+CallableStatement.21=Parameter 
+CallableStatement.22=\ is not registered as an output parameter
+CommunicationsException.2=\ is longer than the server configured value of 
+CommunicationsException.3='wait_timeout'
+CommunicationsException.4='interactive_timeout'
+CommunicationsException.5=may or may not be greater than the server-side timeout 
+CommunicationsException.6=(the driver was unable to determine the value of either the 
+CommunicationsException.7='wait_timeout' or 'interactive_timeout' configuration values from 
+CommunicationsException.8=the server.
+CommunicationsException.9=The last communications with the server was 
+CommunicationsException.10=\ seconds ago, which 
+CommunicationsException.11=. You should consider either expiring and/or testing connection validity 
+CommunicationsException.12=before use in your application, increasing the server configured values for client timeouts, 
+CommunicationsException.13=or using the Connector/J connection property 'autoReconnect=true' to avoid this problem.
+CommunicationsException.14=The driver was unable to create a connection due to 
+CommunicationsException.15=an inability to establish the client portion of a socket.\n\n
+CommunicationsException.16=This is usually caused by a limit on the number of sockets imposed by 
+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.20=Communications link failure
+CommunicationsException.21=\ due to underlying exception: 
+NonRegisteringDriver.3=Hostname of MySQL Server
+NonRegisteringDriver.7=Port number of MySQL Server
+NonRegisteringDriver.13=Username to authenticate as
+NonRegisteringDriver.16=Password to use for authentication
+NonRegisteringDriver.17=Cannot load connection class because of underlying exception: '
+NonRegisteringDriver.18='.
+NonRegisteringDriver.37=Must specify port after ':' in connection string
+SQLError.35=Disconnect error
+SQLError.36=Data truncated
+SQLError.37=Privilege not revoked
+SQLError.38=Invalid connection string attribute
+SQLError.39=Error in row
+SQLError.40=No rows updated or deleted
+SQLError.41=More than one row updated or deleted
+SQLError.42=Wrong number of parameters
+SQLError.43=Unable to connect to data source
+SQLError.44=Connection in use
+SQLError.45=Connection not open
+SQLError.46=Data source rejected establishment of connection
+SQLError.47=Connection failure during transaction
+SQLError.48=Communication link failure
+SQLError.49=Insert value list does not match column list
+SQLError.50=Numeric value out of range
+SQLError.51=Datetime field overflow
+SQLError.52=Division by zero
+SQLError.53=Deadlock found when trying to get lock; Try restarting transaction
+SQLError.54=Invalid authorization specification
+SQLError.55=Syntax error or access violation
+SQLError.56=Base table or view not found
+SQLError.57=Base table or view already exists
+SQLError.58=Base table not found
+SQLError.59=Index already exists
+SQLError.60=Index not found
+SQLError.61=Column already exists
+SQLError.62=Column not found
+SQLError.63=No default for column
+SQLError.64=General error
+SQLError.65=Memory allocation failure
+SQLError.66=Invalid column number
+SQLError.67=Invalid argument value
+SQLError.68=Driver not capable
+SQLError.69=Timeout expired
+ChannelBuffer.0=Unsupported character encoding '
+ChannelBuffer.1='
+Field.12=Unsupported character encoding '
+Field.13='
+Blob.0=indexToWriteAt must be >= 1
+Blob.1=IO Error while writing bytes to blob
+Blob.2=Position 'pos' can not be < 1
+StringUtils.0=Unsupported character encoding '
+StringUtils.1='.
+StringUtils.5=Unsupported character encoding '
+StringUtils.6='.
+StringUtils.10=Unsupported character encoding '
+StringUtils.11='.
+RowDataDynamic.2=WARN: Possible incomplete traversal of result set. Streaming result set had 
+RowDataDynamic.3=\ rows left to read when it was closed.
+RowDataDynamic.4=\n\nYou should consider re-formulating your query to 
+RowDataDynamic.5=return only the rows you are interested in using.
+RowDataDynamic.6=\n\nResultSet was created at: 
+RowDataDynamic.7=\n\nNested Stack Trace:\n
+RowDataDynamic.8=Error retrieving record: Unexpected Exception: 
+RowDataDynamic.9=\ message given: 
+RowDataDynamic.10=Operation not supported for streaming result sets
+Clob.0=indexToWriteAt must be >= 1
+Clob.1=indexToWriteAt must be >= 1
+Clob.2=Starting position can not be < 1
+Clob.3=String to set can not be NULL
+Clob.4=Starting position can not be < 1
+Clob.5=String to set can not be NULL
+Clob.6=CLOB start position can not be < 1
+Clob.7=CLOB start position + length can not be > length of CLOB
+Clob.8=Illegal starting position for search, '
+Clob.9='
+Clob.10=Starting position for search is past end of CLOB
+Clob.11=Cannot truncate CLOB of length 
+Clob.12=\ to length of 
+Clob.13=.
+PacketTooBigException.0=Packet for query is too large (
+PacketTooBigException.1=\ > 
+PacketTooBigException.2=). 
+PacketTooBigException.3=You can change this value on the server by setting the 
+PacketTooBigException.4=max_allowed_packet' variable.
+Util.1=\n\n** BEGIN NESTED EXCEPTION ** \n\n
+Util.2=\nMESSAGE: 
+Util.3=\n\nSTACKTRACE:\n\n
+Util.4=\n\n** END NESTED EXCEPTION **\n\n
+MiniAdmin.0=Conection can not be null.
+MiniAdmin.1=MiniAdmin can only be used with MySQL connections
+NamedPipeSocketFactory.2=Can not specify NULL or empty value for property '
+NamedPipeSocketFactory.3='.
+NamedPipeSocketFactory.4=Named pipe path can not be null or empty
+MysqlIO.1=Unexpected end of input stream
+MysqlIO.2=Reading packet of length 
+MysqlIO.3=\nPacket header:\n
+MysqlIO.4=readPacket() payload:\n
+MysqlIO.8=Slow query explain results for '
+MysqlIO.9=' :\n\n
+MysqlIO.10=\ message from server: "
+MysqlIO.15=SSL Connection required, but not supported by server.
+MysqlIO.17=Attempt to close streaming result set 
+MysqlIO.18=\ when no streaming  result set was registered. This is an internal error.
+MysqlIO.19=Attempt to close streaming result set 
+MysqlIO.20=\ that was not registered.
+MysqlIO.21=\ Only one streaming result set may be open and in use per-connection. Ensure that you have called .close() on 
+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.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: 
+MysqlIO.35=The following query was executed using no index, use 'EXPLAIN' for more details: 
+MysqlIO.36=\n\nLarge packet dump truncated at 
+MysqlIO.37=\ bytes.
+MysqlIO.39=Streaming result set 
+MysqlIO.40=\ is still active.
+MysqlIO.41=\ No statements may be issued when any streaming result sets are open and in use on a given connection.
+MysqlIO.42=\ Ensure that you have called .close() on any active streaming result sets before attempting more queries.
+MysqlIO.43=Unexpected end of input stream
+MysqlIO.44=Reading reusable packet of length 
+MysqlIO.45=\nPacket header:\n
+MysqlIO.46=reuseAndReadPacket() payload:\n
+MysqlIO.47=Unexpected end of input stream
+MysqlIO.48=Unexpected end of input stream
+MysqlIO.49=Packets received out of order
+MysqlIO.50=Short read from server, expected 
+MysqlIO.51=\ bytes, received only 
+MysqlIO.53=Packets received out of order
+MysqlIO.54=Short read from server, expected 
+MysqlIO.55=\ bytes, received only 
+MysqlIO.57=send() compressed packet:\n
+MysqlIO.58=\n\nOriginal packet (uncompressed):\n
+MysqlIO.59=send() packet payload:\n
+MysqlIO.60=Unable to open file 
+MysqlIO.63=for 'LOAD DATA LOCAL INFILE' command.
+MysqlIO.64=Due to underlying IOException: 
+MysqlIO.65=Unable to close local file during LOAD DATA LOCAL INFILE command
+MysqlIO.68=\ message from server: "
+MysqlIO.70=Unknown column
+MysqlIO.72=\ message from server: "
+MysqlIO.75=No name specified for socket factory
+MysqlIO.76=Could not create socket factory '
+MysqlIO.77=' due to underlying exception: 
+MysqlIO.79=Unexpected end of input stream
+MysqlIO.80=Unexpected end of input stream
+MysqlIO.81=Unexpected end of input stream
+MysqlIO.82=Unexpected end of input stream
+MysqlIO.83=Packets received out of order
+MysqlIO.84=Packets received out of order
+MysqlIO.85=Unexpected end of input stream
+MysqlIO.86=Unexpected end of input stream
+MysqlIO.87=Unexpected end of input stream
+MysqlIO.88=Packets received out of order
+MysqlIO.89=Packets received out of order
+MysqlIO.91=Failed to create message digest 'SHA-1' for authentication. 
+MysqlIO.92=\ You must use a JDK that supports JCE to be able to use secure connection authentication
+MysqlIO.93=Failed to create message digest 'SHA-1' for authentication. 
+MysqlIO.94=\ You must use a JDK that supports JCE to be able to use secure connection authentication
+MysqlIO.95=Failed to create message digest 'SHA-1' for authentication. 
+MysqlIO.96=\ You must use a JDK that supports JCE to be able to use secure connection authentication
+MysqlIO.97=Unknown type '
+MysqlIO.98=\ in column 
+MysqlIO.99=\ of 
+MysqlIO.100=\ in binary-encoded result set.
+MysqlIO.102=, underlying cause: 
+NotImplemented.0=Feature not implemented
+PreparedStatement.0=SQL String can not be NULL
+PreparedStatement.1=SQL String can not be NULL
+PreparedStatement.2=Parameter index out of range (
+PreparedStatement.3=\ > 
+PreparedStatement.4=)
+PreparedStatement.16=Unknown Types value
+PreparedStatement.17=Cannot convert 
+PreparedStatement.18=\ to SQL type requested due to 
+PreparedStatement.19=\ - 
+PreparedStatement.20=Connection is read-only. 
+PreparedStatement.21=Queries leading to data modification are not allowed
+PreparedStatement.25=Connection is read-only. 
+PreparedStatement.26=Queries leading to data modification are not allowed
+PreparedStatement.32=Unsupported character encoding '
+PreparedStatement.33='
+PreparedStatement.34=Connection is read-only. 
+PreparedStatement.35=Queries leading to data modification are not allowed
+PreparedStatement.37=Can not issue executeUpdate() for SELECTs
+PreparedStatement.40=No value specified for parameter 
+PreparedStatement.43=PreparedStatement created, but used 1 or fewer times. It is more efficient to prepare statements once, and re-use them many times
+PreparedStatement.48=PreparedStatement has been closed. No further operations allowed.
+PreparedStatement.49=Parameter index out of range (
+PreparedStatement.50=\ < 1 ).
+PreparedStatement.51=Parameter index out of range (
+PreparedStatement.52=\ > number of parameters, which is 
+PreparedStatement.53=).
+PreparedStatement.54=Invalid argument value: 
+PreparedStatement.55=Error reading from InputStream 
+PreparedStatement.56=Error reading from InputStream 
+PreparedStatement.61=SQL String can not be NULL
+ServerPreparedStatement.2=Connection is read-only. 
+ServerPreparedStatement.3=Queries leading to data modification are not allowed
+ServerPreparedStatement.6=\ unable to materialize as string due to underlying SQLException: 
+ServerPreparedStatement.7=Not supported for server-side prepared statements.
+ServerPreparedStatement.8=No parameters defined during prepareCall()
+ServerPreparedStatement.9=Parameter index out of bounds. 
+ServerPreparedStatement.10=\ is not between valid values of 1 and 
+ServerPreparedStatement.11=Driver can not re-execute prepared statement when a parameter has been changed 
+ServerPreparedStatement.12=from a streaming type to an intrinsic data type without calling clearParameters() first.
+ServerPreparedStatement.13=Statement parameter 
+ServerPreparedStatement.14=\ not set.
+ServerPreparedStatement.15=Slow query (exceeded 
+ServerPreparedStatement.15a=\ ms., duration:\ 
+ServerPreparedStatement.16=\ ms): 
+ServerPreparedStatement.18=Unknown LONG DATA type '
+ServerPreparedStatement.22=Unsupported character encoding '
+ServerPreparedStatement.24=Error while reading binary stream: 
+ServerPreparedStatement.25=Error while reading binary stream: 
+ByteArrayBuffer.0=ByteArrayBuffer has no NIO buffers
+ByteArrayBuffer.1=Unsupported character encoding '
+AssertionFailedException.0=ASSERT FAILS: Exception 
+AssertionFailedException.1=\ that should not be thrown, was thrown
+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.4=select all primary keys from that table. See the JDBC 2.1 API Specification, 
+NotUpdatable.5=section 5.6 for more details.

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Messages.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Messages.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Messages.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,112 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.text.MessageFormat;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+
+/**
+ * Support for localized messages.
+ * 
+ * @author Mark Matthews
+ * @version $Id: Messages.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class Messages {
+
+	private static final String BUNDLE_NAME = "com.mysql.jdbc.LocalizedErrorMessages"; //$NON-NLS-1$
+
+	private static final ResourceBundle RESOURCE_BUNDLE;
+
+	static {
+		ResourceBundle temp = null;
+
+		//
+		// Overly-pedantic here, some appserver and JVM combos don't deal
+		// well with the no-args version, others don't deal well with
+		// the three-arg version, so we need to try both :(
+		//
+
+		try {
+			temp = ResourceBundle.getBundle(BUNDLE_NAME, Locale.getDefault(),
+					Messages.class.getClassLoader());
+		} catch (Throwable t) {
+			try {
+				temp = ResourceBundle.getBundle(BUNDLE_NAME);
+			} catch (Throwable t2) {
+				throw new RuntimeException(
+						"Can't load resource bundle due to underlying exception "
+								+ t.toString());
+			}
+		} finally {
+			RESOURCE_BUNDLE = temp;
+		}
+	}
+
+	/**
+	 * Returns the localized message for the given message key
+	 * 
+	 * @param key
+	 *            the message key
+	 * @return The localized message for the key
+	 */
+	public static String getString(String key) {
+		if (RESOURCE_BUNDLE == null) {
+			throw new RuntimeException(
+					"Localized messages from resource bundle '" + BUNDLE_NAME
+							+ "' not loaded during initialization of driver.");
+		}
+
+		try {
+			if (key == null) {
+				throw new IllegalArgumentException(
+						"Message key can not be null");
+			}
+
+			String message = RESOURCE_BUNDLE.getString(key);
+
+			if (message == null) {
+				message = "Missing error message for key '" + key + "'";
+			}
+
+			return message;
+		} catch (MissingResourceException e) {
+			return '!' + key + '!';
+		}
+	}
+
+	public static String getString(String key, Object[] args) {
+		return MessageFormat.format(getString(key), args);
+	}
+
+	/**
+	 * Dis-allow construction ...
+	 */
+	private Messages() {
+
+		// XXX Auto-generated constructor stub
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MiniAdmin.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MiniAdmin.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MiniAdmin.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,110 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+import java.util.Properties;
+
+/**
+ * Utility functions for admin functionality from Java.
+ * 
+ * @author Mark Matthews
+ */
+public class MiniAdmin {
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	private Connection conn;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Create a new MiniAdmin using the given connection
+	 * 
+	 * @param conn
+	 *            the existing connection to use.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public MiniAdmin(java.sql.Connection conn) throws SQLException {
+		if (conn == null) {
+			throw SQLError.createSQLException(
+					Messages.getString("MiniAdmin.0"), SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$
+		}
+
+		if (!(conn instanceof Connection)) {
+			throw SQLError.createSQLException(Messages.getString("MiniAdmin.1"), //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		this.conn = (Connection) conn;
+	}
+
+	/**
+	 * Create a new MiniAdmin, connecting using the given JDBC URL.
+	 * 
+	 * @param jdbcUrl
+	 *            the JDBC URL to use
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public MiniAdmin(String jdbcUrl) throws SQLException {
+		this(jdbcUrl, new Properties());
+	}
+
+	/**
+	 * Create a new MiniAdmin, connecting using the given JDBC URL and
+	 * properties
+	 * 
+	 * @param jdbcUrl
+	 *            the JDBC URL to use
+	 * @param props
+	 *            the properties to use when connecting
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public MiniAdmin(String jdbcUrl, Properties props) throws SQLException {
+		this.conn = (Connection) (new Driver().connect(jdbcUrl, props));
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Shuts down the MySQL server at the other end of the connection that this
+	 * MiniAdmin was created from/for.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public void shutdown() throws SQLException {
+		this.conn.shutdownServer();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlDataTruncation.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlDataTruncation.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlDataTruncation.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,74 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.DataTruncation;
+
+/**
+ * MySQL wrapper for DataTruncation until the server can support sending all
+ * needed information.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: MysqlDataTruncation.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class MysqlDataTruncation extends DataTruncation {
+
+	private String message;
+
+	/**
+	 * Creates a new MysqlDataTruncation exception/warning.
+	 * 
+	 * @param message
+	 *            the message from the server
+	 * @param index
+	 *            of column or parameter
+	 * @param parameter
+	 *            was a parameter?
+	 * @param read
+	 *            was truncated on read?
+	 * @param dataSize
+	 *            size requested
+	 * @param transferSize
+	 *            size actually used
+	 */
+	public MysqlDataTruncation(String message, int index, boolean parameter,
+			boolean read, int dataSize, int transferSize) {
+		super(index, parameter, read, dataSize, transferSize);
+
+		this.message = message;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.lang.Throwable#getMessage()
+	 */
+	public String getMessage() {
+		// TODO Auto-generated method stub
+		return super.getMessage() + ": " + this.message; //$NON-NLS-1$
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlDefs.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlDefs.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlDefs.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,592 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.Types;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * MysqlDefs contains many values that are needed for communication with the
+ * MySQL server.
+ * 
+ * @author Mark Matthews
+ * @version $Id: MysqlDefs.java 4724 2005-12-20 23:27:01Z mmatthews $
+ */
+final class MysqlDefs {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	static final int COM_BINLOG_DUMP = 18;
+
+	static final int COM_CHANGE_USER = 17;
+
+	static final int COM_CLOSE_STATEMENT = 25;
+
+	static final int COM_CONNECT_OUT = 20;
+
+	static final int COM_END = 29;
+
+	static final int COM_EXECUTE = 23;
+
+	static final int COM_FETCH = 28;
+
+	static final int COM_LONG_DATA = 24;
+
+	static final int COM_PREPARE = 22;
+
+	static final int COM_REGISTER_SLAVE = 21;
+
+	static final int COM_RESET_STMT = 26;
+
+	static final int COM_SET_OPTION = 27;
+
+	static final int COM_TABLE_DUMP = 19;
+
+	static final int CONNECT = 11;
+
+	static final int CREATE_DB = 5;
+
+	static final int DEBUG = 13;
+
+	static final int DELAYED_INSERT = 16;
+
+	static final int DROP_DB = 6;
+
+	static final int FIELD_LIST = 4;
+
+	static final int FIELD_TYPE_BIT = 16;
+
+	static final int FIELD_TYPE_BLOB = 252;
+
+	static final int FIELD_TYPE_DATE = 10;
+
+	static final int FIELD_TYPE_DATETIME = 12;
+
+	// Data Types
+	static final int FIELD_TYPE_DECIMAL = 0;
+
+	static final int FIELD_TYPE_DOUBLE = 5;
+
+	static final int FIELD_TYPE_ENUM = 247;
+
+	static final int FIELD_TYPE_FLOAT = 4;
+
+	static final int FIELD_TYPE_GEOMETRY = 255;
+
+	static final int FIELD_TYPE_INT24 = 9;
+
+	static final int FIELD_TYPE_LONG = 3;
+
+	static final int FIELD_TYPE_LONG_BLOB = 251;
+
+	static final int FIELD_TYPE_LONGLONG = 8;
+
+	static final int FIELD_TYPE_MEDIUM_BLOB = 250;
+
+	static final int FIELD_TYPE_NEW_DECIMAL = 246;
+
+	static final int FIELD_TYPE_NEWDATE = 14;
+
+	static final int FIELD_TYPE_NULL = 6;
+
+	static final int FIELD_TYPE_SET = 248;
+
+	static final int FIELD_TYPE_SHORT = 2;
+
+	static final int FIELD_TYPE_STRING = 254;
+
+	static final int FIELD_TYPE_TIME = 11;
+
+	static final int FIELD_TYPE_TIMESTAMP = 7;
+
+	static final int FIELD_TYPE_TINY = 1;
+
+	// Older data types
+	static final int FIELD_TYPE_TINY_BLOB = 249;
+
+	static final int FIELD_TYPE_VAR_STRING = 253;
+
+	static final int FIELD_TYPE_VARCHAR = 15;
+
+	// Newer data types
+	static final int FIELD_TYPE_YEAR = 13;
+
+	static final int INIT_DB = 2;
+
+	static final long LENGTH_BLOB = 65535;
+
+	static final long LENGTH_LONGBLOB = 4294967295L;
+
+	static final long LENGTH_MEDIUMBLOB = 16777215;
+
+	static final long LENGTH_TINYBLOB = 255;
+
+	// Limitations
+	static final int MAX_ROWS = 50000000; // From the MySQL FAQ
+
+	/**
+	 * Used to indicate that the server sent no field-level character set
+	 * information, so the driver should use the connection-level character
+	 * encoding instead.
+	 */
+	public static final int NO_CHARSET_INFO = -1;
+
+	static final byte OPEN_CURSOR_FLAG = 1;
+
+	static final int PING = 14;
+
+	static final int PROCESS_INFO = 10;
+
+	static final int PROCESS_KILL = 12;
+
+	static final int QUERY = 3;
+
+	static final int QUIT = 1;
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	static final int RELOAD = 7;
+
+	static final int SHUTDOWN = 8;
+
+	//
+	// Constants defined from mysql
+	//
+	// DB Operations
+	static final int SLEEP = 0;
+
+	static final int STATISTICS = 9;
+
+	static final int TIME = 15;
+
+	/**
+	 * Maps the given MySQL type to the correct JDBC type.
+	 */
+	static int mysqlToJavaType(int mysqlType) {
+		int jdbcType;
+
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
+		case MysqlDefs.FIELD_TYPE_DECIMAL:
+			jdbcType = Types.DECIMAL;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_TINY:
+			jdbcType = Types.TINYINT;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_SHORT:
+			jdbcType = Types.SMALLINT;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_LONG:
+			jdbcType = Types.INTEGER;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			jdbcType = Types.REAL;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			jdbcType = Types.DOUBLE;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_NULL:
+			jdbcType = Types.NULL;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+			jdbcType = Types.TIMESTAMP;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			jdbcType = Types.BIGINT;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_INT24:
+			jdbcType = Types.INTEGER;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_DATE:
+			jdbcType = Types.DATE;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_TIME:
+			jdbcType = Types.TIME;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_DATETIME:
+			jdbcType = Types.TIMESTAMP;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			jdbcType = Types.DATE;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_NEWDATE:
+			jdbcType = Types.DATE;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_ENUM:
+			jdbcType = Types.CHAR;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_SET:
+			jdbcType = Types.CHAR;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
+			jdbcType = Types.VARBINARY;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
+			jdbcType = Types.LONGVARBINARY;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
+			jdbcType = Types.LONGVARBINARY;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_BLOB:
+			jdbcType = Types.LONGVARBINARY;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_VAR_STRING:
+		case MysqlDefs.FIELD_TYPE_VARCHAR:
+			jdbcType = Types.VARCHAR;
+
+			break;
+
+		case MysqlDefs.FIELD_TYPE_STRING:
+			jdbcType = Types.CHAR;
+
+			break;
+		case MysqlDefs.FIELD_TYPE_GEOMETRY:
+			jdbcType = Types.BINARY;
+
+			break;
+		case MysqlDefs.FIELD_TYPE_BIT:
+			jdbcType = Types.BIT;
+
+			break;
+		default:
+			jdbcType = Types.VARCHAR;
+		}
+
+		return jdbcType;
+	}
+
+	/**
+	 * Maps the given MySQL type to the correct JDBC type.
+	 */
+	static int mysqlToJavaType(String mysqlType) {
+		if (mysqlType.equalsIgnoreCase("BIT")) {
+			return mysqlToJavaType(FIELD_TYPE_BIT);
+		} else if (mysqlType.equalsIgnoreCase("TINYINT")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_TINY);
+		} else if (mysqlType.equalsIgnoreCase("SMALLINT")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_SHORT);
+		} else if (mysqlType.equalsIgnoreCase("MEDIUMINT")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_INT24);
+		} else if (mysqlType.equalsIgnoreCase("INT") || mysqlType.equalsIgnoreCase("INTEGER")) { //$NON-NLS-1$ //$NON-NLS-2$
+			return mysqlToJavaType(FIELD_TYPE_LONG);
+		} else if (mysqlType.equalsIgnoreCase("BIGINT")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_LONGLONG);
+		} else if (mysqlType.equalsIgnoreCase("INT24")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_INT24);
+		} else if (mysqlType.equalsIgnoreCase("REAL")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_DOUBLE);
+		} else if (mysqlType.equalsIgnoreCase("FLOAT")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_FLOAT);
+		} else if (mysqlType.equalsIgnoreCase("DECIMAL")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_DECIMAL);
+		} else if (mysqlType.equalsIgnoreCase("NUMERIC")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_DECIMAL);
+		} else if (mysqlType.equalsIgnoreCase("DOUBLE")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_DOUBLE);
+		} else if (mysqlType.equalsIgnoreCase("CHAR")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_STRING);
+		} else if (mysqlType.equalsIgnoreCase("VARCHAR")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_VAR_STRING);
+		} else if (mysqlType.equalsIgnoreCase("DATE")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_DATE);
+		} else if (mysqlType.equalsIgnoreCase("TIME")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_TIME);
+		} else if (mysqlType.equalsIgnoreCase("YEAR")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_YEAR);
+		} else if (mysqlType.equalsIgnoreCase("TIMESTAMP")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_TIMESTAMP);
+		} else if (mysqlType.equalsIgnoreCase("DATETIME")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_DATETIME);
+		} else if (mysqlType.equalsIgnoreCase("TINYBLOB")) { //$NON-NLS-1$
+			return java.sql.Types.BINARY;
+		} else if (mysqlType.equalsIgnoreCase("BLOB")) { //$NON-NLS-1$
+			return java.sql.Types.LONGVARBINARY;
+		} else if (mysqlType.equalsIgnoreCase("MEDIUMBLOB")) { //$NON-NLS-1$
+			return java.sql.Types.LONGVARBINARY;
+		} else if (mysqlType.equalsIgnoreCase("LONGBLOB")) { //$NON-NLS-1$
+			return java.sql.Types.LONGVARBINARY;
+		} else if (mysqlType.equalsIgnoreCase("TINYTEXT")) { //$NON-NLS-1$
+			return java.sql.Types.VARCHAR;
+		} else if (mysqlType.equalsIgnoreCase("TEXT")) { //$NON-NLS-1$
+			return java.sql.Types.LONGVARCHAR;
+		} else if (mysqlType.equalsIgnoreCase("MEDIUMTEXT")) { //$NON-NLS-1$
+			return java.sql.Types.LONGVARCHAR;
+		} else if (mysqlType.equalsIgnoreCase("LONGTEXT")) { //$NON-NLS-1$
+			return java.sql.Types.LONGVARCHAR;
+		} else if (mysqlType.equalsIgnoreCase("ENUM")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_ENUM);
+		} else if (mysqlType.equalsIgnoreCase("SET")) { //$NON-NLS-1$
+			return mysqlToJavaType(FIELD_TYPE_SET);
+		} else if (mysqlType.equalsIgnoreCase("GEOMETRY")) {
+			return mysqlToJavaType(FIELD_TYPE_GEOMETRY);
+		} else if (mysqlType.equalsIgnoreCase("BINARY")) {
+			return Types.BINARY; // no concrete type on the wire
+		} else if (mysqlType.equalsIgnoreCase("VARBINARY")) {
+			return Types.VARBINARY; // no concrete type on the wire
+		} else if (mysqlType.equalsIgnoreCase("BIT")) {
+			return mysqlToJavaType(FIELD_TYPE_BIT);
+		}
+
+		// Punt
+		return java.sql.Types.OTHER;
+	}
+
+	/**
+	 * @param mysqlType
+	 * @return
+	 */
+	public static String typeToName(int mysqlType) {
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_DECIMAL:
+			return "FIELD_TYPE_DECIMAL";
+
+		case MysqlDefs.FIELD_TYPE_TINY:
+			return "FIELD_TYPE_TINY";
+
+		case MysqlDefs.FIELD_TYPE_SHORT:
+			return "FIELD_TYPE_SHORT";
+
+		case MysqlDefs.FIELD_TYPE_LONG:
+			return "FIELD_TYPE_LONG";
+
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			return "FIELD_TYPE_FLOAT";
+
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			return "FIELD_TYPE_DOUBLE";
+
+		case MysqlDefs.FIELD_TYPE_NULL:
+			return "FIELD_TYPE_NULL";
+
+		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+			return "FIELD_TYPE_TIMESTAMP";
+
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			return "FIELD_TYPE_LONGLONG";
+
+		case MysqlDefs.FIELD_TYPE_INT24:
+			return "FIELD_TYPE_INT24";
+
+		case MysqlDefs.FIELD_TYPE_DATE:
+			return "FIELD_TYPE_DATE";
+
+		case MysqlDefs.FIELD_TYPE_TIME:
+			return "FIELD_TYPE_TIME";
+
+		case MysqlDefs.FIELD_TYPE_DATETIME:
+			return "FIELD_TYPE_DATETIME";
+
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			return "FIELD_TYPE_YEAR";
+
+		case MysqlDefs.FIELD_TYPE_NEWDATE:
+			return "FIELD_TYPE_NEWDATE";
+
+		case MysqlDefs.FIELD_TYPE_ENUM:
+			return "FIELD_TYPE_ENUM";
+
+		case MysqlDefs.FIELD_TYPE_SET:
+			return "FIELD_TYPE_SET";
+
+		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
+			return "FIELD_TYPE_TINY_BLOB";
+
+		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
+			return "FIELD_TYPE_MEDIUM_BLOB";
+
+		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
+			return "FIELD_TYPE_LONG_BLOB";
+
+		case MysqlDefs.FIELD_TYPE_BLOB:
+			return "FIELD_TYPE_BLOB";
+
+		case MysqlDefs.FIELD_TYPE_VAR_STRING:
+			return "FIELD_TYPE_VAR_STRING";
+
+		case MysqlDefs.FIELD_TYPE_STRING:
+			return "FIELD_TYPE_STRING";
+
+		case MysqlDefs.FIELD_TYPE_VARCHAR:
+			return "FIELD_TYPE_VARCHAR";
+
+		case MysqlDefs.FIELD_TYPE_GEOMETRY:
+			return "FIELD_TYPE_GEOMETRY";
+
+		default:
+			return " Unknown MySQL Type # " + mysqlType;
+		}
+	}
+
+	private static Map mysqlToJdbcTypesMap = new HashMap();
+
+	static {
+		mysqlToJdbcTypesMap.put("BIT", new Integer(
+				mysqlToJavaType(FIELD_TYPE_BIT)));
+
+		mysqlToJdbcTypesMap.put("TINYINT", new Integer(
+				mysqlToJavaType(FIELD_TYPE_TINY)));
+		mysqlToJdbcTypesMap.put("SMALLINT", new Integer(
+				mysqlToJavaType(FIELD_TYPE_SHORT)));
+		mysqlToJdbcTypesMap.put("MEDIUMINT", new Integer(
+				mysqlToJavaType(FIELD_TYPE_INT24)));
+		mysqlToJdbcTypesMap.put("INT", new Integer(
+				mysqlToJavaType(FIELD_TYPE_LONG)));
+		mysqlToJdbcTypesMap.put("INTEGER", new Integer(
+				mysqlToJavaType(FIELD_TYPE_LONG)));
+		mysqlToJdbcTypesMap.put("BIGINT", new Integer(
+				mysqlToJavaType(FIELD_TYPE_LONGLONG)));
+		mysqlToJdbcTypesMap.put("INT24", new Integer(
+				mysqlToJavaType(FIELD_TYPE_INT24)));
+		mysqlToJdbcTypesMap.put("REAL", new Integer(
+				mysqlToJavaType(FIELD_TYPE_DOUBLE)));
+		mysqlToJdbcTypesMap.put("FLOAT", new Integer(
+				mysqlToJavaType(FIELD_TYPE_FLOAT)));
+		mysqlToJdbcTypesMap.put("DECIMAL", new Integer(
+				mysqlToJavaType(FIELD_TYPE_DECIMAL)));
+		mysqlToJdbcTypesMap.put("NUMERIC", new Integer(
+				mysqlToJavaType(FIELD_TYPE_DECIMAL)));
+		mysqlToJdbcTypesMap.put("DOUBLE", new Integer(
+				mysqlToJavaType(FIELD_TYPE_DOUBLE)));
+		mysqlToJdbcTypesMap.put("CHAR", new Integer(
+				mysqlToJavaType(FIELD_TYPE_STRING)));
+		mysqlToJdbcTypesMap.put("VARCHAR", new Integer(
+				mysqlToJavaType(FIELD_TYPE_VAR_STRING)));
+		mysqlToJdbcTypesMap.put("DATE", new Integer(
+				mysqlToJavaType(FIELD_TYPE_DATE)));
+		mysqlToJdbcTypesMap.put("TIME", new Integer(
+				mysqlToJavaType(FIELD_TYPE_TIME)));
+		mysqlToJdbcTypesMap.put("YEAR", new Integer(
+				mysqlToJavaType(FIELD_TYPE_YEAR)));
+		mysqlToJdbcTypesMap.put("TIMESTAMP", new Integer(
+				mysqlToJavaType(FIELD_TYPE_TIMESTAMP)));
+		mysqlToJdbcTypesMap.put("DATETIME", new Integer(
+				mysqlToJavaType(FIELD_TYPE_DATETIME)));
+		mysqlToJdbcTypesMap.put("TINYBLOB", new Integer(java.sql.Types.BINARY));
+		mysqlToJdbcTypesMap.put("BLOB", new Integer(
+				java.sql.Types.LONGVARBINARY));
+		mysqlToJdbcTypesMap.put("MEDIUMBLOB", new Integer(
+				java.sql.Types.LONGVARBINARY));
+		mysqlToJdbcTypesMap.put("LONGBLOB", new Integer(
+				java.sql.Types.LONGVARBINARY));
+		mysqlToJdbcTypesMap
+				.put("TINYTEXT", new Integer(java.sql.Types.VARCHAR));
+		mysqlToJdbcTypesMap
+				.put("TEXT", new Integer(java.sql.Types.LONGVARCHAR));
+		mysqlToJdbcTypesMap.put("MEDIUMTEXT", new Integer(
+				java.sql.Types.LONGVARCHAR));
+		mysqlToJdbcTypesMap.put("LONGTEXT", new Integer(
+				java.sql.Types.LONGVARCHAR));
+		mysqlToJdbcTypesMap.put("ENUM", new Integer(
+				mysqlToJavaType(FIELD_TYPE_ENUM)));
+		mysqlToJdbcTypesMap.put("SET", new Integer(
+				mysqlToJavaType(FIELD_TYPE_SET)));
+		mysqlToJdbcTypesMap.put("GEOMETRY", new Integer(
+				mysqlToJavaType(FIELD_TYPE_GEOMETRY)));
+	}
+
+	static final void appendJdbcTypeMappingQuery(StringBuffer buf, String mysqlTypeColumnName) {
+
+		buf.append("CASE ");
+		Map typesMap = new HashMap();
+		typesMap.putAll(mysqlToJdbcTypesMap);
+		typesMap.put("BINARY", new Integer(Types.BINARY));
+		typesMap.put("VARBINARY", new Integer(Types.VARBINARY));
+		
+		Iterator mysqlTypes = typesMap.keySet().iterator();
+		
+		while (mysqlTypes.hasNext()) {
+			String mysqlTypeName = (String)mysqlTypes.next();
+			buf.append(" WHEN ");
+			buf.append(mysqlTypeColumnName);
+			buf.append("='");
+			buf.append(mysqlTypeName);
+			buf.append("' THEN ");
+			buf.append(typesMap.get(mysqlTypeName));
+			
+			if (mysqlTypeName.equalsIgnoreCase("DOUBLE") ||
+					mysqlTypeName.equalsIgnoreCase("FLOAT") ||
+					mysqlTypeName.equalsIgnoreCase("DECIMAL") ||
+					mysqlTypeName.equalsIgnoreCase("NUMERIC")) {
+				buf.append(" WHEN ");
+				buf.append(mysqlTypeColumnName);
+				buf.append("='");
+				buf.append(mysqlTypeName);
+				buf.append(" unsigned' THEN ");
+				buf.append(typesMap.get(mysqlTypeName));	
+			}	
+		}
+		
+		buf.append(" ELSE ");
+		buf.append(Types.OTHER);
+		buf.append(" END ");
+		
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlErrorNumbers.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlErrorNumbers.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlErrorNumbers.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,641 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+ 
+ */
+package com.mysql.jdbc;
+
+/**
+ * Constants representing MySQL error numbers returned by the server in error
+ * messages.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: MysqlErrorNumbers.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
+ *          Exp $
+ */
+public final class MysqlErrorNumbers {
+
+	public final static int ER_ABORTING_CONNECTION = 1152;
+
+	public final static int ER_ACCESS_DENIED_ERROR = 1045;
+
+	public final static int ER_ALTER_INFO = 1088;
+
+	public final static int ER_AUTO_CONVERT = 1246;
+
+	public final static int ER_BAD_DB_ERROR = 1049;
+
+	public final static int ER_BAD_FIELD_ERROR = 1054;
+
+	public final static int ER_BAD_FT_COLUMN = 1283;
+
+	public final static int ER_BAD_HOST_ERROR = 1042;
+
+	public final static int ER_BAD_NULL_ERROR = 1048;
+
+	public final static int ER_BAD_SLAVE = 1200;
+
+	public final static int ER_BAD_SLAVE_UNTIL_COND = 1277;
+
+	public final static int ER_BAD_TABLE_ERROR = 1051;
+
+	public final static int ER_BLOB_CANT_HAVE_DEFAULT = 1101;
+
+	public final static int ER_BLOB_KEY_WITHOUT_LENGTH = 1170;
+
+	public final static int ER_BLOB_USED_AS_KEY = 1073;
+
+	public final static int ER_BLOBS_AND_NO_TERMINATED = 1084;
+
+	public final static int ER_CANNOT_ADD_FOREIGN = 1215;
+
+	public final static int ER_CANT_AGGREGATE_2COLLATIONS = 1267;
+
+	public final static int ER_CANT_AGGREGATE_3COLLATIONS = 1270;
+
+	public final static int ER_CANT_AGGREGATE_NCOLLATIONS = 1271;
+
+	public final static int ER_CANT_CREATE_DB = 1006;
+
+	public final static int ER_CANT_CREATE_FILE = 1004;
+
+	public final static int ER_CANT_CREATE_TABLE = 1005;
+
+	public final static int ER_CANT_CREATE_THREAD = 1135;
+
+	public final static int ER_CANT_DELETE_FILE = 1011;
+
+	public final static int ER_CANT_DO_THIS_DURING_AN_TRANSACTION = 1179;
+
+	public final static int ER_CANT_DROP_FIELD_OR_KEY = 1091;
+
+	public final static int ER_CANT_FIND_DL_ENTRY = 1127;
+
+	public final static int ER_CANT_FIND_SYSTEM_REC = 1012;
+
+	public final static int ER_CANT_FIND_UDF = 1122;
+
+	public final static int ER_CANT_GET_STAT = 1013;
+
+	public final static int ER_CANT_GET_WD = 1014;
+
+	public final static int ER_CANT_INITIALIZE_UDF = 1123;
+
+	public final static int ER_CANT_LOCK = 1015;
+
+	public final static int ER_CANT_OPEN_FILE = 1016;
+
+	public final static int ER_CANT_OPEN_LIBRARY = 1126;
+
+	public final static int ER_CANT_READ_DIR = 1018;
+
+	public final static int ER_CANT_REMOVE_ALL_FIELDS = 1090;
+
+	public final static int ER_CANT_REOPEN_TABLE = 1137;
+
+	public final static int ER_CANT_SET_WD = 1019;
+
+	public final static int ER_CANT_UPDATE_WITH_READLOCK = 1223;
+
+	public final static int ER_CANT_USE_OPTION_HERE = 1234;
+
+	public final static int ER_CHECK_NO_SUCH_TABLE = 1177;
+
+	public final static int ER_CHECK_NOT_IMPLEMENTED = 1178;
+
+	public final static int ER_CHECKREAD = 1020;
+
+	public final static int ER_COLLATION_CHARSET_MISMATCH = 1253;
+
+	public final static int ER_COLUMNACCESS_DENIED_ERROR = 1143;
+
+	public final static int ER_CON_COUNT_ERROR = 1040;
+
+	public final static int ER_CONNECT_TO_MASTER = 1218;
+
+	public final static int ER_CORRUPT_HELP_DB = 1244;
+
+	public final static int ER_CRASHED_ON_REPAIR = 1195;
+
+	public final static int ER_CRASHED_ON_USAGE = 1194;
+
+	public final static int ER_CREATE_DB_WITH_READ_LOCK = 1209;
+
+	public final static int ER_CUT_VALUE_GROUP_CONCAT = 1260;
+
+	public final static int ER_CYCLIC_REFERENCE = 1245;
+
+	public final static int ER_DB_CREATE_EXISTS = 1007;
+
+	public final static int ER_DB_DROP_DELETE = 1009;
+
+	public final static int ER_DB_DROP_EXISTS = 1008;
+
+	public final static int ER_DB_DROP_RMDIR = 1010;
+
+	public final static int ER_DBACCESS_DENIED_ERROR = 1044;
+
+	public final static int ER_DELAYED_CANT_CHANGE_LOCK = 1150;
+
+	public final static int ER_DELAYED_INSERT_TABLE_LOCKED = 1165;
+
+	public final static int ER_DERIVED_MUST_HAVE_ALIAS = 1248;
+
+	public final static int ER_DISK_FULL = 1021;
+
+	public final static int ER_DROP_DB_WITH_READ_LOCK = 1208;
+
+	public final static int ER_DROP_USER = 1268;
+
+	public final static int ER_DUMP_NOT_IMPLEMENTED = 1185;
+
+	public final static int ER_DUP_ARGUMENT = 1225;
+
+	public final static int ER_DUP_ENTRY = 1062;
+
+	public final static int ER_DUP_FIELDNAME = 1060;
+
+	public final static int ER_DUP_KEY = 1022;
+
+	public final static int ER_DUP_KEYNAME = 1061;
+
+	public final static int ER_DUP_UNIQUE = 1169;
+
+	public final static int ER_DUPLICATED_VALUE_IN_TYPE = 1291;
+
+	public final static int ER_EMPTY_QUERY = 1065;
+
+	public final static int ER_ERROR_DURING_CHECKPOINT = 1183;
+
+	public final static int ER_ERROR_DURING_COMMIT = 1180;
+
+	public final static int ER_ERROR_DURING_FLUSH_LOGS = 1182;
+
+	public final static int ER_ERROR_DURING_ROLLBACK = 1181;
+
+	public final static int ER_ERROR_MESSAGES = 298;
+
+	public final static int ER_ERROR_ON_CLOSE = 1023;
+
+	public final static int ER_ERROR_ON_READ = 1024;
+
+	public final static int ER_ERROR_ON_RENAME = 1025;
+
+	public final static int ER_ERROR_ON_WRITE = 1026;
+
+	public final static int ER_ERROR_WHEN_EXECUTING_COMMAND = 1220;
+
+	public final static int ER_FEATURE_DISABLED = 1289;
+
+	public final static int ER_FIELD_SPECIFIED_TWICE = 1110;
+
+	public final static int ER_FILE_EXISTS_ERROR = 1086;
+
+	public final static int ER_FILE_NOT_FOUND = 1017;
+
+	public final static int ER_FILE_USED = 1027;
+
+	public final static int ER_FILSORT_ABORT = 1028;
+
+	public final static int ER_FLUSH_MASTER_BINLOG_CLOSED = 1186;
+
+	public final static int ER_FORCING_CLOSE = 1080;
+
+	public final static int ER_FORM_NOT_FOUND = 1029;
+
+	public final static int ER_FT_MATCHING_KEY_NOT_FOUND = 1191;
+
+	public final static int ER_FUNCTION_NOT_DEFINED = 1128;
+
+	public final static int ER_GET_ERRMSG = 1296;
+
+	public final static int ER_GET_ERRNO = 1030;
+
+	public final static int ER_GET_TEMPORARY_ERRMSG = 1297;
+
+	public final static int ER_GLOBAL_VARIABLE = 1229;
+
+	public final static int ER_GOT_SIGNAL = 1078;
+
+	public final static int ER_GRANT_WRONG_HOST_OR_USER = 1145;
+
+	public final static int ER_HANDSHAKE_ERROR = 1043;
+
+	public final static int ER_HASHCHK = 1000;
+
+	public final static int ER_HOST_IS_BLOCKED = 1129;
+
+	public final static int ER_HOST_NOT_PRIVILEGED = 1130;
+
+	public final static int ER_ILLEGAL_GRANT_FOR_TABLE = 1144;
+
+	public final static int ER_ILLEGAL_HA = 1031;
+
+	public final static int ER_ILLEGAL_REFERENCE = 1247;
+
+	public final static int ER_INCORRECT_GLOBAL_LOCAL_VAR = 1238;
+
+	public final static int ER_INDEX_REBUILD = 1187;
+
+	public final static int ER_INSERT_INFO = 1092;
+
+	public final static int ER_INVALID_DEFAULT = 1067;
+
+	public final static int ER_INVALID_GROUP_FUNC_USE = 1111;
+
+	public final static int ER_INVALID_ON_UPDATE = 1294;
+
+	public final static int ER_INVALID_USE_OF_NULL = 1138;
+
+	public final static int ER_IPSOCK_ERROR = 1081;
+
+	public final static int ER_KEY_COLUMN_DOES_NOT_EXITS = 1072;
+
+	public final static int ER_KEY_DOES_NOT_EXITS = 1176;
+
+	public final static int ER_KEY_NOT_FOUND = 1032;
+
+	public final static int ER_KEY_REF_DO_NOT_MATCH_TABLE_REF = 1240;
+
+	public final static int ER_KILL_DENIED_ERROR = 1095;
+
+	public final static int ER_LOAD_INFO = 1087;
+
+	public final static int ER_LOCAL_VARIABLE = 1228;
+
+	public final static int ER_LOCK_DEADLOCK = 1213;
+
+	public final static int ER_LOCK_OR_ACTIVE_TRANSACTION = 1192;
+
+	public final static int ER_LOCK_TABLE_FULL = 1206;
+
+	public final static int ER_LOCK_WAIT_TIMEOUT = 1205;
+
+	public final static int ER_MASTER = 1188;
+
+	public final static int ER_MASTER_FATAL_ERROR_READING_BINLOG = 1236;
+
+	public final static int ER_MASTER_INFO = 1201;
+
+	public final static int ER_MASTER_NET_READ = 1189;
+
+	public final static int ER_MASTER_NET_WRITE = 1190;
+
+	public final static int ER_MISSING_SKIP_SLAVE = 1278;
+
+	public final static int ER_MIX_OF_GROUP_FUNC_AND_FIELDS = 1140;
+
+	public final static int ER_MIXING_NOT_ALLOWED = 1224;
+
+	public final static int ER_MULTIPLE_PRI_KEY = 1068;
+
+	public final static int ER_NET_ERROR_ON_WRITE = 1160;
+
+	public final static int ER_NET_FCNTL_ERROR = 1155;
+
+	public final static int ER_NET_PACKET_TOO_LARGE = 1153;
+
+	public final static int ER_NET_PACKETS_OUT_OF_ORDER = 1156;
+
+	public final static int ER_NET_READ_ERROR = 1158;
+
+	public final static int ER_NET_READ_ERROR_FROM_PIPE = 1154;
+
+	public final static int ER_NET_READ_INTERRUPTED = 1159;
+
+	public final static int ER_NET_UNCOMPRESS_ERROR = 1157;
+
+	public final static int ER_NET_WRITE_INTERRUPTED = 1161;
+
+	public final static int ER_NEW_ABORTING_CONNECTION = 1184;
+
+	public final static int ER_NISAMCHK = 1001;
+
+	public final static int ER_NO = 1002;
+
+	public final static int ER_NO_DB_ERROR = 1046;
+
+	public final static int ER_NO_DEFAULT = 1230;
+
+	public final static int ER_NO_PERMISSION_TO_CREATE_USER = 1211;
+
+	public final static int ER_NO_RAID_COMPILED = 1174;
+
+	public final static int ER_NO_REFERENCED_ROW = 1216;
+
+	public final static int ER_NO_SUCH_INDEX = 1082;
+
+	public final static int ER_NO_SUCH_TABLE = 1146;
+
+	public final static int ER_NO_SUCH_THREAD = 1094;
+
+	public final static int ER_NO_TABLES_USED = 1096;
+
+	public final static int ER_NO_UNIQUE_LOGFILE = 1098;
+
+	public final static int ER_NON_UNIQ_ERROR = 1052;
+
+	public final static int ER_NON_UPDATABLE_TABLE = 1288;
+
+	public final static int ER_NONEXISTING_GRANT = 1141;
+
+	public final static int ER_NONEXISTING_TABLE_GRANT = 1147;
+
+	public final static int ER_NONUNIQ_TABLE = 1066;
+
+	public final static int ER_NORMAL_SHUTDOWN = 1077;
+
+	public final static int ER_NOT_ALLOWED_COMMAND = 1148;
+
+	public final static int ER_NOT_FORM_FILE = 1033;
+
+	public final static int ER_NOT_KEYFILE = 1034;
+
+	public final static int ER_NOT_SUPPORTED_AUTH_MODE = 1251;
+
+	public final static int ER_NOT_SUPPORTED_YET = 1235;
+
+	public final static int ER_NULL_COLUMN_IN_INDEX = 1121;
+
+	public final static int ER_OLD_KEYFILE = 1035;
+
+	public final static int ER_OPEN_AS_READONLY = 1036;
+
+	public final static int ER_OPERAND_COLUMNS = 1241;
+
+	public final static int ER_OPTION_PREVENTS_STATEMENT = 1290;
+
+	public final static int ER_OUT_OF_RESOURCES = 1041;
+
+	public final static int ER_OUT_OF_SORTMEMORY = 1038;
+
+	public final static int ER_OUTOFMEMORY = 1037;
+
+	public final static int ER_PARSE_ERROR = 1064;
+
+	public final static int ER_PASSWORD_ANONYMOUS_USER = 1131;
+
+	public final static int ER_PASSWORD_NO_MATCH = 1133;
+
+	public final static int ER_PASSWORD_NOT_ALLOWED = 1132;
+
+	public final static int ER_PRIMARY_CANT_HAVE_NULL = 1171;
+
+	public final static int ER_QUERY_ON_MASTER = 1219;
+
+	public final static int ER_READ_ONLY_TRANSACTION = 1207;
+
+	public final static int ER_READY = 1076;
+
+	public final static int ER_RECORD_FILE_FULL = 1114;
+
+	public final static int ER_REGEXP_ERROR = 1139;
+
+	public final static int ER_REQUIRES_PRIMARY_KEY = 1173;
+
+	public final static int ER_REVOKE_GRANTS = 1269;
+
+	public final static int ER_ROW_IS_REFERENCED = 1217;
+
+	public final static int ER_SELECT_REDUCED = 1249;
+
+	public final static int ER_SERVER_IS_IN_SECURE_AUTH_MODE = 1275;
+
+	public final static int ER_SERVER_SHUTDOWN = 1053;
+
+	public final static int ER_SET_CONSTANTS_ONLY = 1204;
+
+	public final static int ER_SHUTDOWN_COMPLETE = 1079;
+
+	public final static int ER_SLAVE_IGNORED_SSL_PARAMS = 1274;
+
+	public final static int ER_SLAVE_IGNORED_TABLE = 1237;
+
+	public final static int ER_SLAVE_MUST_STOP = 1198;
+
+	public final static int ER_SLAVE_NOT_RUNNING = 1199;
+
+	public final static int ER_SLAVE_THREAD = 1202;
+
+	public final static int ER_SLAVE_WAS_NOT_RUNNING = 1255;
+
+	public final static int ER_SLAVE_WAS_RUNNING = 1254;
+
+	public final static int ER_SPATIAL_CANT_HAVE_NULL = 1252;
+
+	public final static int ER_SPECIFIC_ACCESS_DENIED_ERROR = 1227;
+
+	public final static int ER_STACK_OVERRUN = 1119;
+
+	public final static int ER_SUBQUERY_NO_1_ROW = 1242;
+
+	public final static int ER_SYNTAX_ERROR = 1149;
+
+	public final static int ER_TABLE_CANT_HANDLE_AUTO_INCREMENT = 1164;
+
+	public final static int ER_TABLE_CANT_HANDLE_BLOB = 1163;
+
+	public final static int ER_TABLE_CANT_HANDLE_FT = 1214;
+
+	public final static int ER_TABLE_EXISTS_ERROR = 1050;
+
+	public final static int ER_TABLE_MUST_HAVE_COLUMNS = 1113;
+
+	public final static int ER_TABLE_NOT_LOCKED = 1100;
+
+	public final static int ER_TABLE_NOT_LOCKED_FOR_WRITE = 1099;
+
+	public final static int ER_TABLEACCESS_DENIED_ERROR = 1142;
+
+	public final static int ER_TABLENAME_NOT_ALLOWED_HERE = 1250;
+
+	public final static int ER_TEXTFILE_NOT_READABLE = 1085;
+
+	public final static int ER_TOO_BIG_FIELDLENGTH = 1074;
+
+	public final static int ER_TOO_BIG_FOR_UNCOMPRESS = 1256;
+
+	public final static int ER_TOO_BIG_ROWSIZE = 1118;
+
+	public final static int ER_TOO_BIG_SELECT = 1104;
+
+	public final static int ER_TOO_BIG_SET = 1097;
+
+	public final static int ER_TOO_LONG_IDENT = 1059;
+
+	public final static int ER_TOO_LONG_KEY = 1071;
+
+	public final static int ER_TOO_LONG_STRING = 1162;
+
+	public final static int ER_TOO_MANY_DELAYED_THREADS = 1151;
+
+	public final static int ER_TOO_MANY_FIELDS = 1117;
+
+	public final static int ER_TOO_MANY_KEY_PARTS = 1070;
+
+	public final static int ER_TOO_MANY_KEYS = 1069;
+
+	public final static int ER_TOO_MANY_ROWS = 1172;
+
+	public final static int ER_TOO_MANY_TABLES = 1116;
+
+	public final static int ER_TOO_MANY_USER_CONNECTIONS = 1203;
+
+	public final static int ER_TOO_MUCH_AUTO_TIMESTAMP_COLS = 1293;
+
+	public final static int ER_TRANS_CACHE_FULL = 1197;
+
+	public final static int ER_TRUNCATED_WRONG_VALUE = 1292;
+
+	public final static int ER_UDF_EXISTS = 1125;
+
+	public final static int ER_UDF_NO_PATHS = 1124;
+
+	public final static int ER_UNEXPECTED_EOF = 1039;
+
+	public final static int ER_UNION_TABLES_IN_DIFFERENT_DIR = 1212;
+
+	public final static int ER_UNKNOWN_CHARACTER_SET = 1115;
+
+	public final static int ER_UNKNOWN_COLLATION = 1273;
+
+	public final static int ER_UNKNOWN_COM_ERROR = 1047;
+
+	public final static int ER_UNKNOWN_ERROR = 1105;
+
+	public final static int ER_UNKNOWN_KEY_CACHE = 1284;
+
+	public final static int ER_UNKNOWN_PROCEDURE = 1106;
+
+	public final static int ER_UNKNOWN_STMT_HANDLER = 1243;
+
+	public final static int ER_UNKNOWN_STORAGE_ENGINE = 1286;
+
+	public final static int ER_UNKNOWN_SYSTEM_VARIABLE = 1193;
+
+	public final static int ER_UNKNOWN_TABLE = 1109;
+
+	public final static int ER_UNSUPPORTED_EXTENSION = 1112;
+
+	public final static int ER_UNSUPPORTED_PS = 1295;
+
+	public final static int ER_UNTIL_COND_IGNORED = 1279;
+
+	public final static int ER_UPDATE_INFO = 1134;
+
+	public final static int ER_UPDATE_TABLE_USED = 1093;
+
+	public final static int ER_UPDATE_WITHOUT_KEY_IN_SAFE_MODE = 1175;
+
+	public final static int ER_USER_LIMIT_REACHED = 1226;
+
+	public final static int ER_VAR_CANT_BE_READ = 1233;
+
+	public final static int ER_VARIABLE_IS_NOT_STRUCT = 1272;
+
+	public final static int ER_WARN_DATA_OUT_OF_RANGE = 1264;
+
+	public final static int ER_WARN_DATA_TRUNCATED = 1265;
+
+	public final static int ER_WARN_DEPRECATED_SYNTAX = 1287;
+
+	public final static int ER_WARN_FIELD_RESOLVED = 1276;
+
+	public final static int ER_WARN_HOSTNAME_WONT_WORK = 1285;
+
+	public final static int ER_WARN_NULL_TO_NOTNULL = 1263;
+
+	public final static int ER_WARN_QC_RESIZE = 1282;
+
+	public final static int ER_WARN_TOO_FEW_RECORDS = 1261;
+
+	public final static int ER_WARN_TOO_MANY_RECORDS = 1262;
+
+	public final static int ER_WARN_USING_OTHER_HANDLER = 1266;
+
+	public final static int ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196;
+
+	public final static int ER_WRONG_ARGUMENTS = 1210;
+
+	public final static int ER_WRONG_AUTO_KEY = 1075;
+
+	public final static int ER_WRONG_COLUMN_NAME = 1166;
+
+	public final static int ER_WRONG_DB_NAME = 1102;
+
+	public final static int ER_WRONG_FIELD_SPEC = 1063;
+
+	public final static int ER_WRONG_FIELD_TERMINATORS = 1083;
+
+	public final static int ER_WRONG_FIELD_WITH_GROUP = 1055;
+
+	public final static int ER_WRONG_FK_DEF = 1239;
+
+	public final static int ER_WRONG_GROUP_FIELD = 1056;
+
+	public final static int ER_WRONG_KEY_COLUMN = 1167;
+
+	public final static int ER_WRONG_MRG_TABLE = 1168;
+
+	public final static int ER_WRONG_NAME_FOR_CATALOG = 1281;
+
+	public final static int ER_WRONG_NAME_FOR_INDEX = 1280;
+
+	public final static int ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT = 1222;
+
+	public final static int ER_WRONG_OUTER_JOIN = 1120;
+
+	public final static int ER_WRONG_PARAMCOUNT_TO_PROCEDURE = 1107;
+
+	public final static int ER_WRONG_PARAMETERS_TO_PROCEDURE = 1108;
+
+	public final static int ER_WRONG_SUB_KEY = 1089;
+
+	public final static int ER_WRONG_SUM_SELECT = 1057;
+
+	public final static int ER_WRONG_TABLE_NAME = 1103;
+
+	public final static int ER_WRONG_TYPE_FOR_VAR = 1232;
+
+	public final static int ER_WRONG_USAGE = 1221;
+
+	public final static int ER_WRONG_VALUE_COUNT = 1058;
+
+	public final static int ER_WRONG_VALUE_COUNT_ON_ROW = 1136;
+
+	public final static int ER_WRONG_VALUE_FOR_VAR = 1231;
+	
+	public final static int ER_XA_RMERR = 1401;
+
+	public final static int ER_YES = 1003;
+
+	public final static int ER_ZLIB_Z_BUF_ERROR = 1258;
+
+	public final static int ER_ZLIB_Z_DATA_ERROR = 1259;
+
+	public final static int ER_ZLIB_Z_MEM_ERROR = 1257;
+
+	private MysqlErrorNumbers() {
+		// prevent instantiation
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlIO.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlIO.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlIO.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,3902 @@
+/*
+      Copyright (C) 2002-2004 MySQL AB
+
+      This program is free software; you can redistribute it and/or modify
+      it under the terms of version 2 of the GNU General Public License as
+      published by the Free Software Foundation.
+
+      There are special exceptions to the terms and conditions of the GPL
+      as it is applied to this software. View the full text of the
+      exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+      software distribution.
+
+      This program is distributed in the hope that it will be useful,
+      but WITHOUT ANY WARRANTY; without even the implied warranty of
+      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+      GNU General Public License for more details.
+
+      You should have received a copy of the GNU General Public License
+      along with this program; if not, write to the Free Software
+      Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+import com.mysql.jdbc.util.ReadAheadInputStream;
+import com.mysql.jdbc.util.ResultSetUtil;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.EOFException;
+import java.io.FileInputStream;
+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.SQLException;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Properties;
+import java.util.zip.Deflater;
+
+
+/**
+ * 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 $
+ *
+ * @see java.sql.Connection
+ */
+class MysqlIO {
+    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;
+    private static int maxBufferSize = 65535;
+    private static final int CLIENT_COMPRESS = 32; /* Can use compression
+    protcol */
+    protected static final int CLIENT_CONNECT_WITH_DB = 8;
+    private static final int CLIENT_FOUND_ROWS = 2;
+    private static final int CLIENT_LOCAL_FILES = 128; /* Can use LOAD DATA
+    LOCAL */
+
+    /* Found instead of
+       affected rows */
+    private static final int CLIENT_LONG_FLAG = 4; /* Get all column flags */
+    private static final int CLIENT_LONG_PASSWORD = 1; /* new more secure
+    passwords */
+    private static final int CLIENT_PROTOCOL_41 = 512; // for > 4.1.1
+    private static final int CLIENT_INTERACTIVE = 1024;
+    protected static final int CLIENT_SSL = 2048;
+    private static final int CLIENT_TRANSACTIONS = 8192; // Client knows about transactions
+    protected static final int CLIENT_RESERVED = 16384; // for 4.1.0 only
+    protected static final int CLIENT_SECURE_CONNECTION = 32768;
+    private static final int CLIENT_MULTI_QUERIES = 65536; // Enable/disable multiquery support
+    private static final int CLIENT_MULTI_RESULTS = 131072; // Enable/disable multi-results
+    private static final int SERVER_STATUS_IN_TRANS = 1;
+    private static final int SERVER_STATUS_AUTOCOMMIT = 2; // Server in auto_commit mode
+    private static final int SERVER_MORE_RESULTS_EXISTS = 8; // Multi query - next query exists
+    private static final int SERVER_QUERY_NO_GOOD_INDEX_USED = 16;
+    private static final int SERVER_QUERY_NO_INDEX_USED = 32;
+	private static final int  SERVER_STATUS_CURSOR_EXISTS = 64;
+    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
+
+    /**
+     * 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
+     * can decide what to do based on connection setting
+     */
+    protected final static String ZERO_DATE_VALUE_MARKER = "0000-00-00";
+    protected final static String ZERO_DATETIME_VALUE_MARKER = "0000-00-00 00:00:00";
+
+    static {
+        OutputStreamWriter outWriter = null;
+
+        //
+        // Use the I/O system to get the encoding (if possible), to avoid
+        // security restrictions on System.getProperty("file.encoding") in
+        // applets (why is that restricted?)
+        //
+        try {
+            outWriter = new OutputStreamWriter(new ByteArrayOutputStream());
+            jvmPlatformCharset = outWriter.getEncoding();
+        } finally {
+            try {
+                if (outWriter != null) {
+                    outWriter.close();
+                }
+            } catch (IOException ioEx) {
+                // ignore
+            }
+        }
+    }
+
+    /** Max number of bytes to dump when tracing the protocol */
+    private final static int MAX_PACKET_DUMP_LENGTH = 1024;
+    private boolean packetSequenceReset = false;
+    protected int serverCharsetIndex;
+
+    //
+    // Use this when reading in rows to avoid thousands of new()
+    // calls, because the byte arrays just get copied out of the
+    // packet anyway
+    //
+    private Buffer reusablePacket = null;
+    private Buffer sendPacket = null;
+    private Buffer sharedSendPacket = null;
+
+    /** Data to the server */
+    protected BufferedOutputStream mysqlOutput = null;
+    protected com.mysql.jdbc.Connection connection;
+    private Deflater deflater = null;
+    protected InputStream mysqlInput = null;
+    private LinkedList packetDebugRingBuffer = null;
+    private RowData streamingData = null;
+
+    /** The connection to the server */
+    protected Socket mysqlConnection = null;
+    private SocketFactory socketFactory = null;
+
+    //
+    // Packet used for 'LOAD DATA LOCAL INFILE'
+    //
+    // We use a SoftReference, so that we don't penalize intermittent
+    // use of this feature
+    //
+    private SoftReference loadFileBufRef;
+
+    //
+    // Used to send large packets to the server versions 4+
+    // We use a SoftReference, so that we don't penalize intermittent
+    // use of this feature
+    //
+    private SoftReference splitBufRef;
+    protected String host = null;
+    protected String seed;
+    private String serverVersion = null;
+    private String socketFactoryClassName = null;
+    private byte[] packetHeaderBuf = new byte[4];
+    private boolean colDecimalNeedsBump = false; // do we need to increment the colDecimal flag?
+    private boolean hadWarnings = false;
+    private boolean has41NewNewProt = false;
+
+    /** Does the server support long column info? */
+    private boolean hasLongColumnInfo = false;
+    private boolean isInteractiveClient = false;
+    private boolean logSlowQueries = false;
+
+    /**
+     * Does the character set of this connection match the character set of the
+     * platform
+     */
+    private boolean platformDbCharsetMatches = true; // changed once we've connected.
+    private boolean profileSql = false;
+    private boolean queryBadIndexUsed = false;
+    private boolean queryNoIndexUsed = false;
+
+    /** Should we use 4.1 protocol extensions? */
+    private boolean use41Extensions = false;
+    private boolean useCompression = false;
+    private boolean useNewLargePackets = false;
+    private boolean useNewUpdateCounts = false; // should we use the new larger update counts?
+    private byte packetSequence = 0;
+    private byte readPacketSequence = -1;
+    private boolean checkPacketSequence = false;
+    byte protocolVersion = 0;
+    private int maxAllowedPacket = 1024 * 1024;
+    protected int maxThreeBytes = 255 * 255 * 255;
+    protected int port = 3306;
+    protected int serverCapabilities;
+    private int serverMajorVersion = 0;
+    private int serverMinorVersion = 0;
+    private int serverStatus = 0;
+    private int serverSubMinorVersion = 0;
+    private int warningCount = 0;
+    protected long clientParam = 0;
+    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;
+
+    /**
+     * Constructor:  Connect to the MySQL server and setup a stream connection.
+     *
+     * @param host the hostname to connect to
+     * @param port the port number that the server is listening on
+     * @param props the Properties from DriverManager.getConnection()
+     * @param socketFactoryClassName the socket factory to use
+     * @param conn the Connection that is creating us
+     * @param socketTimeout the timeout to set for the socket (0 means no
+     *        timeout)
+     *
+     * @throws IOException if an IOException occurs during connect.
+     * @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 {
+        this.connection = conn;
+
+        if (this.connection.getEnablePacketDebug()) {
+            this.packetDebugRingBuffer = new LinkedList();
+        }
+
+        this.logSlowQueries = this.connection.getLogSlowQueries();
+
+        this.reusablePacket = new Buffer(
+        		this.connection.getNetBufferLength());
+
+        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.connection.getTraceProtocol(),
+        			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.autoGenerateTestcaseScript);
+    }
+
+    /**
+     * Does the server send back extra column info?
+     *
+     * @return true if so
+     */
+    public boolean hasLongColumnInfo() {
+        return this.hasLongColumnInfo;
+    }
+
+    protected boolean isDataAvailable() throws SQLException {
+        try {
+            return this.mysqlInput.available() > 0;
+        } catch (IOException ioEx) {
+            throw new CommunicationsException(this.connection,
+                this.lastPacketSentTimeMs, ioEx);
+        }
+    }
+
+    /**
+     * DOCUMENT ME!
+     *
+     * @return Returns the lastPacketSentTimeMs.
+     */
+    protected long getLastPacketSentTimeMs() {
+        return this.lastPacketSentTimeMs;
+    }
+
+    /**
+     * Build a result set. Delegates to buildResultSetWithRows() to build a
+     * JDBC-version-specific ResultSet, given rows as byte data, and field
+     * information.
+     *
+     * @param callingStatement DOCUMENT ME!
+     * @param columnCount the number of columns in the result set
+     * @param maxRows the maximum number of rows to read (-1 means all rows)
+     * @param resultSetType (TYPE_FORWARD_ONLY, TYPE_SCROLL_????)
+     * @param resultSetConcurrency the type of result set (CONCUR_UPDATABLE or
+     *        READ_ONLY)
+     * @param streamResults should the result set be read all at once, or
+     *        streamed?
+     * @param catalog the database name in use when the result set was created
+     * @param isBinaryEncoded is this result set in native encoding?
+     * @param unpackFieldInfo should we read MYSQL_FIELD info (if available)?
+     *
+     * @return a result set
+     *
+     * @throws SQLException if a database access error occurs
+     */
+    protected ResultSet getResultSet(Statement callingStatement,
+        long columnCount, int maxRows, int resultSetType,
+        int resultSetConcurrency, boolean streamResults, String catalog,
+        boolean isBinaryEncoded, boolean unpackFieldInfo)
+        throws SQLException {
+        Buffer packet; // The packet from the server
+        Field[] fields = null;
+
+        if (unpackFieldInfo) {
+            fields = new Field[(int) columnCount];
+        }
+
+        // Read in the column information
+        for (int i = 0; i < columnCount; i++) {
+            Buffer fieldPacket = null;
+
+            fieldPacket = readPacket();
+            
+            if (unpackFieldInfo) {
+                fields[i] = unpackField(fieldPacket, false);
+            }
+        }
+
+        packet = reuseAndReadPacket(this.reusablePacket);
+
+        readServerStatusForResultSets(packet);
+		
+		//
+		// Handle cursor-based fetch first
+		//
+		
+		if (this.connection.versionMeetsMinimum(5, 0, 2)
+				&& 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 & 
+						SERVER_STATUS_CURSOR_EXISTS) != 0;
+			}
+			
+			if (usingCursor) {
+				RowData rows = new CursorRowProvider(
+					this,
+					prepStmt,
+					((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields);
+
+				ResultSet rs = buildResultSetWithRows(
+					callingStatement,
+					catalog,
+					((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).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);
+        } else {
+            rowData = new RowDataDynamic(this, (int) columnCount, fields,
+                    isBinaryEncoded);
+            this.streamingData = rowData;
+        }
+
+        ResultSet rs = buildResultSetWithRows(callingStatement, catalog, fields,
+            rowData, resultSetType, resultSetConcurrency, isBinaryEncoded);
+ 
+        
+        
+        return rs;
+    }
+
+    /**
+     * Forcibly closes the underlying socket to MySQL.
+     */
+    protected final void forceClose() {
+        try {
+            if (this.mysqlInput != null) {
+                this.mysqlInput.close();
+            }
+        } catch (IOException ioEx) {
+            // we can't do anything constructive about this
+            // Let the JVM clean it up later
+            this.mysqlInput = null;
+        }
+
+        try {
+            if (this.mysqlOutput != null) {
+                this.mysqlOutput.close();
+            }
+        } catch (IOException ioEx) {
+            // we can't do anything constructive about this
+            // Let the JVM clean it up later
+            this.mysqlOutput = null;
+        }
+
+        try {
+            if (this.mysqlConnection != null) {
+                this.mysqlConnection.close();
+            }
+        } catch (IOException ioEx) {
+            // we can't do anything constructive about this
+            // Let the JVM clean it up later
+            this.mysqlConnection = null;
+        }
+    }
+
+    /**
+     * Read one packet from the MySQL server
+     *
+     * @return the packet from the server.
+     *
+     * @throws SQLException DOCUMENT ME!
+     * @throws CommunicationsException DOCUMENT ME!
+     */
+    protected final Buffer readPacket() 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;
+
+            // Read data
+            byte[] buffer = new byte[packetLength + 1];
+            int numBytesRead = readFully(this.mysqlInput, buffer, 0,
+                    packetLength);
+
+            if (numBytesRead != packetLength) {
+                throw new IOException("Short read, expected " +
+                    packetLength + " bytes, only read " + numBytesRead);
+            }
+
+            buffer[packetLength] = 0;
+
+            Buffer packet = new Buffer(buffer);
+            packet.setBufLength(packetLength + 1);
+
+            if (this.traceProtocol) {
+                StringBuffer traceMessageBuf = new StringBuffer();
+
+                traceMessageBuf.append(Messages.getString("MysqlIO.4")); //$NON-NLS-1$
+                traceMessageBuf.append(getPacketDumpToLog(packet,
+                        packetLength));
+
+                this.connection.getLog().logTrace(traceMessageBuf.toString());
+            }
+
+            if (this.enablePacketDebug) {
+                enqueuePacketForDebugging(false, false, 0,
+                    this.packetHeaderBuf, packet);
+            }
+
+            return packet;
+        } catch (IOException ioEx) {
+            throw new CommunicationsException(this.connection,
+                this.lastPacketSentTimeMs, ioEx);
+        } catch (OutOfMemoryError oom) {
+        	try {
+    			this.connection.realClose(false, false, true, oom);
+    		} finally {
+    			throw oom;
+    		}
+        }
+    }
+
+    /**
+     * Unpacks the Field information from the given packet. Understands pre 4.1
+     * and post 4.1 server version field packet structures.
+     *
+     * @param packet the packet containing the field information
+     * @param extractDefaultValues should default values be extracted?
+     *
+     * @return the unpacked field
+     *
+     * @throws SQLException DOCUMENT ME!
+     */
+    protected final Field unpackField(Buffer packet,
+        boolean extractDefaultValues) throws SQLException {
+        if (this.use41Extensions) {
+            // we only store the position of the string and
+            // materialize only if needed...
+            if (this.has41NewNewProt) {
+                // Not used yet, 5.0?
+                int catalogNameStart = packet.getPosition() + 1;
+                int catalogNameLength = packet.fastSkipLenString();
+                catalogNameStart = adjustStartForFieldLength(catalogNameStart, catalogNameLength);
+            }
+
+            int databaseNameStart = packet.getPosition() + 1;
+            int databaseNameLength = packet.fastSkipLenString();
+            databaseNameStart = adjustStartForFieldLength(databaseNameStart, databaseNameLength);
+
+            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();
+            originalTableNameStart = adjustStartForFieldLength(originalTableNameStart, originalTableNameLength);
+
+            // 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...
+            int originalColumnNameStart = packet.getPosition() + 1;
+            int originalColumnNameLength = packet.fastSkipLenString();
+            originalColumnNameStart = adjustStartForFieldLength(originalColumnNameStart, originalColumnNameLength);
+
+            packet.readByte();
+
+            short charSetNumber = (short) packet.readInt();
+
+            long colLength = 0;
+
+            if (this.has41NewNewProt) {
+                colLength = packet.readLong();
+            } else {
+                colLength = packet.readLongInt();
+            }
+
+            int colType = packet.readByte() & 0xff;
+
+            short colFlag = 0;
+
+            if (this.hasLongColumnInfo) {
+                colFlag = (short) packet.readInt();
+            } else {
+                colFlag = (short) (packet.readByte() & 0xff);
+            }
+
+            int colDecimals = packet.readByte() & 0xff;
+
+            int defaultValueStart = -1;
+            int defaultValueLength = -1;
+
+            if (extractDefaultValues) {
+                defaultValueStart = packet.getPosition() + 1;
+                defaultValueLength = packet.fastSkipLenString();
+            }
+
+            Field field = new Field(this.connection, packet.getByteBuffer(),
+                    databaseNameStart, databaseNameLength, tableNameStart,
+                    tableNameLength, originalTableNameStart,
+                    originalTableNameLength, nameStart, nameLength,
+                    originalColumnNameStart, originalColumnNameLength,
+                    colLength, colType, colFlag, colDecimals,
+                    defaultValueStart, defaultValueLength, charSetNumber);
+
+            return field;
+        }
+
+        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
+
+        short colFlag = 0;
+
+        if (this.hasLongColumnInfo) {
+            colFlag = (short) (packet.readInt());
+        } else {
+            colFlag = (short) (packet.readByte() & 0xff);
+        }
+
+        int colDecimals = (packet.readByte() & 0xff);
+
+        if (this.colDecimalNeedsBump) {
+            colDecimals++;
+        }
+
+        Field field = new Field(this.connection, packet.getByteBuffer(),
+                nameStart, nameLength, tableNameStart, tableNameLength,
+                colLength, colType, colFlag, colDecimals);
+
+        return field;
+    }
+
+    private int adjustStartForFieldLength(int nameStart, int nameLength) {
+    	if (nameLength < 251) {
+    		return nameStart;
+    	}
+    	
+		if (nameLength >= 251 && nameLength < 65536) {
+			return nameStart + 2;
+		}
+		
+		if (nameLength >= 65536 && nameLength < 16777216) {
+			return nameStart + 3;
+		}
+		
+		return nameStart + 8;
+	}
+
+    protected boolean isSetNeededForAutoCommitMode(boolean autoCommitFlag) {
+        if (this.use41Extensions && this.connection.getElideSetAutoCommits()) {
+            boolean autoCommitModeOnServer = ((this.serverStatus &
+                SERVER_STATUS_AUTOCOMMIT) != 0);
+
+            if (!autoCommitFlag) {
+                // 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
+                boolean inTransactionOnServer = ((this.serverStatus &
+                    SERVER_STATUS_IN_TRANS) != 0);
+
+                return !inTransactionOnServer;
+            }
+
+            return !autoCommitModeOnServer;
+        }
+
+        return true;
+    }
+
+    /**
+     * Re-authenticates as the given user and password
+     *
+     * @param userName DOCUMENT ME!
+     * @param password DOCUMENT ME!
+     * @param database DOCUMENT ME!
+     *
+     * @throws SQLException DOCUMENT ME!
+     */
+    protected void changeUser(String userName, String password, String database)
+        throws SQLException {
+        this.packetSequence = -1;
+
+        int passwordLength = 16;
+        int userLength = 0;
+
+        if (userName != null) {
+            userLength = userName.length();
+        }
+
+        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);
+
+            if (versionMeetsMinimum(4, 1, 1)) {
+                secureAuth411(changeUserPacket, packLength, userName, password,
+                    database, false);
+            } else {
+                secureAuth(changeUserPacket, packLength, userName, password,
+                    database, false);
+            }
+        } else {
+            // Passwords can be 16 chars long
+            Buffer packet = new Buffer(packLength);
+            packet.writeByte((byte) MysqlDefs.COM_CHANGE_USER);
+
+            // User/Password data
+            packet.writeString(userName);
+
+            if (this.protocolVersion > 9) {
+                packet.writeString(Util.newCrypt(password, this.seed));
+            } else {
+                packet.writeString(Util.oldCrypt(password, this.seed));
+            }
+
+			boolean localUseConnectWithDb = this.useConnectWithDb && 
+				(database != null && database.length() > 0);
+			
+            if (localUseConnectWithDb) {
+                packet.writeString(database);
+            }
+
+            send(packet, packet.getPosition());
+            checkErrorPacket();
+			
+			if (!localUseConnectWithDb) {
+				changeDatabaseTo(database);
+			}
+        }
+    }
+
+    /**
+     * Checks for errors in the reply packet, and if none, returns the reply
+     * packet, ready for reading
+     *
+     * @return a packet ready for reading.
+     *
+     * @throws SQLException is the packet is an error packet
+     */
+    protected Buffer checkErrorPacket() throws SQLException {
+        return checkErrorPacket(-1);
+    }
+
+    /**
+     * Determines if the database charset is the same as the platform charset
+     */
+    protected void checkForCharsetMismatch() {
+        if (this.connection.getUseUnicode() &&
+                (this.connection.getEncoding() != null)) {
+            String encodingToCheck = jvmPlatformCharset;
+
+            if (encodingToCheck == null) {
+                encodingToCheck = System.getProperty("file.encoding"); //$NON-NLS-1$
+            }
+
+            if (encodingToCheck == null) {
+                this.platformDbCharsetMatches = false;
+            } else {
+                this.platformDbCharsetMatches = encodingToCheck.equals(this.connection.getEncoding());
+            }
+        }
+    }
+
+    protected void clearInputStream() throws SQLException {
+    
+        try {
+            int len = this.mysqlInput.available();
+
+            while (len > 0) {
+                this.mysqlInput.skip(len);
+                len = this.mysqlInput.available();
+            }
+        } catch (IOException ioEx) {
+            throw new CommunicationsException(this.connection,
+                this.lastPacketSentTimeMs, ioEx);
+        }
+    }
+
+    protected void resetReadPacketSequence() {
+        this.readPacketSequence = 0;
+    }
+
+    protected void dumpPacketRingBuffer() throws SQLException {
+        if ((this.packetDebugRingBuffer != null) &&
+                this.connection.getEnablePacketDebug()) {
+            StringBuffer dumpBuffer = new StringBuffer();
+
+            dumpBuffer.append("Last " + this.packetDebugRingBuffer.size() +
+                " packets received from server, from oldest->newest:\n");
+            dumpBuffer.append("\n");
+
+            for (Iterator ringBufIter = this.packetDebugRingBuffer.iterator();
+                    ringBufIter.hasNext();) {
+                dumpBuffer.append((StringBuffer) ringBufIter.next());
+                dumpBuffer.append("\n");
+            }
+
+            this.connection.getLog().logTrace(dumpBuffer.toString());
+        }
+    }
+
+    /**
+     * Runs an 'EXPLAIN' on the given query and dumps the results to  the log
+     *
+     * @param querySQL DOCUMENT ME!
+     * @param truncatedQuery DOCUMENT ME!
+     *
+     * @throws SQLException DOCUMENT ME!
+     */
+    protected void explainSlowQuery(byte[] querySQL, String truncatedQuery)
+        throws SQLException {
+        if (StringUtils.startsWithIgnoreCaseAndWs(truncatedQuery, "SELECT")) { //$NON-NLS-1$
+
+            PreparedStatement stmt = null;
+            java.sql.ResultSet rs = null;
+
+            try {
+                stmt = this.connection.clientPrepareStatement("EXPLAIN ?"); //$NON-NLS-1$
+                stmt.setBytesNoEscapeNoQuotes(1, querySQL);
+                rs = stmt.executeQuery();
+
+                StringBuffer explainResults = new StringBuffer(Messages.getString(
+                            "MysqlIO.8") + truncatedQuery //$NON-NLS-1$
+                         +Messages.getString("MysqlIO.9")); //$NON-NLS-1$
+
+                ResultSetUtil.appendResultSetSlashGStyle(explainResults, rs);
+
+                this.connection.getLog().logWarn(explainResults.toString());
+            } catch (SQLException sqlEx) {
+            } finally {
+                if (rs != null) {
+                    rs.close();
+                }
+
+                if (stmt != null) {
+                    stmt.close();
+                }
+            }
+        } else {
+        }
+    }
+
+    static int getMaxBuf() {
+        return maxBufferSize;
+    }
+
+    /**
+     * Get the major version of the MySQL server we are talking to.
+     *
+     * @return DOCUMENT ME!
+     */
+    final int getServerMajorVersion() {
+        return this.serverMajorVersion;
+    }
+
+    /**
+     * Get the minor version of the MySQL server we are talking to.
+     *
+     * @return DOCUMENT ME!
+     */
+    final int getServerMinorVersion() {
+        return this.serverMinorVersion;
+    }
+
+    /**
+     * Get the sub-minor version of the MySQL server we are talking to.
+     *
+     * @return DOCUMENT ME!
+     */
+    final int getServerSubMinorVersion() {
+        return this.serverSubMinorVersion;
+    }
+
+    /**
+     * Get the version string of the server we are talking to
+     *
+     * @return DOCUMENT ME!
+     */
+    String getServerVersion() {
+        return this.serverVersion;
+    }
+
+    /**
+     * Initialize communications with the MySQL server. Handles logging on, and
+     * handling initial connection errors.
+     *
+     * @param user DOCUMENT ME!
+     * @param password DOCUMENT ME!
+     * @param database DOCUMENT ME!
+     *
+     * @throws SQLException DOCUMENT ME!
+     * @throws CommunicationsException DOCUMENT ME!
+     */
+    void doHandshake(String user, String password, String database)
+        throws SQLException {
+        // Read the first packet
+        this.checkPacketSequence = false;
+        this.readPacketSequence = 0;
+
+        Buffer buf = readPacket();
+
+        // Get the protocol version
+        this.protocolVersion = buf.readByte();
+
+        if (this.protocolVersion == -1) {
+            try {
+                this.mysqlConnection.close();
+            } catch (Exception e) {
+                ; // ignore
+            }
+
+            int errno = 2000;
+
+            errno = buf.readInt();
+
+            String serverErrorMessage = buf.readString();
+
+            StringBuffer errorBuf = new StringBuffer(Messages.getString(
+                        "MysqlIO.10")); //$NON-NLS-1$
+            errorBuf.append(serverErrorMessage);
+            errorBuf.append("\""); //$NON-NLS-1$
+
+            String xOpen = SQLError.mysqlToSqlState(errno,
+                    this.connection.getUseSqlStateCodes());
+
+            throw SQLError.createSQLException(SQLError.get(xOpen) + ", " //$NON-NLS-1$
+                 +errorBuf.toString(), xOpen, errno);
+        }
+
+        this.serverVersion = buf.readString();
+
+        // Parse the server version into major/minor/subminor
+        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) {
+                ;
+            }
+
+            String remaining = this.serverVersion.substring(point + 1,
+                    this.serverVersion.length());
+            point = remaining.indexOf("."); //$NON-NLS-1$
+
+            if (point != -1) {
+                try {
+                    int n = Integer.parseInt(remaining.substring(0, point));
+                    this.serverMinorVersion = n;
+                } catch (NumberFormatException nfe) {
+                    ;
+                }
+
+                remaining = remaining.substring(point + 1, remaining.length());
+
+                int pos = 0;
+
+                while (pos < remaining.length()) {
+                    if ((remaining.charAt(pos) < '0') ||
+                            (remaining.charAt(pos) > '9')) {
+                        break;
+                    }
+
+                    pos++;
+                }
+
+                try {
+                    int n = Integer.parseInt(remaining.substring(0, pos));
+                    this.serverSubMinorVersion = n;
+                } catch (NumberFormatException nfe) {
+                    ;
+                }
+            }
+        }
+
+        if (versionMeetsMinimum(4, 0, 8)) {
+            this.maxThreeBytes = (256 * 256 * 256) - 1;
+            this.useNewLargePackets = true;
+        } else {
+            this.maxThreeBytes = 255 * 255 * 255;
+            this.useNewLargePackets = false;
+        }
+
+        this.colDecimalNeedsBump = versionMeetsMinimum(3, 23, 0);
+        this.colDecimalNeedsBump = !versionMeetsMinimum(3, 23, 15); // guess? Not noted in changelog
+        this.useNewUpdateCounts = versionMeetsMinimum(3, 22, 5);
+
+        threadId = buf.readLong(); 
+        this.seed = buf.readString();
+
+        this.serverCapabilities = 0;
+
+        if (buf.getPosition() < buf.getBufLength()) {
+            this.serverCapabilities = buf.readInt();
+        }
+
+        if (versionMeetsMinimum(4, 1, 1)) {
+            int position = buf.getPosition();
+
+            /* New protocol with 16 bytes to describe server characteristics */
+            this.serverCharsetIndex = buf.readByte() & 0xff;
+            this.serverStatus = buf.readInt();
+            buf.setPosition(position + 16);
+
+            String seedPart2 = buf.readString();
+            StringBuffer newSeed = new StringBuffer(20);
+            newSeed.append(this.seed);
+            newSeed.append(seedPart2);
+            this.seed = newSeed.toString();
+        }
+
+        if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) &&
+                this.connection.getUseCompression()) {
+            this.clientParam |= CLIENT_COMPRESS;
+        }
+
+		this.useConnectWithDb = (database != null) && 
+			(database.length() > 0) &&
+			!this.connection.getCreateDatabaseIfNotExist();
+		
+        if (this.useConnectWithDb) {
+            this.clientParam |= CLIENT_CONNECT_WITH_DB;
+        }
+
+        if (((this.serverCapabilities & CLIENT_SSL) == 0) &&
+                this.connection.getUseSSL()) {
+            if (this.connection.getRequireSSL()) {
+                this.connection.close();
+                forceClose();
+                throw SQLError.createSQLException(Messages.getString("MysqlIO.15"), //$NON-NLS-1$
+                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
+            }
+
+            this.connection.setUseSSL(false);
+        }
+
+        if ((this.serverCapabilities & CLIENT_LONG_FLAG) != 0) {
+            // We understand other column flags, as well
+            this.clientParam |= CLIENT_LONG_FLAG;
+            this.hasLongColumnInfo = true;
+        }
+
+        // return FOUND rows
+        this.clientParam |= CLIENT_FOUND_ROWS;
+
+        if (this.connection.getAllowLoadLocalInfile()) {
+            this.clientParam |= CLIENT_LOCAL_FILES;
+        }
+
+        if (this.isInteractiveClient) {
+            this.clientParam |= CLIENT_INTERACTIVE;
+        }
+
+        // Authenticate
+        if (this.protocolVersion > 9) {
+            this.clientParam |= CLIENT_LONG_PASSWORD; // for long passwords
+        } else {
+            this.clientParam &= ~CLIENT_LONG_PASSWORD;
+        }
+
+        //
+        // 4.1 has some differences in the protocol
+        //
+        if (versionMeetsMinimum(4, 1, 0)) {
+            if (versionMeetsMinimum(4, 1, 1)) {
+                this.clientParam |= CLIENT_PROTOCOL_41;
+                this.has41NewNewProt = true;
+
+                // Need this to get server status values
+                this.clientParam |= CLIENT_TRANSACTIONS;
+
+                // We always allow multiple result sets
+                this.clientParam |= CLIENT_MULTI_RESULTS;
+
+                // We allow the user to configure whether
+                // or not they want to support multiple queries
+                // (by default, this is disabled).
+                if (this.connection.getAllowMultiQueries()) {
+                    this.clientParam |= CLIENT_MULTI_QUERIES;
+                }
+            } else {
+                this.clientParam |= CLIENT_RESERVED;
+                this.has41NewNewProt = false;
+            }
+
+            this.use41Extensions = true;
+        }
+
+        int passwordLength = 16;
+        int userLength = 0;
+        int databaseLength = 0;
+
+        if (user != null) {
+            userLength = user.length();
+        }
+
+        if (database != null) {
+            databaseLength = database.length();
+        }
+
+        int packLength = (userLength + passwordLength + databaseLength) + 7 +
+            HEADER_LENGTH;
+        Buffer packet = null;
+
+        if (!this.connection.getUseSSL()) {
+            if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
+                this.clientParam |= CLIENT_SECURE_CONNECTION;
+
+                if (versionMeetsMinimum(4, 1, 1)) {
+                    secureAuth411(null, packLength, user, password, database,
+                        true);
+                } else {
+                    secureAuth(null, packLength, user, password, database, true);
+                }
+            } else {
+                // Passwords can be 16 chars long
+                packet = new Buffer(packLength);
+
+                if ((this.clientParam & CLIENT_RESERVED) != 0) {
+                    if (versionMeetsMinimum(4, 1, 1)) {
+                        packet.writeLong(this.clientParam);
+                        packet.writeLong(this.maxThreeBytes);
+
+                        // charset, JDBC will connect as 'latin1',
+                        // and use 'SET NAMES' to change to the desired
+                        // charset after the connection is established.
+                        packet.writeByte((byte) 8);
+
+                        // Set of bytes reserved for future use.
+                        packet.writeBytesNoNull(new byte[23]);
+                    } else {
+                        packet.writeLong(this.clientParam);
+                        packet.writeLong(this.maxThreeBytes);
+                    }
+                } else {
+                    packet.writeInt((int) this.clientParam);
+                    packet.writeLongInt(this.maxThreeBytes);
+                }
+
+                // User/Password data
+                packet.writeString(user, "Cp1252", this.connection);
+
+                if (this.protocolVersion > 9) {
+                    packet.writeString(Util.newCrypt(password, this.seed), "Cp1252", this.connection);
+                } else {
+                    packet.writeString(Util.oldCrypt(password, this.seed), "Cp1252", this.connection);
+                }
+
+                if (this.useConnectWithDb) {
+                    packet.writeString(database, "Cp1252", this.connection);
+                }
+
+                send(packet, packet.getPosition());
+            }
+        } else {
+            negotiateSSLConnection(user, password, database, packLength);
+        }
+
+        // Check for errors, not for 4.1.1 or newer,
+        // as the new auth protocol doesn't work that way
+        // (see secureAuth411() for more details...)
+        if (!versionMeetsMinimum(4, 1, 1)) {
+            checkErrorPacket();
+        }
+
+        //
+        // Can't enable compression until after handshake
+        //
+        if (((this.serverCapabilities & CLIENT_COMPRESS) != 0) &&
+                this.connection.getUseCompression()) {
+            // The following matches with ZLIB's
+            // compress()
+            this.deflater = new Deflater();
+            this.useCompression = true;
+            this.mysqlInput = new CompressedInputStream(this.connection,
+                    this.mysqlInput);
+        }
+
+        if (!this.useConnectWithDb) {
+            changeDatabaseTo(database);
+        }
+    }
+
+	private void changeDatabaseTo(String database) throws SQLException, CommunicationsException {
+		if (database == null || database.length() == 0) {
+			return;
+		}
+		
+		try {
+		    sendCommand(MysqlDefs.INIT_DB, database, null, false, null);
+		} catch (Exception ex) {
+			if (this.connection.getCreateDatabaseIfNotExist()) {
+				sendCommand(MysqlDefs.QUERY, "CREATE DATABASE IF NOT EXISTS " +
+					database,
+					null, false, null);
+				sendCommand(MysqlDefs.INIT_DB, database, null, false, null);
+			} else {
+				throw new CommunicationsException(this.connection,
+						this.lastPacketSentTimeMs, ex);
+			}
+		}
+	}
+
+    /**
+    * Retrieve one row from the MySQL server. Note: this method is not
+    * thread-safe, but it is only called from methods that are guarded by
+    * synchronizing on this object.
+    *
+    * @param fields DOCUMENT ME!
+    * @param columnCount DOCUMENT ME!
+    * @param isBinaryEncoded DOCUMENT ME!
+    * @param resultSetConcurrency DOCUMENT ME!
+    *
+    * @return DOCUMENT ME!
+    *
+    * @throws SQLException DOCUMENT ME!
+    */
+    final Object[] nextRow(Field[] fields, int columnCount,
+        boolean isBinaryEncoded, int resultSetConcurrency)
+        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 (!isBinaryEncoded) {
+            //
+            // Didn't read an error, so re-position to beginning
+            // of packet in order to read result set data
+            //
+            rowPacket.setPosition(rowPacket.getPosition() - 1);
+
+            if (!rowPacket.isLastDataPacket()) {
+                byte[][] rowData = new byte[columnCount][];
+
+                int offset = 0;
+
+                for (int i = 0; i < columnCount; i++) {
+                    rowData[i] = rowPacket.readLenByteArray(offset);
+                }
+
+                return rowData;
+            }
+
+            readServerStatusForResultSets(rowPacket);
+
+            return null;
+        }
+
+        // 
+        // Handle binary-encoded data for server-side   
+        // PreparedStatements...
+        //
+        if (!rowPacket.isLastDataPacket()) {
+            return unpackBinaryResultSetRow(fields, rowPacket,
+                resultSetConcurrency);
+        }
+
+        rowPacket.setPosition(rowPacket.getPosition() - 1);
+        readServerStatusForResultSets(rowPacket);
+
+        return null;
+    }
+
+    /**
+     * Log-off of the MySQL server and close the socket.
+     *
+     * @throws SQLException DOCUMENT ME!
+     */
+    final void quit() throws SQLException {
+        Buffer packet = new Buffer(6);
+        this.packetSequence = -1;
+        packet.writeByte((byte) MysqlDefs.QUIT);
+        send(packet, packet.getPosition());
+        forceClose();
+    }
+
+    /**
+     * Returns the packet used for sending data (used by PreparedStatement)
+     * Guarded by external synchronization on a mutex.
+     *
+     * @return A packet to send data with
+     */
+    Buffer getSharedSendPacket() {
+        if (this.sharedSendPacket == null) {
+        	this.sharedSendPacket = new Buffer(
+        			this.connection.getNetBufferLength());
+        }
+
+        return this.sharedSendPacket;
+    }
+
+    void closeStreamer(RowData streamer) throws SQLException {
+        if (this.streamingData == null) {
+            throw SQLError.createSQLException(Messages.getString("MysqlIO.17") //$NON-NLS-1$
+                 +streamer + Messages.getString("MysqlIO.18")); //$NON-NLS-1$
+        }
+
+        if (streamer != this.streamingData) {
+            throw SQLError.createSQLException(Messages.getString("MysqlIO.19") //$NON-NLS-1$
+                 +streamer + Messages.getString("MysqlIO.20") //$NON-NLS-1$
+                 +Messages.getString("MysqlIO.21") //$NON-NLS-1$
+                 +Messages.getString("MysqlIO.22")); //$NON-NLS-1$
+        }
+
+        this.streamingData = null;
+    }
+
+    ResultSet readAllResults(Statement callingStatement, int maxRows,
+        int resultSetType, int resultSetConcurrency, boolean streamResults,
+        String catalog, Buffer resultPacket, boolean isBinaryEncoded,
+        long preSentColumnCount, boolean unpackFieldInfo)
+        throws SQLException {
+        resultPacket.setPosition(resultPacket.getPosition() - 1);
+
+        ResultSet topLevelResultSet = readResultsForQueryOrUpdate(callingStatement,
+                maxRows, resultSetType, resultSetConcurrency, streamResults,
+                catalog, resultPacket, isBinaryEncoded, preSentColumnCount,
+                unpackFieldInfo);
+
+        ResultSet currentResultSet = topLevelResultSet;
+
+        boolean checkForMoreResults = ((this.clientParam &
+            CLIENT_MULTI_RESULTS) != 0);
+
+        boolean serverHasMoreResults = (this.serverStatus &
+            SERVER_MORE_RESULTS_EXISTS) != 0;
+
+        //
+        // TODO: We need to support streaming of multiple result sets
+        //
+        if (serverHasMoreResults && streamResults) {
+            clearInputStream();
+
+            throw SQLError.createSQLException(Messages.getString("MysqlIO.23"), //$NON-NLS-1$
+                SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+        }
+
+        boolean moreRowSetsExist = checkForMoreResults & serverHasMoreResults;
+
+        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,
+                    maxRows, resultSetType, resultSetConcurrency,
+                    streamResults, catalog, fieldPacket, isBinaryEncoded,
+                    preSentColumnCount, unpackFieldInfo);
+
+            currentResultSet.setNextResultSet(newResultSet);
+
+            currentResultSet = newResultSet;
+
+            moreRowSetsExist = (this.serverStatus & SERVER_MORE_RESULTS_EXISTS) != 0;
+        }
+
+        if (!streamResults) {
+            clearInputStream();
+        }
+
+        reclaimLargeReusablePacket();
+
+        return topLevelResultSet;
+    }
+
+    /**
+     * Sets the buffer size to max-buf
+     */
+    void resetMaxBuf() {
+        this.maxAllowedPacket = this.connection.getMaxAllowedPacket();
+    }
+
+    /**
+     * Send a command to the MySQL server If data is to be sent with command,
+     * it should be put in extraData.
+     *
+     * Raw packets can be sent by setting queryPacket to something other
+     * than null.
+     *
+     * @param command the MySQL protocol 'command' from MysqlDefs
+     * @param extraData any 'string' data for the command
+     * @param queryPacket a packet pre-loaded with data for the protocol (i.e.
+     * from a client-side prepared statement).
+     * @param skipCheck do not call checkErrorPacket() if true
+     * @param extraDataCharEncoding the character encoding of the extraData
+     * parameter.
+     *
+     * @return the response packet from the server
+     *
+     * @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 {
+        //
+        // We cache these locally, per-command, as the checks
+        // for them are in very 'hot' sections of the I/O code
+        // and we save 10-15% in overall performance by doing this...
+        //
+        this.enablePacketDebug = this.connection.getEnablePacketDebug();
+        this.traceProtocol = this.connection.getTraceProtocol();
+        this.readPacketSequence = 0;
+
+        try {
+        	
+            checkForOutstandingStreamingData();
+           
+            // Clear serverStatus...this value is guarded by an
+            // external mutex, as you can only ever be processing 
+            // one command at a time
+            this.serverStatus = 0;
+            this.hadWarnings = false;
+            this.warningCount = 0;
+
+            this.queryNoIndexUsed = false;
+            this.queryBadIndexUsed = false;
+
+            //
+            // Compressed input stream needs cleared at beginning
+            // of each command execution...
+            //
+            if (this.useCompression) {
+                int bytesLeft = this.mysqlInput.available();
+
+                if (bytesLeft > 0) {
+                    this.mysqlInput.skip(bytesLeft);
+                }
+            }
+
+            try {
+                clearInputStream();
+
+                //
+                // PreparedStatements construct their own packets,
+                // for efficiency's sake.
+                //
+                // If this is a generic query, we need to re-use
+                // the sending packet.
+                //
+                if (queryPacket == null) {
+                    int packLength = HEADER_LENGTH + COMP_HEADER_LENGTH + 1 +
+                        ((extraData != null) ? extraData.length() : 0) + 2;
+
+                    if (this.sendPacket == null) {
+                        this.sendPacket = new Buffer(packLength);
+                    }
+
+                    this.packetSequence = -1;
+                    this.readPacketSequence = 0;
+                    this.checkPacketSequence = true;
+                    this.sendPacket.clear();
+
+                    this.sendPacket.writeByte((byte) command);
+
+                    if ((command == MysqlDefs.INIT_DB) ||
+                            (command == MysqlDefs.CREATE_DB) ||
+                            (command == MysqlDefs.DROP_DB) ||
+                            (command == MysqlDefs.QUERY) ||
+                            (command == MysqlDefs.COM_PREPARE)) {
+                        if (extraDataCharEncoding == null) {
+                            this.sendPacket.writeStringNoNull(extraData);
+                        } else {
+                            this.sendPacket.writeStringNoNull(extraData,
+                                extraDataCharEncoding,
+                                this.connection.getServerCharacterEncoding(),
+                                this.connection.parserKnowsUnicode(), this.connection);
+                        }
+                    } else if (command == MysqlDefs.PROCESS_KILL) {
+                        long id = new Long(extraData).longValue();
+                        this.sendPacket.writeLong(id);
+                    }
+
+                    send(this.sendPacket, this.sendPacket.getPosition());
+                } else {
+                    this.packetSequence = -1;
+                    send(queryPacket, queryPacket.getPosition()); // packet passed by PreparedStatement
+                }
+            } catch (SQLException sqlEx) {
+                // don't wrap SQLExceptions
+                throw sqlEx;
+            } catch (Exception ex) {
+                throw new CommunicationsException(this.connection,
+                    this.lastPacketSentTimeMs, ex);
+            }
+
+            Buffer returnPacket = null;
+
+            if (!skipCheck) {
+                if ((command == MysqlDefs.COM_EXECUTE) ||
+                        (command == MysqlDefs.COM_RESET_STMT)) {
+                    this.readPacketSequence = 0;
+                    this.packetSequenceReset = true;
+                }
+
+                returnPacket = checkErrorPacket(command);
+            }
+
+            return returnPacket;
+        } catch (IOException ioEx) {
+            throw new CommunicationsException(this.connection,
+                this.lastPacketSentTimeMs, ioEx);
+        }
+    }
+
+    /**
+     * Send a query stored in a packet directly to the server.
+     *
+     * @param callingStatement DOCUMENT ME!
+     * @param resultSetConcurrency DOCUMENT ME!
+     * @param characterEncoding DOCUMENT ME!
+     * @param queryPacket DOCUMENT ME!
+     * @param maxRows DOCUMENT ME!
+     * @param conn DOCUMENT ME!
+     * @param resultSetType DOCUMENT ME!
+     * @param resultSetConcurrency DOCUMENT ME!
+     * @param streamResults DOCUMENT ME!
+     * @param catalog DOCUMENT ME!
+     * @param unpackFieldInfo should we read MYSQL_FIELD info (if available)?
+     *
+     * @return DOCUMENT ME!
+     *
+     * @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;
+
+        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;
+
+            if (this.sendPacket == null) {
+            	this.sendPacket = new Buffer(packLength);
+            } else {
+                this.sendPacket.clear();
+            }
+
+            this.sendPacket.writeByte((byte) MysqlDefs.QUERY);
+
+            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);
+            }
+
+            queryPacket = this.sendPacket;
+        }
+
+        byte[] queryBuf = null;
+        int oldPacketPosition = 0;
+
+        
+        
+        if (needToGrabQueryFromPacket) {
+            queryBuf = queryPacket.getByteBuffer();
+
+            // save the packet position
+            oldPacketPosition = queryPacket.getPosition();
+
+            queryStartTime = System.currentTimeMillis();
+        }
+
+        // Send query command and sql query string
+        Buffer resultPacket = sendCommand(MysqlDefs.QUERY, null, queryPacket,
+                false, null);
+
+        long fetchBeginTime = 0;
+        long fetchEndTime = 0;
+
+        String profileQueryToLog = null;
+
+        boolean queryWasSlow = false;
+
+        if (this.profileSql || this.logSlowQueries) {
+            queryEndTime = System.currentTimeMillis();
+
+            boolean shouldExtractQuery = false;
+
+            if (this.profileSql) {
+                shouldExtractQuery = true;
+            } else if (this.logSlowQueries &&
+                    ((queryEndTime - queryStartTime) > this.connection.getSlowQueryThresholdMillis())) {
+                shouldExtractQuery = true;
+                queryWasSlow = true;
+            }
+
+            if (shouldExtractQuery) {
+                // Extract the actual query from the network packet 
+                boolean truncated = false;
+
+                int extractPosition = oldPacketPosition;
+
+                if (oldPacketPosition > this.connection.getMaxQuerySizeToLog()) {
+                    extractPosition = this.connection.getMaxQuerySizeToLog() + 5;
+                    truncated = true;
+                }
+
+                profileQueryToLog = new String(queryBuf, 5,
+                        (extractPosition - 5));
+
+                if (truncated) {
+                    profileQueryToLog += Messages.getString("MysqlIO.25"); //$NON-NLS-1$
+                }
+            }
+
+            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);
+
+        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);
+
+            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,
+                    rs.resultId, System.currentTimeMillis(),
+                    (int) (queryEndTime - queryStartTime), null,
+                    new Throwable(), profileQueryToLog));
+            
+            //this.connection.getLog().logWarn(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.profileSql) {
+            fetchEndTime = System.currentTimeMillis();
+
+            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,
+                    rs.resultId, System.currentTimeMillis(),
+                    (int) (queryEndTime - queryStartTime), null,
+                    new Throwable(), profileQueryToLog));
+
+            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 (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));
+            }
+
+            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.hadWarnings) {
+            scanForAndThrowDataTruncation();
+        }
+
+        return rs;
+    }
+
+    /**
+     * Returns the host this IO is connected to
+     *
+     * @return DOCUMENT ME!
+     */
+    String getHost() {
+        return this.host;
+    }
+
+    /**
+     * Is the version of the MySQL server we are connected to the given
+     * version?
+     *
+     * @param major the major version
+     * @param minor the minor version
+     * @param subminor the subminor version
+     *
+     * @return true if the version of the MySQL server we are connected  is the
+     *         given version
+     */
+    boolean isVersion(int major, int minor, int subminor) {
+        return ((major == getServerMajorVersion()) &&
+        (minor == getServerMinorVersion()) &&
+        (subminor == getServerSubMinorVersion()));
+    }
+
+    /**
+     * Does the version of the MySQL server we are connected to meet the given
+     * minimums?
+     *
+     * @param major DOCUMENT ME!
+     * @param minor DOCUMENT ME!
+     * @param subminor DOCUMENT ME!
+     *
+     * @return DOCUMENT ME!
+     */
+    boolean versionMeetsMinimum(int major, int minor, int subminor) {
+        if (getServerMajorVersion() >= major) {
+            if (getServerMajorVersion() == major) {
+                if (getServerMinorVersion() >= minor) {
+                    if (getServerMinorVersion() == minor) {
+                        return (getServerSubMinorVersion() >= subminor);
+                    }
+
+                    // newer than major.minor
+                    return true;
+                }
+
+                // older than major.minor
+                return false;
+            }
+
+            // newer than major  
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Returns the hex dump of the given packet, truncated to
+     * MAX_PACKET_DUMP_LENGTH if packetLength exceeds that value.
+     *
+     * @param packetToDump the packet to dump in hex
+     * @param packetLength the number of bytes to dump
+     *
+     * @return the hex dump of the given packet
+     */
+    private final static String getPacketDumpToLog(Buffer packetToDump,
+        int packetLength) {
+        if (packetLength < MAX_PACKET_DUMP_LENGTH) {
+            return packetToDump.dump(packetLength);
+        }
+
+        StringBuffer packetDumpBuf = new StringBuffer(MAX_PACKET_DUMP_LENGTH * 4);
+        packetDumpBuf.append(packetToDump.dump(MAX_PACKET_DUMP_LENGTH));
+        packetDumpBuf.append(Messages.getString("MysqlIO.36")); //$NON-NLS-1$
+        packetDumpBuf.append(MAX_PACKET_DUMP_LENGTH);
+        packetDumpBuf.append(Messages.getString("MysqlIO.37")); //$NON-NLS-1$
+
+        return packetDumpBuf.toString();
+    }
+
+    private final int readFully(InputStream in, byte[] b, int off, int len)
+        throws IOException {
+        if (len < 0) {
+            throw new IndexOutOfBoundsException();
+        }
+
+        int n = 0;
+
+        while (n < len) {
+            int count = in.read(b, off + n, len - n);
+
+            if (count < 0) {
+                throw new EOFException();
+            }
+
+            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.
+     *
+     * @param callingStatement DOCUMENT ME!
+     * @param maxRows the maximum rows to return in the result set.
+     * @param resultSetType scrollability
+     * @param resultSetConcurrency updatability
+     * @param streamResults should the driver leave the results on the wire,
+     *        and read them only when needed?
+     * @param catalog the catalog in use
+     * @param resultPacket the first packet of information in the result set
+     * @param isBinaryEncoded is this result set from a prepared statement?
+     * @param preSentColumnCount do we already know the number of columns?
+     * @param unpackFieldInfo should we unpack the field information?
+     *
+     * @return a result set that either represents the rows, or an update count
+     *
+     * @throws SQLException if an error occurs while reading the rows
+     */
+    private final ResultSet readResultsForQueryOrUpdate(
+        Statement callingStatement, int maxRows, int resultSetType,
+        int resultSetConcurrency, boolean streamResults, String catalog,
+        Buffer resultPacket, boolean isBinaryEncoded, long preSentColumnCount,
+        boolean unpackFieldInfo) throws SQLException {
+        long columnCount = resultPacket.readFieldLength();
+
+        if (columnCount == 0) {
+            return buildResultSetWithUpdates(callingStatement, resultPacket);
+        } else if (columnCount == Buffer.NULL_LENGTH) {
+            String charEncoding = null;
+
+            if (this.connection.getUseUnicode()) {
+                charEncoding = this.connection.getEncoding();
+            }
+
+            String fileName = null;
+
+            if (this.platformDbCharsetMatches) {
+                fileName = ((charEncoding != null)
+                    ? resultPacket.readString(charEncoding)
+                    : resultPacket.readString());
+            } else {
+                fileName = resultPacket.readString();
+            }
+
+            return sendFileToServer(callingStatement, fileName);
+        } else {
+            com.mysql.jdbc.ResultSet results = getResultSet(callingStatement,
+                    columnCount, maxRows, resultSetType, resultSetConcurrency,
+                    streamResults, catalog, isBinaryEncoded, unpackFieldInfo);
+
+            return results;
+        }
+    }
+
+    private int alignPacketSize(int a, int l) {
+        return ((((a) + (l)) - 1) & ~((l) - 1));
+    }
+
+    private com.mysql.jdbc.ResultSet buildResultSetWithRows(
+        Statement callingStatement, String catalog,
+        com.mysql.jdbc.Field[] fields, RowData rows, int resultSetType,
+        int resultSetConcurrency, boolean isBinaryEncoded)
+        throws SQLException {
+        ResultSet rs = null;
+
+        switch (resultSetConcurrency) {
+        case java.sql.ResultSet.CONCUR_READ_ONLY:
+            rs = new com.mysql.jdbc.ResultSet(catalog, fields, rows,
+                    this.connection, callingStatement);
+
+            if (isBinaryEncoded) {
+                rs.setBinaryEncoded();
+            }
+
+            break;
+
+        case java.sql.ResultSet.CONCUR_UPDATABLE:
+            rs = new com.mysql.jdbc.UpdatableResultSet(catalog, fields, rows,
+                    this.connection, callingStatement);
+
+            break;
+
+        default:
+            return new com.mysql.jdbc.ResultSet(catalog, fields, rows,
+                this.connection, callingStatement);
+        }
+
+        rs.setResultSetType(resultSetType);
+        rs.setResultSetConcurrency(resultSetConcurrency);
+
+        return rs;
+    }
+
+    private com.mysql.jdbc.ResultSet buildResultSetWithUpdates(
+        Statement callingStatement, Buffer resultPacket)
+        throws SQLException {
+        long updateCount = -1;
+        long updateID = -1;
+        String info = null;
+
+        try {
+            if (this.useNewUpdateCounts) {
+                updateCount = resultPacket.newReadLength();
+                updateID = resultPacket.newReadLength();
+            } else {
+                updateCount = resultPacket.readLength();
+                updateID = resultPacket.readLength();
+            }
+
+            if (this.use41Extensions) {
+                this.serverStatus = resultPacket.readInt();
+
+                this.warningCount = resultPacket.readInt();
+
+                if (this.warningCount > 0) {
+                    this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
+                }
+
+                resultPacket.readByte(); // advance pointer
+
+                if (this.profileSql) {
+                    this.queryNoIndexUsed = (this.serverStatus &
+                        SERVER_QUERY_NO_GOOD_INDEX_USED) != 0;
+                    this.queryBadIndexUsed = (this.serverStatus &
+                        SERVER_QUERY_NO_INDEX_USED) != 0;
+                }
+            }
+
+            if (this.connection.isReadInfoMsgEnabled()) {
+                info = resultPacket.readString();
+            }
+        } catch (Exception ex) {
+            throw SQLError.createSQLException(SQLError.get(
+                    SQLError.SQL_STATE_GENERAL_ERROR) + ": " //$NON-NLS-1$
+                 +ex.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
+        }
+
+        ResultSet updateRs = new com.mysql.jdbc.ResultSet(updateCount,
+                updateID, this.connection, callingStatement);
+
+        if (info != null) {
+            updateRs.setServerInfo(info);
+        }
+
+        return updateRs;
+    }
+
+    private void checkForOutstandingStreamingData() throws SQLException {
+        if (this.streamingData != null) {
+            if (!this.connection.getClobberStreamingResults()) {
+                throw SQLError.createSQLException(Messages.getString("MysqlIO.39") //$NON-NLS-1$
+                     +this.streamingData +
+                    Messages.getString("MysqlIO.40") //$NON-NLS-1$
+                     +Messages.getString("MysqlIO.41") //$NON-NLS-1$
+                     +Messages.getString("MysqlIO.42")); //$NON-NLS-1$
+            }
+
+            // Close the result set
+            this.streamingData.getOwner().realClose(false);
+
+            // clear any pending data....
+            clearInputStream();
+        }
+    }
+
+    private Buffer compressPacket(Buffer packet, int offset, int packetLen,
+        int headerLength) throws SQLException {
+        packet.writeLongInt(packetLen - headerLength);
+        packet.writeByte((byte) 0); // wrapped packet has 0 packet seq.
+
+        int lengthToWrite = 0;
+        int compressedLength = 0;
+        byte[] bytesToCompress = packet.getByteBuffer();
+        byte[] compressedBytes = null;
+        int offsetWrite = 0;
+
+        if (packetLen < MIN_COMPRESS_LEN) {
+            lengthToWrite = packetLen;
+            compressedBytes = packet.getByteBuffer();
+            compressedLength = 0;
+            offsetWrite = offset;
+        } else {
+            compressedBytes = new byte[bytesToCompress.length * 2];
+
+            this.deflater.reset();
+            this.deflater.setInput(bytesToCompress, offset, packetLen);
+            this.deflater.finish();
+
+            int compLen = this.deflater.deflate(compressedBytes);
+
+            if (compLen > packetLen) {
+                lengthToWrite = packetLen;
+                compressedBytes = packet.getByteBuffer();
+                compressedLength = 0;
+                offsetWrite = offset;
+            } else {
+                lengthToWrite = compLen;
+                headerLength += COMP_HEADER_LENGTH;
+                compressedLength = packetLen;
+            }
+        }
+
+        Buffer compressedPacket = new Buffer(packetLen + headerLength);
+
+        compressedPacket.setPosition(0);
+        compressedPacket.writeLongInt(lengthToWrite);
+        compressedPacket.writeByte(this.packetSequence);
+        compressedPacket.writeLongInt(compressedLength);
+        compressedPacket.writeBytesNoNull(compressedBytes, offsetWrite,
+            lengthToWrite);
+
+        return compressedPacket;
+    }
+
+    private final void readServerStatusForResultSets(Buffer rowPacket)
+        throws SQLException {
+        if (this.use41Extensions) {
+            rowPacket.readByte(); // skips the 'last packet' flag
+
+            this.warningCount = rowPacket.readInt();
+
+            if (this.warningCount > 0) {
+                this.hadWarnings = true; // this is a 'latch', it's reset by sendCommand()
+            }
+
+            this.serverStatus = rowPacket.readInt();
+
+            if (this.profileSql) {
+                this.queryNoIndexUsed = (this.serverStatus &
+                    SERVER_QUERY_NO_GOOD_INDEX_USED) != 0;
+                this.queryBadIndexUsed = (this.serverStatus &
+                    SERVER_QUERY_NO_INDEX_USED) != 0;
+            }
+        }
+    }
+    
+    private SocketFactory createSocketFactory() throws SQLException {
+        try {
+            if (this.socketFactoryClassName == null) {
+                throw SQLError.createSQLException(Messages.getString("MysqlIO.75"), //$NON-NLS-1$
+                    SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
+            }
+
+            return (SocketFactory) (Class.forName(this.socketFactoryClassName)
+                                         .newInstance());
+        } catch (Exception ex) {
+            throw SQLError.createSQLException(Messages.getString("MysqlIO.76") //$NON-NLS-1$
+                 +this.socketFactoryClassName +
+                Messages.getString("MysqlIO.77") + ex.toString() //$NON-NLS-1$
+                 +(this.connection.getParanoid() ? "" //$NON-NLS-1$
+                                                 : Util.stackTraceToString(ex)),
+                SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
+        }
+    }
+
+    private void enqueuePacketForDebugging(boolean isPacketBeingSent,
+        boolean isPacketReused, int sendLength, byte[] header, Buffer packet)
+        throws SQLException {
+        if ((this.packetDebugRingBuffer.size() + 1) > this.connection.getPacketDebugBufferSize()) {
+            this.packetDebugRingBuffer.removeFirst();
+        }
+
+        StringBuffer packetDump = null;
+
+        if (!isPacketBeingSent) {
+            int bytesToDump = Math.min(MAX_PACKET_DUMP_LENGTH,
+                    packet.getBufLength());
+
+            Buffer packetToDump = new Buffer(4 + bytesToDump);
+
+            packetToDump.setPosition(0);
+            packetToDump.writeBytesNoNull(header);
+            packetToDump.writeBytesNoNull(packet.getBytes(0, bytesToDump));
+
+            String packetPayload = packetToDump.dump(bytesToDump);
+
+            packetDump = new StringBuffer(96 + packetPayload.length());
+
+            packetDump.append("Server ");
+
+            if (isPacketReused) {
+                packetDump.append("(re-used)");
+            } else {
+                packetDump.append("(new)");
+            }
+
+            packetDump.append(" ");
+            packetDump.append(packet.toSuperString());
+            packetDump.append(" --------------------> Client\n");
+            packetDump.append("\nPacket payload:\n\n");
+            packetDump.append(packetPayload);
+
+            if (bytesToDump == MAX_PACKET_DUMP_LENGTH) {
+                packetDump.append("\nNote: Packet of " + packet.getBufLength() +
+                    " bytes truncated to " + MAX_PACKET_DUMP_LENGTH +
+                    " bytes.\n");
+            }
+        } else {
+            int bytesToDump = Math.min(MAX_PACKET_DUMP_LENGTH, sendLength);
+
+            String packetPayload = packet.dump(bytesToDump);
+
+            packetDump = new StringBuffer(64 + 4 + packetPayload.length());
+
+            packetDump.append("Client ");
+            packetDump.append(packet.toSuperString());
+            packetDump.append("--------------------> Server\n");
+            packetDump.append("\nPacket payload:\n\n");
+            packetDump.append(packetPayload);
+
+            if (bytesToDump == MAX_PACKET_DUMP_LENGTH) {
+                packetDump.append("\nNote: Packet of " + sendLength +
+                    " bytes truncated to " + MAX_PACKET_DUMP_LENGTH +
+                    " bytes.\n");
+            }
+        }
+
+        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();
+
+        // Now read the data
+        Object rowBytes = nextRow(fields, (int) columnCount, isBinaryEncoded,
+                resultSetConcurrency);
+        
+        int rowCount = 0;
+
+        if (rowBytes != null) {
+            rows.add(rowBytes);
+            rowCount = 1;
+        }
+
+        while (rowBytes != null) {
+            rowBytes = nextRow(fields, (int) columnCount, isBinaryEncoded,
+                    resultSetConcurrency);
+
+            if (rowBytes != null) {
+            	if ((maxRows == -1) || (rowCount < maxRows)) {
+            		rows.add(rowBytes);
+            		rowCount++;
+            	}
+            }
+        }
+
+        rowData = new RowDataStatic(rows);
+
+        return rowData;
+    }
+
+    /**
+     * 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());
+        }
+    }
+
+    /**
+     * Re-use a packet to read from the MySQL server
+     *
+     * @param reuse DOCUMENT ME!
+     *
+     * @return DOCUMENT ME!
+     *
+     * @throws SQLException DOCUMENT ME!
+     * @throws SQLException DOCUMENT ME!
+     */
+    private final Buffer reuseAndReadPacket(Buffer reuse)
+        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 = (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);
+    			}
+    		} 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,
+    		// rather than getBufLength(), because getBufLength() is not
+    		// necesarily the actual length of the byte array
+    		// used as the buffer
+    		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);
+    		}
+    		
+    		if (!isMultiPacket) {
+    			reuse.getByteBuffer()[packetLength] = 0; // Null-termination
+    		}
+    		
+    		return reuse;
+    	} catch (IOException ioEx) {
+    		throw new CommunicationsException(this.connection,
+    				this.lastPacketSentTimeMs, ioEx);
+    	} catch (OutOfMemoryError oom) {
+    		try {
+    			// _Try_ this
+    			clearInputStream();
+    		} finally {
+    			try {
+    				this.connection.realClose(false, false, true, oom);
+    			} finally {
+    				throw oom;
+    			}
+    		}
+    	}
+    	
+    }
+
+    /**
+         * @param multiPacketSeq
+         * @throws CommunicationsException
+         */
+    private void checkPacketSequencing(byte multiPacketSeq)
+        throws CommunicationsException {
+        if ((multiPacketSeq == -128) && (this.readPacketSequence != 127)) {
+            throw new CommunicationsException(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,
+                this.lastPacketSentTimeMs,
+                new IOException("Packets out of order, expected packet # -1, but received packet # " +
+                    multiPacketSeq));
+        }
+
+        if ((multiPacketSeq != -128) && (this.readPacketSequence != -1) &&
+                (multiPacketSeq != (this.readPacketSequence + 1))) {
+            throw new CommunicationsException(this.connection,
+                this.lastPacketSentTimeMs,
+                new IOException("Packets out of order, expected packet # " +
+                    (this.readPacketSequence + 1) + ", but received packet # " +
+                    multiPacketSeq));
+        }
+    }
+
+    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 {
+            if (packetLen > this.maxAllowedPacket) {
+                throw new PacketTooBigException(packetLen, this.maxAllowedPacket);
+            }
+
+			if (this.connection.getMaintainTimeStats()) {
+				this.lastPacketSentTimeMs = System.currentTimeMillis();
+			}
+
+            if ((this.serverMajorVersion >= 4) &&
+                    (packetLen >= this.maxThreeBytes)) {
+                sendSplitPackets(packet);
+            } else {
+                this.packetSequence++;
+
+                Buffer packetToSend = packet;
+
+                packetToSend.setPosition(0);
+
+                if (this.useCompression) {
+                    int originalPacketLen = packetLen;
+
+                    packetToSend = compressPacket(packet, 0, packetLen,
+                            HEADER_LENGTH);
+                    packetLen = packetToSend.getPosition();
+
+                    if (this.traceProtocol) {
+                        StringBuffer traceMessageBuf = new StringBuffer();
+
+                        traceMessageBuf.append(Messages.getString("MysqlIO.57")); //$NON-NLS-1$
+                        traceMessageBuf.append(getPacketDumpToLog(
+                                packetToSend, packetLen));
+                        traceMessageBuf.append(Messages.getString("MysqlIO.58")); //$NON-NLS-1$
+                        traceMessageBuf.append(getPacketDumpToLog(packet,
+                                originalPacketLen));
+
+                        this.connection.getLog().logTrace(traceMessageBuf.toString());
+                    }
+                } else {
+                    packetToSend.writeLongInt(packetLen - HEADER_LENGTH);
+                    packetToSend.writeByte(this.packetSequence);
+
+                    if (this.traceProtocol) {
+                        StringBuffer traceMessageBuf = new StringBuffer();
+
+                        traceMessageBuf.append(Messages.getString("MysqlIO.59")); //$NON-NLS-1$
+                        traceMessageBuf.append(packetToSend.dump(packetLen));
+
+                        this.connection.getLog().logTrace(traceMessageBuf.toString());
+                    }
+                }
+
+                
+                this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
+                		packetLen);
+                this.mysqlOutput.flush();   
+            }
+
+            if (this.enablePacketDebug) {
+                enqueuePacketForDebugging(true, false, packetLen + 5,
+                    this.packetHeaderBuf, packet);
+            }
+
+            // 
+            // Don't hold on to large packets
+            //
+            if (packet == this.sharedSendPacket) {
+                reclaimLargeSharedSendPacket();
+            }
+        } catch (IOException ioEx) {
+            throw new CommunicationsException(this.connection,
+                this.lastPacketSentTimeMs, ioEx);
+        }
+    }
+
+    /**
+     * Reads and sends a file to the server for LOAD DATA LOCAL INFILE
+     *
+     * @param callingStatement DOCUMENT ME!
+     * @param fileName the file name to send.
+     *
+     * @return DOCUMENT ME!
+     *
+     * @throws SQLException DOCUMENT ME!
+     */
+    private final ResultSet sendFileToServer(Statement callingStatement,
+        String fileName) throws SQLException {
+    	
+        Buffer filePacket = (this.loadFileBufRef == null) ? null
+                                                          : (Buffer) (this.loadFileBufRef.get());
+
+        int bigPacketLength = Math.min(this.connection.getMaxAllowedPacket() -
+                (HEADER_LENGTH * 3),
+                alignPacketSize(this.connection.getMaxAllowedPacket() - 16, 4096) -
+                (HEADER_LENGTH * 3));
+        
+        int oneMeg = 1024 * 1024;
+        
+        int smallerPacketSizeAligned = Math.min(oneMeg - (HEADER_LENGTH * 3), 
+        		alignPacketSize(oneMeg - 16, 4096) - (HEADER_LENGTH * 3));
+        
+        int packetLength = Math.min(smallerPacketSizeAligned, bigPacketLength);
+
+        if (filePacket == null) {
+        	try {
+        		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." 
+						+ " Try increasing max heap allocation for JVM or decreasing server variable "
+						+ "'max_allowed_packet'", SQLError.SQL_STATE_MEMORY_ALLOCATION_FAILURE);
+				
+        	}
+        }
+
+        filePacket.clear();
+        send(filePacket, 0);
+
+        byte[] fileBuf = new byte[packetLength];
+
+        BufferedInputStream fileIn = null;
+
+        try {
+            if (!this.connection.getAllowUrlInLocalInfile()) {
+                fileIn = new BufferedInputStream(new FileInputStream(fileName));
+            } else {
+                // First look for ':'
+                if (fileName.indexOf(":") != -1) {
+                    try {
+                        URL urlFromFileName = new URL(fileName);
+                        fileIn = new BufferedInputStream(urlFromFileName.openStream());
+                    } catch (MalformedURLException badUrlEx) {
+                        // we fall back to trying this as a file input stream
+                        fileIn = new BufferedInputStream(new FileInputStream(
+                                    fileName));
+                    }
+                } else {
+                    fileIn = new BufferedInputStream(new FileInputStream(
+                                fileName));
+                }
+            }
+
+            int bytesRead = 0;
+
+            while ((bytesRead = fileIn.read(fileBuf)) != -1) {
+                filePacket.clear();
+                filePacket.writeBytesNoNull(fileBuf, 0, bytesRead);
+                send(filePacket, filePacket.getPosition());
+            }
+        } catch (IOException ioEx) {
+            StringBuffer messageBuf = new StringBuffer(Messages.getString(
+                        "MysqlIO.60")); //$NON-NLS-1$
+
+            if (!this.connection.getParanoid()) {
+                messageBuf.append("'"); //$NON-NLS-1$
+
+                if (fileName != null) {
+                    messageBuf.append(fileName);
+                }
+
+                messageBuf.append("'"); //$NON-NLS-1$
+            }
+
+            messageBuf.append(Messages.getString("MysqlIO.63")); //$NON-NLS-1$
+
+            if (!this.connection.getParanoid()) {
+                messageBuf.append(Messages.getString("MysqlIO.64")); //$NON-NLS-1$
+                messageBuf.append(Util.stackTraceToString(ioEx));
+            }
+
+            throw SQLError.createSQLException(messageBuf.toString(),
+                SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+        } finally {
+            if (fileIn != null) {
+                try {
+                    fileIn.close();
+                } catch (Exception ex) {
+                    throw SQLError.createSQLException(Messages.getString("MysqlIO.65"), //$NON-NLS-1$
+                        SQLError.SQL_STATE_GENERAL_ERROR);
+                }
+
+                fileIn = null;
+            } else {
+                // file open failed, but server needs one packet
+                filePacket.clear();
+                send(filePacket, filePacket.getPosition());
+                checkErrorPacket(); // to clear response off of queue
+            }
+        }
+
+        // send empty packet to mark EOF
+        filePacket.clear();
+        send(filePacket, filePacket.getPosition());
+
+        Buffer resultPacket = checkErrorPacket();
+
+        return buildResultSetWithUpdates(callingStatement, resultPacket);
+    }
+
+    /**
+     * Checks for errors in the reply packet, and if none, returns the reply
+     * packet, ready for reading
+     *
+     * @param command the command being issued (if used)
+     *
+     * @return DOCUMENT ME!
+     *
+     * @throws SQLException if an error packet was received
+     * @throws CommunicationsException DOCUMENT ME!
+     */
+    private Buffer checkErrorPacket(int command) throws SQLException {
+        int statusCode = 0;
+        Buffer resultPacket = null;
+        this.serverStatus = 0;
+
+        try {
+            // Check return value, if we get a java.io.EOFException,
+            // the server has gone away. We'll pass it on up the
+            // 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,
+                this.lastPacketSentTimeMs, fallThru);
+        }
+
+        // Error handling
+        if (statusCode == (byte) 0xff) {
+            String serverErrorMessage;
+            int errno = 2000;
+
+            if (this.protocolVersion > 9) {
+                errno = resultPacket.readInt();
+
+                String xOpen = null;
+
+                serverErrorMessage = 
+                	resultPacket.readString(this.connection.getErrorMessageEncoding());
+
+                if (serverErrorMessage.startsWith("#")) { //$NON-NLS-1$
+
+                    // we have an SQLState
+                    if (serverErrorMessage.length() > 6) {
+                        xOpen = serverErrorMessage.substring(1, 6);
+                        serverErrorMessage = serverErrorMessage.substring(6);
+
+                        if (xOpen.equals("HY000")) { //$NON-NLS-1$
+                            xOpen = SQLError.mysqlToSqlState(errno,
+                                    this.connection.getUseSqlStateCodes());
+                        }
+                    } else {
+                        xOpen = SQLError.mysqlToSqlState(errno,
+                                this.connection.getUseSqlStateCodes());
+                    }
+                } else {
+                    xOpen = SQLError.mysqlToSqlState(errno,
+                            this.connection.getUseSqlStateCodes());
+                }
+
+                clearInputStream();
+
+                StringBuffer errorBuf = new StringBuffer();
+
+                String xOpenErrorMessage = SQLError.get(xOpen);
+
+                if (!this.connection.getUseOnlyServerErrorMessages()) {
+                    if (xOpenErrorMessage != null) {
+                        errorBuf.append(xOpenErrorMessage);
+                        errorBuf.append(Messages.getString("MysqlIO.68")); //$NON-NLS-1$
+                    }
+                }
+
+                errorBuf.append(serverErrorMessage);
+
+                if (!this.connection.getUseOnlyServerErrorMessages()) {
+                    if (xOpenErrorMessage != null) {
+                        errorBuf.append("\""); //$NON-NLS-1$
+                    }
+                }
+                
+                if (xOpen != null && xOpen.startsWith("22")) {
+                	throw new MysqlDataTruncation(errorBuf.toString(), 0, true, false, 0, 0); 
+                } else {
+                	throw SQLError.createSQLException(errorBuf.toString(), xOpen, errno);
+                }
+            }
+
+            serverErrorMessage = resultPacket.readString(
+            		this.connection.getErrorMessageEncoding());
+            clearInputStream();
+
+            if (serverErrorMessage.indexOf(Messages.getString("MysqlIO.70")) != -1) { //$NON-NLS-1$
+                throw SQLError.createSQLException(SQLError.get(
+                        SQLError.SQL_STATE_COLUMN_NOT_FOUND) +
+                    ", " //$NON-NLS-1$
+                     +serverErrorMessage, SQLError.SQL_STATE_COLUMN_NOT_FOUND,
+                    -1);
+            }
+
+            StringBuffer errorBuf = new StringBuffer(Messages.getString(
+                        "MysqlIO.72")); //$NON-NLS-1$
+            errorBuf.append(serverErrorMessage);
+            errorBuf.append("\""); //$NON-NLS-1$
+
+            throw SQLError.createSQLException(SQLError.get(
+                    SQLError.SQL_STATE_GENERAL_ERROR) + ", " //$NON-NLS-1$
+                 +errorBuf.toString(), SQLError.SQL_STATE_GENERAL_ERROR, -1);
+        }
+
+        return resultPacket;
+    }
+
+    /**
+     * Sends a large packet to the server as a series of smaller packets
+     *
+     * @param packet DOCUMENT ME!
+     *
+     * @throws SQLException DOCUMENT ME!
+     * @throws CommunicationsException DOCUMENT ME!
+     */
+    private final void sendSplitPackets(Buffer packet)
+        throws SQLException {
+        try {
+            //
+            // Big packets are handled by splitting them in packets of MAX_THREE_BYTES
+            // length. The last packet is always a packet that is < MAX_THREE_BYTES.
+            // (The last packet may even have a length of 0)
+            //
+            //
+            // NB: Guarded by execSQL. If the driver changes architecture, this
+            // will need to be synchronized in some other way
+            //
+            Buffer headerPacket = (this.splitBufRef == null) ? null
+                                                             : (Buffer) (this.splitBufRef.get());
+
+            //
+            // Store this packet in a soft reference...It can be re-used if not GC'd (so clients
+            // that use it frequently won't have to re-alloc the 16M buffer), but we don't
+            // penalize infrequent users of large packets by keeping 16M allocated all of the time
+            //
+            if (headerPacket == null) {
+                headerPacket = new Buffer((this.maxThreeBytes +
+                        HEADER_LENGTH));
+                this.splitBufRef = new SoftReference(headerPacket);
+            }
+
+            int len = packet.getPosition();
+            int splitSize = this.maxThreeBytes;
+            int originalPacketPos = HEADER_LENGTH;
+            byte[] origPacketBytes = packet.getByteBuffer();
+            byte[] headerPacketBytes = headerPacket.getByteBuffer();
+
+            while (len >= this.maxThreeBytes) {
+                this.packetSequence++;
+
+                headerPacket.setPosition(0);
+                headerPacket.writeLongInt(splitSize);
+
+                headerPacket.writeByte(this.packetSequence);
+                System.arraycopy(origPacketBytes, originalPacketPos,
+                    headerPacketBytes, 4, splitSize);
+
+                int packetLen = splitSize + HEADER_LENGTH;
+
+                //
+                // Swap a compressed packet in, if we're using
+                // compression...
+                //
+                if (!this.useCompression) {
+                    this.mysqlOutput.write(headerPacketBytes, 0,
+                        splitSize + HEADER_LENGTH);
+                    this.mysqlOutput.flush();
+                } else {
+                    Buffer packetToSend;
+
+                    headerPacket.setPosition(0);
+                    packetToSend = compressPacket(headerPacket, HEADER_LENGTH,
+                            splitSize, HEADER_LENGTH);
+                    packetLen = packetToSend.getPosition();
+
+                    this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
+                        packetLen);
+                    this.mysqlOutput.flush();
+                }
+
+                originalPacketPos += splitSize;
+                len -= splitSize;
+            }
+
+            //
+            // Write last packet
+            //
+            headerPacket.clear();
+            headerPacket.setPosition(0);
+            headerPacket.writeLongInt(len - HEADER_LENGTH);
+            this.packetSequence++;
+            headerPacket.writeByte(this.packetSequence);
+
+            if (len != 0) {
+                System.arraycopy(origPacketBytes, originalPacketPos,
+                    headerPacketBytes, 4, len - HEADER_LENGTH);
+            }
+
+            int packetLen = len - HEADER_LENGTH;
+
+            //
+            // Swap a compressed packet in, if we're using
+            // compression...
+            //
+            if (!this.useCompression) {
+                this.mysqlOutput.write(headerPacket.getByteBuffer(), 0, len);
+                this.mysqlOutput.flush();
+            } else {
+                Buffer packetToSend;
+
+                headerPacket.setPosition(0);
+                packetToSend = compressPacket(headerPacket, HEADER_LENGTH,
+                        packetLen, HEADER_LENGTH);
+                packetLen = packetToSend.getPosition();
+
+                this.mysqlOutput.write(packetToSend.getByteBuffer(), 0,
+                    packetLen);
+                this.mysqlOutput.flush();
+            }
+        } catch (IOException ioEx) {
+            throw new CommunicationsException(this.connection,
+                this.lastPacketSentTimeMs, ioEx);
+        }
+    }
+
+    private void reclaimLargeSharedSendPacket() {
+        if ((this.sharedSendPacket != null) &&
+                (this.sharedSendPacket.getCapacity() > 1048576)) {
+            this.sharedSendPacket = new Buffer(this.connection.getNetBufferLength());
+        }
+    }
+
+    boolean hadWarnings() {
+    	return this.hadWarnings;
+    }
+    
+    void scanForAndThrowDataTruncation() throws SQLException {
+        if ((this.streamingData == null) && versionMeetsMinimum(4, 1, 0) &&
+                this.connection.getJdbcCompliantTruncation()) {
+            SQLError.convertShowWarningsToSQLWarnings(this.connection,
+                this.warningCount, true);
+        }
+    }
+
+    /**
+     * Secure authentication for 4.1 and newer servers.
+     *
+     * @param packet DOCUMENT ME!
+     * @param packLength
+     * @param user
+     * @param password
+     * @param database DOCUMENT ME!
+     * @param writeClientParams
+     *
+     * @throws SQLException
+     */
+    private void secureAuth(Buffer packet, int packLength, String user,
+        String password, String database, boolean writeClientParams)
+        throws SQLException {
+        // Passwords can be 16 chars long
+        if (packet == null) {
+            packet = new Buffer(packLength);
+        }
+
+        if (writeClientParams) {
+            if (this.use41Extensions) {
+                if (versionMeetsMinimum(4, 1, 1)) {
+                    packet.writeLong(this.clientParam);
+                    packet.writeLong(this.maxThreeBytes);
+
+                    // charset, JDBC will connect as 'latin1',
+                    // and use 'SET NAMES' to change to the desired
+                    // charset after the connection is established.
+                    packet.writeByte((byte) 8);
+
+                    // Set of bytes reserved for future use.
+                    packet.writeBytesNoNull(new byte[23]);
+                } else {
+                    packet.writeLong(this.clientParam);
+                    packet.writeLong(this.maxThreeBytes);
+                }
+            } else {
+                packet.writeInt((int) this.clientParam);
+                packet.writeLongInt(this.maxThreeBytes);
+            }
+        }
+
+        // User/Password data
+        packet.writeString(user, "Cp1252", this.connection);
+
+        if (password.length() != 0) {
+            /* Prepare false scramble  */
+            packet.writeString(FALSE_SCRAMBLE, "Cp1252", this.connection);
+        } else {
+            /* For empty password*/
+            packet.writeString("", "Cp1252", this.connection); //$NON-NLS-1$
+        }
+
+        if (this.useConnectWithDb) {
+            packet.writeString(database, "Cp1252", this.connection);
+        }
+
+        send(packet, packet.getPosition());
+
+        //
+        // Don't continue stages if password is empty
+        //
+        if (password.length() > 0) {
+            Buffer b = readPacket();
+
+            b.setPosition(0);
+
+            byte[] replyAsBytes = b.getByteBuffer();
+
+            if ((replyAsBytes.length == 25) && (replyAsBytes[0] != 0)) {
+                // Old passwords will have '*' at the first byte of hash */
+                if (replyAsBytes[0] != '*') {
+                    try {
+                        /* Build full password hash as it is required to decode scramble */
+                        byte[] buff = Security.passwordHashStage1(password);
+
+                        /* Store copy as we'll need it later */
+                        byte[] passwordHash = new byte[buff.length];
+                        System.arraycopy(buff, 0, passwordHash, 0, buff.length);
+
+                        /* Finally hash complete password using hash we got from server */
+                        passwordHash = Security.passwordHashStage2(passwordHash,
+                                replyAsBytes);
+
+                        byte[] packetDataAfterSalt = new byte[replyAsBytes.length -
+                            5];
+
+                        System.arraycopy(replyAsBytes, 4, packetDataAfterSalt,
+                            0, replyAsBytes.length - 5);
+
+                        byte[] mysqlScrambleBuff = new byte[20];
+
+                        /* Decypt and store scramble 4 = hash for stage2 */
+                        Security.passwordCrypt(packetDataAfterSalt,
+                            mysqlScrambleBuff, passwordHash, 20);
+
+                        /* Encode scramble with password. Recycle buffer */
+                        Security.passwordCrypt(mysqlScrambleBuff, buff, buff, 20);
+
+                        Buffer packet2 = new Buffer(25);
+                        packet2.writeBytesNoNull(buff);
+
+                        this.packetSequence++;
+
+                        send(packet2, 24);
+                    } catch (NoSuchAlgorithmException nse) {
+                        throw SQLError.createSQLException(Messages.getString("MysqlIO.91") //$NON-NLS-1$
+                             +Messages.getString("MysqlIO.92"), //$NON-NLS-1$
+                            SQLError.SQL_STATE_GENERAL_ERROR);
+                    }
+                } else {
+                    try {
+                        /* Create password to decode scramble */
+                        byte[] passwordHash = Security.createKeyFromOldPassword(password);
+
+                        /* Decypt and store scramble 4 = hash for stage2 */
+                        byte[] netReadPos4 = new byte[replyAsBytes.length - 5];
+
+                        System.arraycopy(replyAsBytes, 4, netReadPos4, 0,
+                            replyAsBytes.length - 5);
+
+                        byte[] mysqlScrambleBuff = new byte[20];
+
+                        /* Decypt and store scramble 4 = hash for stage2 */
+                        Security.passwordCrypt(netReadPos4, mysqlScrambleBuff,
+                            passwordHash, 20);
+
+                        /* Finally scramble decoded scramble with password */
+                        String scrambledPassword = Util.scramble(new String(
+                                    mysqlScrambleBuff), password);
+
+                        Buffer packet2 = new Buffer(packLength);
+                        packet2.writeString(scrambledPassword, "Cp1252", this.connection);
+                        this.packetSequence++;
+
+                        send(packet2, 24);
+                    } catch (NoSuchAlgorithmException nse) {
+                        throw SQLError.createSQLException(Messages.getString("MysqlIO.93") //$NON-NLS-1$
+                             +Messages.getString("MysqlIO.94"), //$NON-NLS-1$
+                            SQLError.SQL_STATE_GENERAL_ERROR);
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Secure authentication for 4.1.1 and newer servers.
+     *
+     * @param packet DOCUMENT ME!
+     * @param packLength
+     * @param user
+     * @param password
+     * @param database DOCUMENT ME!
+     * @param writeClientParams
+     *
+     * @throws SQLException
+     */
+    void secureAuth411(Buffer packet, int packLength, String user,
+        String password, String database, boolean writeClientParams)
+        throws SQLException {
+        //	SERVER:  public_seed=create_random_string()
+        //			 send(public_seed)
+        //
+        //	CLIENT:  recv(public_seed)
+        //			 hash_stage1=sha1("password")
+        //			 hash_stage2=sha1(hash_stage1)
+        //			 reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
+        //
+        //			 // this three steps are done in scramble()
+        //
+        //			 send(reply)
+        //
+        //
+        //	SERVER:  recv(reply)
+        //			 hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
+        //			 candidate_hash2=sha1(hash_stage1)
+        //			 check(candidate_hash2==hash_stage2)
+        // Passwords can be 16 chars long
+        if (packet == null) {
+            packet = new Buffer(packLength);
+        }
+
+        if (writeClientParams) {
+            if (this.use41Extensions) {
+                if (versionMeetsMinimum(4, 1, 1)) {
+                    packet.writeLong(this.clientParam);
+                    packet.writeLong(this.maxThreeBytes);
+
+                    // charset, JDBC will connect as 'latin1',
+                    // and use 'SET NAMES' to change to the desired
+                    // charset after the connection is established.
+                    packet.writeByte((byte) 8);
+
+                    // Set of bytes reserved for future use.
+                    packet.writeBytesNoNull(new byte[23]);
+                } else {
+                    packet.writeLong(this.clientParam);
+                    packet.writeLong(this.maxThreeBytes);
+                }
+            } else {
+                packet.writeInt((int) this.clientParam);
+                packet.writeLongInt(this.maxThreeBytes);
+            }
+        }
+
+        // User/Password data
+        packet.writeString(user);
+
+        if (password.length() != 0) {
+            packet.writeByte((byte) 0x14);
+
+            try {
+                packet.writeBytesNoNull(Security.scramble411(password, this.seed));
+            } catch (NoSuchAlgorithmException nse) {
+                throw SQLError.createSQLException(Messages.getString("MysqlIO.95") //$NON-NLS-1$
+                     +Messages.getString("MysqlIO.96"), //$NON-NLS-1$
+                    SQLError.SQL_STATE_GENERAL_ERROR);
+            }
+        } else {
+            /* For empty password*/
+            packet.writeByte((byte) 0);
+        }
+
+        if (this.useConnectWithDb) {
+            packet.writeString(database);
+        }
+
+        send(packet, packet.getPosition());
+
+        byte savePacketSequence = this.packetSequence++;
+
+        Buffer reply = checkErrorPacket();
+
+        if (reply.isLastDataPacket()) {
+            /*
+                  By sending this very specific reply server asks us to send scrambled
+                  password in old format. The reply contains scramble_323.
+            */
+            this.packetSequence = ++savePacketSequence;
+            packet.clear();
+
+            String seed323 = this.seed.substring(0, 8);
+            packet.writeString(Util.newCrypt(password, seed323));
+            send(packet, packet.getPosition());
+
+            /* Read what server thinks about out new auth message report */
+            checkErrorPacket();
+        }
+    }
+
+    /**
+     * Un-packs binary-encoded result set data for one row
+     *
+     * @param fields
+     * @param binaryData
+     * @param resultSetConcurrency DOCUMENT ME!
+     *
+     * @return byte[][]
+     *
+     * @throws SQLException DOCUMENT ME!
+     */
+    private final Object[] unpackBinaryResultSetRow(Field[] fields,
+        Buffer binaryData, int resultSetConcurrency) throws SQLException {
+        int numFields = fields.length;
+
+        Object[] unpackedRowData = new Object[numFields];
+
+        //
+        // Unpack the null bitmask, first
+        //
+
+        /* Reserve place for null-marker bytes */
+        int nullCount = (numFields + 9) / 8;
+
+        byte[] nullBitMask = new byte[nullCount];
+
+        for (int i = 0; i < nullCount; i++) {
+            nullBitMask[i] = binaryData.readByte();
+        }
+
+        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, 
+            				unpackedRowData);
+            	} else {
+            		unpackNativeEncodedColumn(binaryData, fields, i, 
+            				unpackedRowData);
+            	}   
+            }
+            
+        	if (((bit <<= 1) & 255) == 0) {
+        		bit = 1; /* To next byte */
+
+        		nullMaskPos++;
+        	}
+        }
+
+        return unpackedRowData;
+    }
+
+        
+    private final void extractNativeEncodedColumn(Buffer binaryData, 
+    		Field[] fields, int columnIndex, Object[] 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:
+
+    		unpackedRowData[columnIndex] = binaryData.getBytes(8);
+    		break;
+    	case MysqlDefs.FIELD_TYPE_FLOAT:
+    		
+    		unpackedRowData[columnIndex] = binaryData.getBytes(4);
+    		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:
+    	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:
+    		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$
+    				+curField.getMysqlType() +
+					Messages.getString("MysqlIO.98") + columnIndex +
+					Messages.getString("MysqlIO.99") //$NON-NLS-1$ //$NON-NLS-2$
+					+ fields.length + Messages.getString("MysqlIO.100"), //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+    	}
+    }
+
+    private final void unpackNativeEncodedColumn(Buffer binaryData, 
+    		Field[] fields, int columnIndex, Object[] 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()) {			
+    			unpackedRowData[columnIndex] = String.valueOf(tinyVal)
+    			.getBytes();  			
+    		} else {
+    			short unsignedTinyVal = (short) (tinyVal & 0xff);
+ 
+    			unpackedRowData[columnIndex] = String.valueOf(unsignedTinyVal)
+    			.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();	
+    		} else {
+    			int unsignedShortVal = shortVal & 0xffff;
+
+    			unpackedRowData[columnIndex] = String.valueOf(unsignedShortVal)
+    			.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();
+    		} else {
+    			long longVal = intVal & 0xffffffffL;
+
+    			unpackedRowData[columnIndex] = String.valueOf(longVal)
+    			.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);
+
+    			unpackedRowData[columnIndex] = asBigInteger.toString()
+    			.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(
+    					this.connection.getZeroDateTimeBehavior())) {
+    				unpackedRowData[columnIndex] = null;
+    				
+    				break;
+    			} else if (ConnectionProperties.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(
+    					this.connection.getZeroDateTimeBehavior())) {
+    				unpackedRowData[columnIndex] = null;
+    				
+    				break;
+    			} else if (ConnectionProperties.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:
+    	case MysqlDefs.FIELD_TYPE_BLOB:
+    	case MysqlDefs.FIELD_TYPE_VAR_STRING:
+    	case MysqlDefs.FIELD_TYPE_STRING:
+    	case MysqlDefs.FIELD_TYPE_VARCHAR:
+    	case MysqlDefs.FIELD_TYPE_DECIMAL:
+    	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() +
+    				Messages.getString("MysqlIO.98") + columnIndex +
+    				Messages.getString("MysqlIO.99") //$NON-NLS-1$ //$NON-NLS-2$
+    				+ fields.length + Messages.getString("MysqlIO.100"), //$NON-NLS-1$
+    				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.
+     *
+     * @param user
+     * @param password
+     * @param database
+     * @param packLength
+     * @throws SQLException
+     * @throws CommunicationsException
+     */
+    private void negotiateSSLConnection(String user, String password,
+        String database, int packLength)
+        throws SQLException, CommunicationsException {
+        if (!ExportControlled.enabled()) {
+            throw new ConnectionFeatureNotAvailableException(this.connection,
+                this.lastPacketSentTimeMs, null);
+        }
+
+        boolean doSecureAuth = false;
+
+        if ((this.serverCapabilities & CLIENT_SECURE_CONNECTION) != 0) {
+            this.clientParam |= CLIENT_SECURE_CONNECTION;
+            doSecureAuth = true;
+        }
+
+        this.clientParam |= CLIENT_SSL;
+
+        Buffer packet = new Buffer(packLength);
+
+        if ((this.clientParam & CLIENT_RESERVED) != 0) {
+            packet.writeLong(this.clientParam);
+        } else {
+            packet.writeInt((int) this.clientParam);
+        }
+
+        send(packet, packet.getPosition());
+
+        ExportControlled.transformSocketToSSLSocket(this);
+
+        packet.clear();
+
+        if (doSecureAuth) {
+            if (versionMeetsMinimum(4, 1, 1)) {
+                secureAuth411(null, packLength, user, password, database, true);
+            } else {
+                secureAuth411(null, packLength, user, password, database, true);
+            }
+        } else {
+            if ((this.clientParam & CLIENT_RESERVED) != 0) {
+                packet.writeLong(this.clientParam);
+                packet.writeLong(this.maxThreeBytes);
+            } else {
+                packet.writeInt((int) this.clientParam);
+                packet.writeLongInt(this.maxThreeBytes);
+            }
+
+            // User/Password data
+            packet.writeString(user);
+
+            if (this.protocolVersion > 9) {
+                packet.writeString(Util.newCrypt(password, this.seed));
+            } else {
+                packet.writeString(Util.oldCrypt(password, this.seed));
+            }
+
+            if (((this.serverCapabilities & CLIENT_CONNECT_WITH_DB) != 0) &&
+                    (database != null) && (database.length() > 0)) {
+                packet.writeString(database);
+            }
+
+            send(packet, packet.getPosition());
+        }
+    }
+
+	protected int getServerStatus() {
+		return serverStatus;
+	}
+
+	protected List fetchRowsViaCursor(List fetchedRows, long statementId,
+			Field[] columnTypes, int fetchSize) 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;
+	
+		while ((row = nextRow(columnTypes, columnTypes.length, true,
+				ResultSet.CONCUR_READ_ONLY)) != null) {
+			fetchedRows.add(row);
+		}
+	
+		return fetchedRows;
+	}
+
+	protected long getThreadId() {
+		return threadId;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlParameterMetadata.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlParameterMetadata.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlParameterMetadata.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,98 @@
+/*
+   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;
+
+import java.sql.ParameterMetaData;
+import java.sql.SQLException;
+
+public class MysqlParameterMetadata implements ParameterMetaData {
+	
+	ResultSetMetaData metadata = null;
+	int parameterCount = 0;
+	
+	
+	MysqlParameterMetadata(Field[] fieldInfo, int parameterCount) {
+		this.metadata = new ResultSetMetaData(fieldInfo, false);
+		
+		this.parameterCount = parameterCount;
+	}
+	
+	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) {
+			throw SQLError.createSQLException(
+				"Parameter metadata not available for the given statement",
+				SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+		}
+	}
+
+	public boolean isSigned(int arg0) throws SQLException {
+		checkAvailable();
+		
+		return (this.metadata.isSigned(arg0));
+	}
+
+	public int getPrecision(int arg0) throws SQLException {
+		checkAvailable();
+		
+		return (this.metadata.getPrecision(arg0));
+	}
+
+	public int getScale(int arg0) throws SQLException {
+		checkAvailable();
+		
+		return (this.metadata.getScale(arg0));
+	}
+
+	public int getParameterType(int arg0) throws SQLException {
+		checkAvailable();
+		
+		return (this.metadata.getColumnType(arg0));
+	}
+
+	public String getParameterTypeName(int arg0) throws SQLException {
+		checkAvailable();
+		
+		return (this.metadata.getColumnTypeName(arg0));
+	}
+
+	public String getParameterClassName(int arg0) throws SQLException {
+		checkAvailable();
+		
+		return (this.metadata.getColumnClassName(arg0));
+	}
+
+	public int getParameterMode(int arg0) throws SQLException {
+		return parameterModeIn;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlSavepoint.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlSavepoint.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/MysqlSavepoint.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,106 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.rmi.server.UID;
+import java.sql.SQLException;
+import java.sql.Savepoint;
+
+/**
+ * Represents SQL SAVEPOINTS in MySQL.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: MysqlSavepoint.java 4489 2005-11-01 00:43:01Z mmatthews $
+ */
+public class MysqlSavepoint implements Savepoint {
+	private static String getUniqueId() {
+		// no need to re-invent the wheel here...
+		String uidStr = new UID().toString();
+
+		int uidLength = uidStr.length();
+
+		StringBuffer safeString = new StringBuffer(uidLength);
+
+		for (int i = 0; i < uidLength; i++) {
+			char c = uidStr.charAt(i);
+
+			if (Character.isLetter(c) || Character.isDigit(c)) {
+				safeString.append(c);
+			} else {
+				safeString.append('_');
+			}
+		}
+
+		return safeString.toString();
+	}
+
+	private String savepointName;
+
+	/**
+	 * Creates an unnamed savepoint.
+	 * 
+	 * @param conn
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	MysqlSavepoint() throws SQLException {
+		this(getUniqueId());
+	}
+
+	/**
+	 * Creates a named savepoint
+	 * 
+	 * @param name
+	 *            the name of the savepoint.
+	 * 
+	 * @throws SQLException
+	 *             if name == null or is empty.
+	 */
+	MysqlSavepoint(String name) throws SQLException {
+		if (name == null || name.length() == 0) {
+			throw SQLError.createSQLException("Savepoint name can not be NULL or empty",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		this.savepointName = name;
+	}
+
+	/**
+	 * @see java.sql.Savepoint#getSavepointId()
+	 */
+	public int getSavepointId() throws SQLException {
+		throw SQLError.createSQLException("Only named savepoints are supported.",
+				SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+	}
+
+	/**
+	 * @see java.sql.Savepoint#getSavepointName()
+	 */
+	public String getSavepointName() throws SQLException {
+		return this.savepointName;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NamedPipeSocketFactory.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NamedPipeSocketFactory.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NamedPipeSocketFactory.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,219 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+
+import java.net.Socket;
+import java.net.SocketException;
+
+import java.util.Properties;
+
+/**
+ * A socket factory for named pipes (on Windows)
+ * 
+ * @author Mark Matthews
+ */
+public class NamedPipeSocketFactory implements SocketFactory {
+	/**
+	 * A socket that encapsulates named pipes on Windows
+	 */
+	class NamedPipeSocket extends Socket {
+		private boolean isClosed = false;
+
+		private RandomAccessFile namedPipeFile;
+
+		NamedPipeSocket(String filePath) throws IOException {
+			if ((filePath == null) || (filePath.length() == 0)) {
+				throw new IOException(Messages
+						.getString("NamedPipeSocketFactory.4")); //$NON-NLS-1$
+			}
+
+			this.namedPipeFile = new RandomAccessFile(filePath, "rw"); //$NON-NLS-1$
+		}
+
+		/**
+		 * @see java.net.Socket#close()
+		 */
+		public synchronized void close() throws IOException {
+			this.namedPipeFile.close();
+			this.isClosed = true;
+		}
+
+		/**
+		 * @see java.net.Socket#getInputStream()
+		 */
+		public InputStream getInputStream() throws IOException {
+			return new RandomAccessFileInputStream(this.namedPipeFile);
+		}
+
+		/**
+		 * @see java.net.Socket#getOutputStream()
+		 */
+		public OutputStream getOutputStream() throws IOException {
+			return new RandomAccessFileOutputStream(this.namedPipeFile);
+		}
+
+		/**
+		 * @see java.net.Socket#isClosed()
+		 */
+		public boolean isClosed() {
+			return this.isClosed;
+		}
+	}
+
+	/**
+	 * Enables OutputStream-type functionality for a RandomAccessFile
+	 */
+	class RandomAccessFileInputStream extends InputStream {
+		RandomAccessFile raFile;
+
+		RandomAccessFileInputStream(RandomAccessFile file) {
+			this.raFile = file;
+		}
+
+		/**
+		 * @see java.io.InputStream#available()
+		 */
+		public int available() throws IOException {
+			return -1;
+		}
+
+		/**
+		 * @see java.io.InputStream#close()
+		 */
+		public void close() throws IOException {
+			this.raFile.close();
+		}
+
+		/**
+		 * @see java.io.InputStream#read()
+		 */
+		public int read() throws IOException {
+			return this.raFile.read();
+		}
+
+		/**
+		 * @see java.io.InputStream#read(byte[])
+		 */
+		public int read(byte[] b) throws IOException {
+			return this.raFile.read(b);
+		}
+
+		/**
+		 * @see java.io.InputStream#read(byte[], int, int)
+		 */
+		public int read(byte[] b, int off, int len) throws IOException {
+			return this.raFile.read(b, off, len);
+		}
+	}
+
+	/**
+	 * Enables OutputStream-type functionality for a RandomAccessFile
+	 */
+	class RandomAccessFileOutputStream extends OutputStream {
+		RandomAccessFile raFile;
+
+		RandomAccessFileOutputStream(RandomAccessFile file) {
+			this.raFile = file;
+		}
+
+		/**
+		 * @see java.io.OutputStream#close()
+		 */
+		public void close() throws IOException {
+			this.raFile.close();
+		}
+
+		/**
+		 * @see java.io.OutputStream#write(byte[])
+		 */
+		public void write(byte[] b) throws IOException {
+			this.raFile.write(b);
+		}
+
+		/**
+		 * @see java.io.OutputStream#write(byte[], int, int)
+		 */
+		public void write(byte[] b, int off, int len) throws IOException {
+			this.raFile.write(b, off, len);
+		}
+
+		/**
+		 * @see java.io.OutputStream#write(int)
+		 */
+		public void write(int b) throws IOException {
+		}
+	}
+
+	private static final String NAMED_PIPE_PROP_NAME = "namedPipePath"; //$NON-NLS-1$
+
+	private Socket namedPipeSocket;
+
+	/**
+	 * Constructor for NamedPipeSocketFactory.
+	 */
+	public NamedPipeSocketFactory() {
+		super();
+	}
+
+	/**
+	 * @see com.mysql.jdbc.SocketFactory#afterHandshake()
+	 */
+	public Socket afterHandshake() throws SocketException, IOException {
+		return this.namedPipeSocket;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.SocketFactory#beforeHandshake()
+	 */
+	public Socket beforeHandshake() throws SocketException, IOException {
+		return this.namedPipeSocket;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.SocketFactory#connect(String, Properties)
+	 */
+	public Socket connect(String host, int portNumber /* ignored */,
+			Properties props) throws SocketException, IOException {
+		String namedPipePath = props.getProperty(NAMED_PIPE_PROP_NAME);
+
+		if (namedPipePath == null) {
+			namedPipePath = "\\\\.\\pipe\\MySQL"; //$NON-NLS-1$
+		} else if (namedPipePath.length() == 0) {
+			throw new SocketException(Messages
+					.getString("NamedPipeSocketFactory.2") //$NON-NLS-1$
+					+ NAMED_PIPE_PROP_NAME
+					+ Messages.getString("NamedPipeSocketFactory.3")); //$NON-NLS-1$
+		}
+
+		this.namedPipeSocket = new NamedPipeSocket(namedPipePath);
+
+		return this.namedPipeSocket;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NonRegisteringDriver.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NonRegisteringDriver.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NonRegisteringDriver.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,653 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.sql.DriverPropertyInfo;
+import java.sql.SQLException;
+
+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
+ * 
+ * <p>
+ * The DriverManager will try to load as many drivers as it can find and then
+ * for any given connection request, it will ask each driver in turn to try to
+ * connect to the target URL.
+ * </p>
+ * 
+ * <p>
+ * It is strongly recommended that each Driver class should be small and
+ * standalone so that the Driver class can be loaded and queried without
+ * bringing in vast quantities of supporting code.
+ * </p>
+ * 
+ * <p>
+ * When a Driver class is loaded, it should create an instance of itself and
+ * register it with the DriverManager. This means that a user can load and
+ * register a driver by doing Class.forName("foo.bah.Driver")
+ * </p>
+ * 
+ * @author Mark Matthews
+ * @version $Id: NonRegisteringDriver.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ * 
+ * @see org.gjt.mm.mysql.Connection
+ * @see java.sql.Driver
+ */
+public class NonRegisteringDriver implements java.sql.Driver {
+	/**
+	 * Key used to retreive the database value from the properties instance
+	 * passed to the driver.
+	 */
+	public static final String DBNAME_PROPERTY_KEY = "DBNAME";
+
+	/** Should the driver generate debugging output? */
+	public static final boolean DEBUG = false;
+
+	/** Index for hostname coming out of parseHostPortPair(). */
+	public final static int HOST_NAME_INDEX = 0;
+
+	/**
+	 * Key used to retreive the hostname value from the properties instance
+	 * passed to the driver.
+	 */
+	public static final String HOST_PROPERTY_KEY = "HOST";
+
+	/**
+	 * Key used to retreive the password value from the properties instance
+	 * passed to the driver.
+	 */
+	public static final String PASSWORD_PROPERTY_KEY = "password";
+
+	/** Index for port # coming out of parseHostPortPair(). */
+	public final static int PORT_NUMBER_INDEX = 1;
+
+	/**
+	 * Key used to retreive the port number value from the properties instance
+	 * passed to the driver.
+	 */
+	public static final String PORT_PROPERTY_KEY = "PORT";
+
+	public static final String PROPERTIES_TRANSFORM_KEY = "propertiesTransform";
+
+	/** Should the driver generate method-call traces? */
+	public static final boolean TRACE = false;
+
+	public static final String USE_CONFIG_PROPERTY_KEY = "useConfigs";
+
+	/**
+	 * Key used to retreive the username value from the properties instance
+	 * passed to the driver.
+	 */
+	public static final String USER_PROPERTY_KEY = "user";
+
+	/**
+	 * Gets the drivers major version number
+	 * 
+	 * @return the drivers major version number
+	 */
+	static int getMajorVersionInternal() {
+		return safeIntParse("@MYSQL_CJ_MAJOR_VERSION@"); //$NON-NLS-1$
+	}
+
+	/**
+	 * Get the drivers minor version number
+	 * 
+	 * @return the drivers minor version number
+	 */
+	static int getMinorVersionInternal() {
+		return safeIntParse("@MYSQL_CJ_MINOR_VERSION@"); //$NON-NLS-1$
+	}
+
+	/**
+	 * Parses hostPortPair in the form of [host][:port] into an array, with the
+	 * element of index HOST_NAME_INDEX being the host (or null if not
+	 * specified), and the element of index PORT_NUMBER_INDEX being the port (or
+	 * null if not specified).
+	 * 
+	 * @param hostPortPair
+	 *            host and port in form of of [host][:port]
+	 * 
+	 * @return array containing host and port as Strings
+	 * 
+	 * @throws SQLException
+	 *             if a parse error occurs
+	 */
+	protected static String[] parseHostPortPair(String hostPortPair)
+			throws SQLException {
+		int portIndex = hostPortPair.indexOf(":"); //$NON-NLS-1$
+
+		String[] splitValues = new String[2];
+
+		String hostname = null;
+
+		if (portIndex != -1) {
+			if ((portIndex + 1) < hostPortPair.length()) {
+				String portAsString = hostPortPair.substring(portIndex + 1);
+				hostname = hostPortPair.substring(0, portIndex);
+
+				splitValues[HOST_NAME_INDEX] = hostname;
+
+				splitValues[PORT_NUMBER_INDEX] = portAsString;
+			} else {
+				throw SQLError.createSQLException(Messages
+						.getString("NonRegisteringDriver.37"), //$NON-NLS-1$
+						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+			}
+		} else {
+			splitValues[HOST_NAME_INDEX] = hostPortPair;
+			splitValues[PORT_NUMBER_INDEX] = null;
+		}
+
+		return splitValues;
+	}
+
+	private static int safeIntParse(String intAsString) {
+		try {
+			return Integer.parseInt(intAsString);
+		} catch (NumberFormatException nfe) {
+			return 0;
+		}
+	}
+
+	/**
+	 * Construct a new driver and register it with DriverManager
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs.
+	 */
+	public NonRegisteringDriver() throws SQLException {
+		// Required for Class.forName().newInstance()
+	}
+
+	/**
+	 * Typically, drivers will return true if they understand the subprotocol
+	 * specified in the URL and false if they don't. This driver's protocols
+	 * start with jdbc:mysql:
+	 * 
+	 * @param url
+	 *            the URL of the driver
+	 * 
+	 * @return true if this driver accepts the given URL
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * 
+	 * @see java.sql.Driver#acceptsURL
+	 */
+	public boolean acceptsURL(String url) throws SQLException {
+		return (parseURL(url, null) != null);
+	}
+
+	//
+	// return the database name property
+	//
+
+	/**
+	 * Try to make a database connection to the given URL. The driver should
+	 * return "null" if it realizes it is the wrong kind of driver to connect to
+	 * the given URL. This will be common, as when the JDBC driverManager is
+	 * asked to connect to a given URL, it passes the URL to each loaded driver
+	 * in turn.
+	 * 
+	 * <p>
+	 * The driver should raise an SQLException if it is the right driver to
+	 * connect to the given URL, but has trouble connecting to the database.
+	 * </p>
+	 * 
+	 * <p>
+	 * The java.util.Properties argument can be used to pass arbitrary string
+	 * tag/value pairs as connection arguments.
+	 * </p>
+	 * 
+	 * <p>
+	 * My protocol takes the form:
+	 * 
+	 * <PRE>
+	 * 
+	 * jdbc:mysql://host:port/database
+	 * 
+	 * </PRE>
+	 * 
+	 * </p>
+	 * 
+	 * @param url
+	 *            the URL of the database to connect to
+	 * @param info
+	 *            a list of arbitrary tag/value pairs as connection arguments
+	 * 
+	 * @return a connection to the URL or null if it isnt us
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 * 
+	 * @see java.sql.Driver#connect
+	 */
+	public java.sql.Connection connect(String url, Properties info)
+			throws SQLException {
+		Properties props = null;
+
+		if ((props = parseURL(url, info)) == null) {
+			return null;
+		}
+
+		try {
+			Connection newConn = new com.mysql.jdbc.Connection(host(props),
+					port(props), props, database(props), url);
+
+			return newConn;
+		} catch (SQLException sqlEx) {
+			// Don't wrap SQLExceptions, throw
+			// them un-changed.
+			throw sqlEx;
+		} catch (Exception ex) {
+			throw SQLError.createSQLException(Messages
+					.getString("NonRegisteringDriver.17") //$NON-NLS-1$
+					+ ex.toString()
+					+ Messages.getString("NonRegisteringDriver.18"), //$NON-NLS-1$
+					SQLError.SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE);
+		}
+	}
+
+	/**
+	 * Returns the database property from <code>props</code>
+	 * 
+	 * @param props
+	 *            the Properties to look for the database property.
+	 * 
+	 * @return the database name.
+	 */
+	public String database(Properties props) {
+		return props.getProperty(DBNAME_PROPERTY_KEY); //$NON-NLS-1$
+	}
+
+	/**
+	 * Gets the drivers major version number
+	 * 
+	 * @return the drivers major version number
+	 */
+	public int getMajorVersion() {
+		return getMajorVersionInternal();
+	}
+
+	/**
+	 * Get the drivers minor version number
+	 * 
+	 * @return the drivers minor version number
+	 */
+	public int getMinorVersion() {
+		return getMinorVersionInternal();
+	}
+
+	/**
+	 * The getPropertyInfo method is intended to allow a generic GUI tool to
+	 * discover what properties it should prompt a human for in order to get
+	 * enough information to connect to a database.
+	 * 
+	 * <p>
+	 * Note that depending on the values the human has supplied so far,
+	 * additional values may become necessary, so it may be necessary to iterate
+	 * through several calls to getPropertyInfo
+	 * </p>
+	 * 
+	 * @param url
+	 *            the Url of the database to connect to
+	 * @param info
+	 *            a proposed list of tag/value pairs that will be sent on
+	 *            connect open.
+	 * 
+	 * @return An array of DriverPropertyInfo objects describing possible
+	 *         properties. This array may be an empty array if no properties are
+	 *         required
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 * 
+	 * @see java.sql.Driver#getPropertyInfo
+	 */
+	public DriverPropertyInfo[] getPropertyInfo(String url, Properties info)
+			throws SQLException {
+		if (info == null) {
+			info = new Properties();
+		}
+
+		if ((url != null) && url.startsWith("jdbc:mysql://")) { //$NON-NLS-1$
+			info = parseURL(url, info);
+		}
+
+		DriverPropertyInfo hostProp = new DriverPropertyInfo(HOST_PROPERTY_KEY, //$NON-NLS-1$
+				info.getProperty(HOST_PROPERTY_KEY)); //$NON-NLS-1$
+		hostProp.required = true;
+		hostProp.description = Messages.getString("NonRegisteringDriver.3"); //$NON-NLS-1$
+
+		DriverPropertyInfo portProp = new DriverPropertyInfo(PORT_PROPERTY_KEY, //$NON-NLS-1$
+				info.getProperty(PORT_PROPERTY_KEY, "3306")); //$NON-NLS-1$ //$NON-NLS-2$
+		portProp.required = false;
+		portProp.description = Messages.getString("NonRegisteringDriver.7"); //$NON-NLS-1$
+
+		DriverPropertyInfo dbProp = new DriverPropertyInfo(DBNAME_PROPERTY_KEY, //$NON-NLS-1$
+				info.getProperty(DBNAME_PROPERTY_KEY)); //$NON-NLS-1$
+		dbProp.required = false;
+		dbProp.description = "Database name"; //$NON-NLS-1$
+
+		DriverPropertyInfo userProp = new DriverPropertyInfo(USER_PROPERTY_KEY, //$NON-NLS-1$
+				info.getProperty(USER_PROPERTY_KEY)); //$NON-NLS-1$
+		userProp.required = true;
+		userProp.description = Messages.getString("NonRegisteringDriver.13"); //$NON-NLS-1$
+
+		DriverPropertyInfo passwordProp = new DriverPropertyInfo(
+				PASSWORD_PROPERTY_KEY, //$NON-NLS-1$
+				info.getProperty(PASSWORD_PROPERTY_KEY)); //$NON-NLS-1$
+		passwordProp.required = true;
+		passwordProp.description = Messages
+				.getString("NonRegisteringDriver.16"); //$NON-NLS-1$
+
+		DriverPropertyInfo[] dpi = ConnectionProperties
+				.exposeAsDriverPropertyInfo(info, 5);
+
+		dpi[0] = hostProp;
+		dpi[1] = portProp;
+		dpi[2] = dbProp;
+		dpi[3] = userProp;
+		dpi[4] = passwordProp;
+
+		return dpi;
+	}
+
+	//
+	// return the value of any property this driver knows about
+	//
+
+	/**
+	 * Returns the hostname property
+	 * 
+	 * @param props
+	 *            the java.util.Properties instance to retrieve the hostname
+	 *            from.
+	 * 
+	 * @return the hostname
+	 */
+	public String host(Properties props) {
+		return props.getProperty(HOST_PROPERTY_KEY, "localhost"); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * Report whether the driver is a genuine JDBC compliant driver. A driver
+	 * may only report "true" here if it passes the JDBC compliance tests,
+	 * otherwise it is required to return false. JDBC compliance requires full
+	 * support for the JDBC API and full support for SQL 92 Entry Level.
+	 * 
+	 * <p>
+	 * MySQL is not SQL92 compliant
+	 * </p>
+	 * 
+	 * @return is this driver JDBC compliant?
+	 */
+	public boolean jdbcCompliant() {
+		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)
+				: new Properties();
+
+		if (url == null) {
+			return null;
+		}
+
+		if (!StringUtils.startsWithIgnoreCase(url, "jdbc:mysql://") && 
+				!StringUtils.startsWithIgnoreCase(url, "jdbc:mysql:mxj://")) { //$NON-NLS-1$
+
+			return null;
+		}
+
+		int beginningOfSlashes = 13;
+		
+		if (StringUtils.startsWithIgnoreCase(url, "jdbc:mysql:mxj://")) {
+			beginningOfSlashes = 17;
+			
+			urlProps.setProperty("socketFactory", 
+				"com.mysql.management.driverlaunched.ServerLauncherSocketFactory");
+		}
+		
+		/*
+		 * Parse parameters after the ? in the URL and remove them from the
+		 * original URL.
+		 */
+		int index = url.indexOf("?"); //$NON-NLS-1$
+
+		if (index != -1) {
+			String paramString = url.substring(index + 1, url.length());
+			url = url.substring(0, index);
+
+			StringTokenizer queryParams = new StringTokenizer(paramString, "&"); //$NON-NLS-1$
+
+			while (queryParams.hasMoreTokens()) {
+				String parameterValuePair = queryParams.nextToken();
+
+				int indexOfEquals = StringUtils.indexOfIgnoreCase(0,
+						parameterValuePair, "=");
+
+				String parameter = null;
+				String value = null;
+
+				if (indexOfEquals != -1) {
+					parameter = parameterValuePair.substring(0, indexOfEquals);
+
+					if (indexOfEquals + 1 < parameterValuePair.length()) {
+						value = parameterValuePair.substring(indexOfEquals + 1);
+					}
+				}
+
+				if ((value != null && value.length() > 0)
+						&& (parameter != null && parameter.length() > 0)) {
+					try {
+						urlProps.put(parameter, URLDecoder.decode(value,
+								"UTF-8"));
+					} catch (UnsupportedEncodingException badEncoding) {
+						// punt
+						urlProps.put(parameter, URLDecoder.decode(value));
+					} catch (NoSuchMethodError nsme) {
+						// punt again
+						urlProps.put(parameter,  URLDecoder.decode(value));
+					}
+				}
+			}
+		}
+
+		url = url.substring(beginningOfSlashes);
+
+		String hostStuff = null;
+
+		int slashIndex = url.indexOf("/"); //$NON-NLS-1$
+
+		if (slashIndex != -1) {
+			hostStuff = url.substring(0, slashIndex);
+
+			if ((slashIndex + 1) < url.length()) {
+				urlProps.put(DBNAME_PROPERTY_KEY, //$NON-NLS-1$
+						url.substring((slashIndex + 1), url.length()));
+			}
+		} else {
+			return null;
+		}
+
+		if ((hostStuff != null) && (hostStuff.length() > 0)) {
+			urlProps.put(HOST_PROPERTY_KEY, hostStuff); //$NON-NLS-1$
+		}
+
+		String propertiesTransformClassName = urlProps
+				.getProperty(PROPERTIES_TRANSFORM_KEY);
+
+		if (propertiesTransformClassName != null) {
+			try {
+				ConnectionPropertiesTransform propTransformer = (ConnectionPropertiesTransform) Class
+						.forName(propertiesTransformClassName).newInstance();
+
+				urlProps = propTransformer.transformProperties(urlProps);
+			} catch (InstantiationException e) {
+				throw SQLError.createSQLException(
+						"Unable to create properties transform instance '"
+								+ propertiesTransformClassName
+								+ "' due to underlying exception: "
+								+ e.toString(),
+						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+			} catch (IllegalAccessException e) {
+				throw SQLError.createSQLException(
+						"Unable to create properties transform instance '"
+								+ propertiesTransformClassName
+								+ "' due to underlying exception: "
+								+ e.toString(),
+						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+			} catch (ClassNotFoundException e) {
+				throw SQLError.createSQLException(
+						"Unable to create properties transform instance '"
+								+ propertiesTransformClassName
+								+ "' due to underlying exception: "
+								+ e.toString(),
+						SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+			}
+		}
+
+		// If we use a config, it actually should get overridden by anything in
+		// the URL or passed-in properties
+
+		String configNames = null;
+
+		if (defaults != null) {
+			configNames = defaults.getProperty(USE_CONFIG_PROPERTY_KEY);
+		}
+
+		if (configNames == null) {
+			configNames = urlProps.getProperty(USE_CONFIG_PROPERTY_KEY);
+		}
+
+		if (configNames != null) {
+			List splitNames = StringUtils.split(configNames, ",", true);
+
+			Properties configProps = new Properties();
+
+			Iterator namesIter = splitNames.iterator();
+
+			while (namesIter.hasNext()) {
+				String configName = (String) namesIter.next();
+
+				try {
+					InputStream configAsStream = getClass()
+							.getResourceAsStream(
+									"configs/" + configName + ".properties");
+
+					if (configAsStream == null) {
+						throw SQLError.createSQLException(
+								"Can't find configuration template named '"
+										+ configName + "'",
+								SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+					}
+					configProps.load(configAsStream);
+				} catch (IOException ioEx) {
+					throw SQLError.createSQLException(
+							"Unable to load configuration template '"
+									+ configName
+									+ "' due to underlying IOException: "
+									+ ioEx,
+							SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE);
+				}
+			}
+
+			Iterator propsIter = urlProps.keySet().iterator();
+
+			while (propsIter.hasNext()) {
+				String key = propsIter.next().toString();
+				String property = urlProps.getProperty(key);
+				configProps.setProperty(key, property);
+			}
+
+			urlProps = configProps;
+		}
+
+		// Properties passed in should override ones in URL
+
+		if (defaults != null) {
+			Iterator propsIter = defaults.keySet().iterator();
+
+			while (propsIter.hasNext()) {
+				String key = propsIter.next().toString();
+				String property = defaults.getProperty(key);
+				urlProps.setProperty(key, property);
+			}
+		}
+
+		return urlProps;
+	}
+
+	/**
+	 * Returns the port number property
+	 * 
+	 * @param props
+	 *            the properties to get the port number from
+	 * 
+	 * @return the port number
+	 */
+	public int port(Properties props) {
+		return Integer.parseInt(props.getProperty(PORT_PROPERTY_KEY, "3306")); //$NON-NLS-1$ //$NON-NLS-2$
+	}
+
+	/**
+	 * Returns the given property from <code>props</code>
+	 * 
+	 * @param name
+	 *            the property name
+	 * @param props
+	 *            the property instance to look in
+	 * 
+	 * @return the property value, or null if not found.
+	 */
+	public String property(String name, Properties props) {
+		return props.getProperty(name);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,118 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+ 
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception 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.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+/**
+ * Driver that opens two connections, one two a replication master, and another
+ * to one or more slaves, and decides to use master when the connection is not
+ * read-only, and use slave(s) when the connection is read-only.
+ * 
+ * @version $Id: NonRegisteringReplicationDriver.java,v 1.1.2.1 2005/05/13
+ *          18:58:37 mmatthews Exp $
+ */
+public class NonRegisteringReplicationDriver extends NonRegisteringDriver {
+	public NonRegisteringReplicationDriver() throws SQLException {
+		super();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Driver#connect(java.lang.String, java.util.Properties)
+	 */
+	public Connection connect(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);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NotImplemented.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NotImplemented.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NotImplemented.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+/**
+ * Thrown from methods not required to be implemented.
+ * 
+ * @author Mark Matthews
+ */
+public class NotImplemented extends java.sql.SQLException {
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new NotImplemented object.
+	 */
+	public NotImplemented() {
+		super(
+				Messages.getString("NotImplemented.0"), SQLError.SQL_STATE_DRIVER_NOT_CAPABLE); //$NON-NLS-1$
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NotUpdatable.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NotUpdatable.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/NotUpdatable.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+/**
+ * Thrown when a result sate is not updatable
+ * 
+ * @author Mark Matthews
+ */
+public class NotUpdatable extends SQLException {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	/**
+	 * The message to use when result set is not updatable.
+	 * 
+	 * The same message is used in the warnings generated by Updatabale result
+	 * set.
+	 */
+	public static final String NOT_UPDATEABLE_MESSAGE = Messages
+			.getString("NotUpdatable.0") //$NON-NLS-1$
+			+ Messages.getString("NotUpdatable.1") //$NON-NLS-1$
+			+ Messages.getString("NotUpdatable.2") //$NON-NLS-1$
+			+ Messages.getString("NotUpdatable.3") //$NON-NLS-1$
+			+ 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);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/OperationNotSupportedException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/OperationNotSupportedException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/OperationNotSupportedException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,16 @@
+/*
+ * Created on Sep 23, 2004
+ *
+ * TODO To change the template for this generated file go to
+ * Window - Preferences - Java - Code Style - Code Templates
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+class OperationNotSupportedException extends SQLException {
+	OperationNotSupportedException() {
+		super(
+				Messages.getString("RowDataDynamic.10"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/OutputStreamWatcher.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/OutputStreamWatcher.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/OutputStreamWatcher.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+/**
+ * Objects that want to be notified of lifecycle events on a
+ * WatchableOutputStream should implement this interface, and register
+ * themselves with setWatcher() on the WatchableOutputStream instance.
+ * 
+ * @author Mark Matthews
+ */
+interface OutputStreamWatcher {
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Called when the OutputStream being watched has .close() called
+	 */
+	void streamClosed(WatchableOutputStream out);
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/PacketTooBigException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/PacketTooBigException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/PacketTooBigException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,54 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+/**
+ * Thrown when a packet that is too big for the server is created.
+ * 
+ * @author Mark Matthews
+ */
+public class PacketTooBigException extends SQLException {
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new PacketTooBigException object.
+	 * 
+	 * @param packetSize
+	 *            the size of the packet that was going to be sent
+	 * @param maximumPacketSize
+	 *            the maximum size the server will accept
+	 */
+	public PacketTooBigException(long packetSize, long maximumPacketSize) {
+		super(
+				Messages.getString("PacketTooBigException.0") + packetSize + Messages.getString("PacketTooBigException.1") //$NON-NLS-1$ //$NON-NLS-2$
+						+ maximumPacketSize
+						+ Messages.getString("PacketTooBigException.2") //$NON-NLS-1$
+						+ Messages.getString("PacketTooBigException.3") //$NON-NLS-1$
+						+ Messages.getString("PacketTooBigException.4"), SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/PreparedStatement.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/PreparedStatement.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/PreparedStatement.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,3776 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectOutputStream;
+import java.io.Reader;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.net.URL;
+import java.sql.Array;
+import java.sql.Clob;
+import java.sql.ParameterMetaData;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.text.ParsePosition;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+import com.mysql.jdbc.Statement.CancelTask;
+import com.mysql.jdbc.exceptions.MySQLTimeoutException;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+/**
+ * A SQL Statement is 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> The setXXX methods for setting IN parameter values must specify
+ * types that are compatible with the defined SQL type of the input parameter.
+ * For instance, if the IN parameter has SQL type Integer, then setInt should be
+ * used.
+ * </p>
+ * 
+ * <p>
+ * If arbitrary parameter type conversions are required, then the setObject
+ * method should be used with a target SQL type.
+ * </p>
+ * 
+ * @author Mark Matthews
+ * @version $Id: PreparedStatement.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
+ *          Exp $
+ * 
+ * @see java.sql.ResultSet
+ * @see java.sql.PreparedStatement
+ */
+public class PreparedStatement extends com.mysql.jdbc.Statement implements
+		java.sql.PreparedStatement {
+	class BatchParams {
+		boolean[] isNull = null;
+
+		boolean[] isStream = null;
+
+		InputStream[] parameterStreams = null;
+
+		byte[][] parameterStrings = null;
+
+		int[] streamLengths = null;
+
+		BatchParams(byte[][] strings, InputStream[] streams,
+				boolean[] isStreamFlags, int[] lengths, boolean[] isNullFlags) {
+			//
+			// Make copies
+			//
+			this.parameterStrings = new byte[strings.length][];
+			this.parameterStreams = new InputStream[streams.length];
+			this.isStream = new boolean[isStreamFlags.length];
+			this.streamLengths = new int[lengths.length];
+			this.isNull = new boolean[isNullFlags.length];
+			System.arraycopy(strings, 0, this.parameterStrings, 0,
+					strings.length);
+			System.arraycopy(streams, 0, this.parameterStreams, 0,
+					streams.length);
+			System.arraycopy(isStreamFlags, 0, this.isStream, 0,
+					isStreamFlags.length);
+			System.arraycopy(lengths, 0, this.streamLengths, 0, lengths.length);
+			System
+					.arraycopy(isNullFlags, 0, this.isNull, 0,
+							isNullFlags.length);
+		}
+	}
+
+	class EndPoint {
+		int begin;
+
+		int end;
+
+		EndPoint(int b, int e) {
+			this.begin = b;
+			this.end = e;
+		}
+	}
+
+	class ParseInfo {
+		char firstStmtChar = 0;
+
+		boolean foundLimitClause = false;
+
+		boolean foundLoadData = false;
+
+		long lastUsed = 0;
+
+		int statementLength = 0;
+
+		byte[][] staticSql = null;
+
+		/**
+		 * 
+		 */
+		public ParseInfo(String sql, Connection 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);
+			}
+
+			this.lastUsed = System.currentTimeMillis();
+
+			String quotedIdentifierString = dbmd.getIdentifierQuoteString();
+
+			char quotedIdentifierChar = 0;
+
+			if ((quotedIdentifierString != null)
+					&& !quotedIdentifierString.equals(" ") //$NON-NLS-1$
+					&& (quotedIdentifierString.length() > 0)) {
+				quotedIdentifierChar = quotedIdentifierString.charAt(0);
+			}
+
+			this.statementLength = sql.length();
+
+			ArrayList endpointList = new ArrayList();
+			boolean inQuotes = false;
+			char quoteChar = 0;
+			boolean inQuotedId = false;
+			int lastParmEnd = 0;
+			int i;
+
+			int stopLookingForLimitClause = this.statementLength - 5;
+
+			this.foundLimitClause = false;
+			
+			boolean noBackslashEscapes = connection.isNoBackslashEscapesSet();
+			
+			for (i = 0; i < this.statementLength; ++i) {
+				char c = sql.charAt(i);
+
+				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);
+				}
+
+				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;
+						}
+					} 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 (!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 ((posM == 'M') || (posM == 'm')) {
+								char posI2 = sql.charAt(i + 3);
+
+								if ((posI2 == 'I') || (posI2 == 'i')) {
+									char posT = sql.charAt(i + 4);
+
+									if ((posT == 'T') || (posT == 't')) {
+										foundLimitClause = 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;
+			}
+
+			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;
+
+				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);
+					}
+
+					this.staticSql[i] = buf;
+				} else {
+					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);
+					}
+				}
+			}
+		}
+	}
+
+	private final static byte[] HEX_DIGITS = new byte[] { (byte) '0',
+			(byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5',
+			(byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) 'A',
+			(byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F' };
+
+	/**
+	 * Reads length bytes from reader into buf. Blocks until enough input is
+	 * available
+	 * 
+	 * @param reader
+	 *            DOCUMENT ME!
+	 * @param buf
+	 *            DOCUMENT ME!
+	 * @param length
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws IOException
+	 *             DOCUMENT ME!
+	 */
+	private static int readFully(Reader reader, char[] buf, int length)
+			throws IOException {
+		int numCharsRead = 0;
+
+		while (numCharsRead < length) {
+			int count = reader.read(buf, numCharsRead, length - numCharsRead);
+
+			if (count < 0) {
+				break;
+			}
+
+			numCharsRead += count;
+		}
+
+		return numCharsRead;
+	}
+
+	/**
+	 * Does the batch (if any) contain "plain" statements added by
+	 * Statement.addBatch(String)?
+	 * 
+	 * If so, we can't re-write it to use multi-value or multi-queries.
+	 */
+	protected boolean batchHasPlainStatements = false;
+
+	private java.sql.DatabaseMetaData dbmd = null;
+
+	/**
+	 * What is the first character of the prepared statement (used to check for
+	 * SELECT vs. INSERT/UPDATE/DELETE)
+	 */
+	protected char firstCharOfStmt = 0;
+
+	/** Does the SQL for this statement contain a 'limit' clause? */
+	protected boolean hasLimitClause = false;
+	
+	/** Is this query a LOAD DATA query? */
+	protected boolean isLoadDataQuery = false;
+
+	private boolean[] isNull = null;
+
+	private boolean[] isStream = null;
+
+	protected int numberOfExecutions = 0;
+
+	/** The SQL that was passed in to 'prepare' */
+	protected String originalSql = null;
+
+	/** The number of parameters in this PreparedStatement */
+	protected int parameterCount;
+
+	protected MysqlParameterMetadata parameterMetaData;
+
+	private InputStream[] parameterStreams = null;
+
+	private byte[][] parameterValues = null;
+
+	private ParseInfo parseInfo;
+
+	private java.sql.ResultSetMetaData pstmtResultMetaData;
+
+	private byte[][] staticSqlStrings = null;
+
+	private byte[] streamConvertBuf = new byte[4096];
+
+	private int[] streamLengths = null;
+
+	private SimpleDateFormat tsdf = null;
+
+	/**
+	 * Are we using a version of MySQL where we can use 'true' boolean values?
+	 */
+	protected boolean useTrueBoolean = false;
+
+	private boolean usingAnsiMode;
+
+	private String batchedValuesClause;
+
+	/**
+	 * Constructor used by server-side prepared statements
+	 * 
+	 * @param conn
+	 *            the connection that created us
+	 * @param catalog
+	 *            the catalog in use when we were created
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected PreparedStatement(Connection conn, String catalog)
+			throws SQLException {
+		super(conn, catalog);
+	}
+
+	/**
+	 * Constructor for the PreparedStatement class.
+	 * 
+	 * @param conn
+	 *            the connection creating this statement
+	 * @param sql
+	 *            the SQL for this statement
+	 * @param catalog
+	 *            the catalog/database this statement should be issued against
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs.
+	 */
+	public PreparedStatement(Connection conn, String sql, String catalog)
+			throws SQLException {
+		super(conn, catalog);
+
+		if (sql == null) {
+			throw SQLError.createSQLException(Messages.getString("PreparedStatement.0"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		this.originalSql = sql;
+
+		this.dbmd = this.connection.getMetaData();
+
+		this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
+
+		this.parseInfo = new ParseInfo(sql, this.connection, this.dbmd,
+				this.charEncoding, this.charConverter);
+
+		initializeFromParseInfo();
+	}
+
+	/**
+	 * Creates a new PreparedStatement object.
+	 * 
+	 * @param conn
+	 *            the connection creating this statement
+	 * @param sql
+	 *            the SQL for this statement
+	 * @param catalog
+	 *            the catalog/database this statement should be issued against
+	 * @param cachedParseInfo
+	 *            already created parseInfo.
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public PreparedStatement(Connection conn, String sql, String catalog,
+			ParseInfo cachedParseInfo) throws SQLException {
+		super(conn, catalog);
+
+		if (sql == null) {
+			throw SQLError.createSQLException(Messages.getString("PreparedStatement.1"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		this.originalSql = sql;
+
+		this.dbmd = this.connection.getMetaData();
+
+		this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
+
+		this.parseInfo = cachedParseInfo;
+
+		this.usingAnsiMode = !this.connection.useAnsiQuotedIdentifiers();
+
+		initializeFromParseInfo();
+	}
+
+	/**
+	 * JDBC 2.0 Add a set of parameters to the batch.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 * 
+	 * @see Statement#addBatch
+	 */
+	public void addBatch() throws SQLException {
+		if (this.batchedArgs == null) {
+			this.batchedArgs = new ArrayList();
+		}
+
+		this.batchedArgs.add(new BatchParams(this.parameterValues,
+				this.parameterStreams, this.isStream, this.streamLengths,
+				this.isNull));
+	}
+
+	public synchronized void addBatch(String sql) throws SQLException {
+		this.batchHasPlainStatements = true;
+
+		super.addBatch(sql);
+	}
+
+	protected String asSql() throws SQLException {
+		return asSql(false);
+	}
+
+	protected String asSql(boolean quoteStreamsAndUnknowns) throws SQLException {
+		if (this.isClosed) {
+			return "statement has been closed, no further internal information available";
+		}
+		
+		StringBuffer buf = new StringBuffer();
+
+		try {
+			for (int i = 0; i < this.parameterCount; ++i) {
+				if (this.charEncoding != null) {
+					buf.append(new String(this.staticSqlStrings[i],
+							this.charEncoding));
+				} else {
+					buf.append(new String(this.staticSqlStrings[i]));
+				}
+
+				if ((this.parameterValues[i] == null) && !this.isStream[i]) {
+					if (quoteStreamsAndUnknowns) {
+						buf.append("'");
+					}
+
+					buf.append("** NOT SPECIFIED **"); //$NON-NLS-1$
+
+					if (quoteStreamsAndUnknowns) {
+						buf.append("'");
+					}
+				} else if (this.isStream[i]) {
+					if (quoteStreamsAndUnknowns) {
+						buf.append("'");
+					}
+
+					buf.append("** STREAM DATA **"); //$NON-NLS-1$
+
+					if (quoteStreamsAndUnknowns) {
+						buf.append("'");
+					}
+				} else {
+					if (this.charConverter != null) {
+						buf.append(this.charConverter
+								.toString(this.parameterValues[i]));
+					} else {
+						if (this.charEncoding != null) {
+							buf.append(new String(this.parameterValues[i],
+									this.charEncoding));
+						} else {
+							buf.append(StringUtils
+									.toAsciiString(this.parameterValues[i]));
+						}
+					}
+				}
+			}
+
+			if (this.charEncoding != null) {
+				buf.append(new String(
+						this.staticSqlStrings[this.parameterCount],
+						this.charEncoding));
+			} else {
+				buf
+						.append(StringUtils
+								.toAsciiString(this.staticSqlStrings[this.parameterCount]));
+			}
+		} catch (UnsupportedEncodingException uue) {
+			throw new RuntimeException(Messages
+					.getString("PreparedStatement.32") //$NON-NLS-1$
+					+ this.charEncoding
+					+ Messages.getString("PreparedStatement.33")); //$NON-NLS-1$
+		}
+
+		return buf.toString();
+	}
+
+	public synchronized void clearBatch() throws SQLException {
+		this.batchHasPlainStatements = false;
+
+		super.clearBatch();
+	}
+
+	/**
+	 * In general, parameter values remain in force for repeated used of a
+	 * Statement. Setting a parameter value automatically clears its previous
+	 * value. However, in some cases, it is useful to immediately release the
+	 * resources used by the current parameter values; this can be done by
+	 * calling clearParameters
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public synchronized void clearParameters() throws SQLException {
+		checkClosed();
+		
+		for (int i = 0; i < this.parameterValues.length; i++) {
+			this.parameterValues[i] = null;
+			this.parameterStreams[i] = null;
+			this.isStream[i] = false;
+			this.isNull[i] = false;
+		}
+	}
+
+	/**
+	 * Closes this prepared statement and releases all resources.
+	 * 
+	 * @throws SQLException
+	 *             if database error occurs.
+	 */
+	public synchronized void close() throws SQLException {
+		realClose(true, true);
+	}
+
+	private final void escapeblockFast(byte[] buf, Buffer packet, int size)
+			throws SQLException {
+		int lastwritten = 0;
+
+		for (int i = 0; i < size; i++) {
+			byte b = buf[i];
+
+			if (b == '\0') {
+				// write stuff not yet written
+				if (i > lastwritten) {
+					packet.writeBytesNoNull(buf, lastwritten, i - lastwritten);
+				}
+
+				// write escape
+				packet.writeByte((byte) '\\');
+				packet.writeByte((byte) '0');
+				lastwritten = i + 1;
+			} else {
+				if ((b == '\\') || (b == '\'')
+						|| (!this.usingAnsiMode && b == '"')) {
+					// write stuff not yet written
+					if (i > lastwritten) {
+						packet.writeBytesNoNull(buf, lastwritten, i
+								- lastwritten);
+					}
+
+					// write escape
+					packet.writeByte((byte) '\\');
+					lastwritten = i; // not i+1 as b wasn't written.
+				}
+			}
+		}
+
+		// write out remaining stuff from buffer
+		if (lastwritten < size) {
+			packet.writeBytesNoNull(buf, lastwritten, size - lastwritten);
+		}
+	}
+
+	private final void escapeblockFast(byte[] buf,
+			ByteArrayOutputStream bytesOut, int size) {
+		int lastwritten = 0;
+
+		for (int i = 0; i < size; i++) {
+			byte b = buf[i];
+
+			if (b == '\0') {
+				// write stuff not yet written
+				if (i > lastwritten) {
+					bytesOut.write(buf, lastwritten, i - lastwritten);
+				}
+
+				// write escape
+				bytesOut.write('\\');
+				bytesOut.write('0');
+				lastwritten = i + 1;
+			} else {
+				if ((b == '\\') || (b == '\'')
+						|| (!this.usingAnsiMode && b == '"')) {
+					// write stuff not yet written
+					if (i > lastwritten) {
+						bytesOut.write(buf, lastwritten, i - lastwritten);
+					}
+
+					// write escape
+					bytesOut.write('\\');
+					lastwritten = i; // not i+1 as b wasn't written.
+				}
+			}
+		}
+
+		// write out remaining stuff from buffer
+		if (lastwritten < size) {
+			bytesOut.write(buf, lastwritten, size - lastwritten);
+		}
+	}
+
+	/**
+	 * Some prepared statements return multiple results; the execute method
+	 * handles these complex statements as well as the simpler form of
+	 * statements handled by executeQuery and executeUpdate
+	 * 
+	 * @return true if the next result is a ResultSet; false if it is an update
+	 *         count or there are no more results
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public boolean execute() throws SQLException {
+		if (this.connection.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);
+		}
+
+		checkClosed();
+
+		ResultSet rs = null;
+
+		synchronized (this.connection.getMutex()) {
+			clearWarnings();
+
+			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);
+			}
+
+			boolean oldInfoMsgState = false;
+
+			if (this.retrieveGeneratedKeys) {
+				oldInfoMsgState = this.connection.isReadInfoMsgEnabled();
+				this.connection.setReadInfoMsgEnabled(true);
+			}
+
+			// 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).
+			//
+			// Only apply max_rows to selects
+			//
+			if (this.connection.useMaxRows()) {
+				int rowLimit = -1;
+
+				if (this.firstCharOfStmt == 'S') {
+					if (this.hasLimitClause) {
+						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);
+						} 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);
+						}
+					}
+				} 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);
+				}
+
+				// Finally, execute the query
+				rs = executeInternal(rowLimit, sendPacket,
+						createStreamingResultSet(),
+						(this.firstCharOfStmt == 'S'), true, false);
+			} else {
+				rs = executeInternal(-1, sendPacket,
+						createStreamingResultSet(),
+						(this.firstCharOfStmt == 'S'), true, false);
+			}
+
+			if (this.retrieveGeneratedKeys) {
+				this.connection.setReadInfoMsgEnabled(oldInfoMsgState);
+				rs.setFirstCharOfQuery('R');
+			}
+
+			if (oldCatalog != null) {
+				this.connection.setCatalog(oldCatalog);
+			}
+
+			this.lastInsertId = rs.getUpdateID();
+
+			if (rs != null) {
+				this.results = rs;
+			}
+		}
+
+		return ((rs != null) && rs.reallyResult());
+	}
+
+	/**
+	 * 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 int[] executeBatch() throws SQLException {
+		checkClosed();
+		
+		if (this.connection.isReadOnly()) {
+			throw new SQLException(Messages.getString("PreparedStatement.25") //$NON-NLS-1$
+					+ Messages.getString("PreparedStatement.26"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		synchronized (this.connection.getMutex()) {
+			try {
+				clearWarnings();
+
+				if (!this.batchHasPlainStatements
+						&& this.connection.getRewriteBatchedStatements()) {
+					if (StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
+							"INSERT")) {
+						return executeBatchedInserts();
+					}
+				}
+
+				return executeBatchSerially();
+			} finally {
+				clearBatch();
+			}
+		}
+	}
+
+	/**
+	 * Rewrites the already prepared statement into a multi-value insert
+	 * statement of 'statementsPerBatch' values and executes the entire batch
+	 * using this new statement.
+	 * 
+	 * @return update counts in the same fashion as executeBatch()
+	 * 
+	 * @throws SQLException
+	 */
+	private int[] executeBatchedInserts() throws SQLException {
+		String valuesClause = extractValuesClause();
+
+		Connection locallyScopedConn = this.connection;
+		
+		if (valuesClause == null) {
+			return executeBatchSerially();
+		}
+
+		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;
+
+		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;
+
+		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) {
+				updateCountRunningTotal += batchedStatement.executeUpdate();
+
+				getBatchedGeneratedKeys(batchedStatement);
+				batchedStatement.clearParameters();
+				batchedParamIndex = 1;
+
+			}
+
+			BatchParams paramArg = (BatchParams) this.batchedArgs
+					.get(batchCounter++);
+
+			batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
+					batchedParamIndex, paramArg);
+		}
+
+		updateCountRunningTotal += batchedStatement.executeUpdate();
+		getBatchedGeneratedKeys(batchedStatement);
+
+		numValuesPerBatch = numBatchedArgs - batchCounter;
+
+		if (numValuesPerBatch > 0) {
+
+			batchedStatement = locallyScopedConn.prepareStatement(
+					generateBatchedInsertSQL(valuesClause, numValuesPerBatch),
+					RETURN_GENERATED_KEYS);
+			batchedParamIndex = 1;
+
+			while (batchCounter < numBatchedArgs) {
+
+				BatchParams paramArg = (BatchParams) this.batchedArgs
+						.get(batchCounter++);
+				batchedParamIndex = setOneBatchedParameterSet(batchedStatement,
+						batchedParamIndex, paramArg);
+			}
+
+			updateCountRunningTotal += batchedStatement.executeUpdate();
+			getBatchedGeneratedKeys(batchedStatement);
+		}
+
+		int[] updateCounts = new int[this.batchedArgs.size()];
+
+		for (int i = 0; i < this.batchedArgs.size(); i++) {
+			updateCounts[i] = 1;
+		}
+
+		return updateCounts;
+	}
+
+	protected int computeBatchSize(int numBatchedArgs) {
+		long sizeOfEntireBatch = 0;
+		long maxSizeOfParameterSet = 0;
+		
+		for (int i = 0; i < numBatchedArgs; i++) {
+			BatchParams paramArg = (BatchParams) this.batchedArgs
+			.get(i);
+
+			boolean[] isNullBatch = paramArg.isNull;
+			boolean[] isStreamBatch = paramArg.isStream;
+
+			long sizeOfParameterSet = 0;
+			
+			for (int j = 0; j < isNullBatch.length; j++) {
+				if (!isNullBatch[j]) {
+
+					if (isStreamBatch[j]) {
+						int streamLength = paramArg.streamLengths[j];
+						
+						if (streamLength != -1) {
+							sizeOfParameterSet += streamLength * 2; // for safety in escaping
+						} else {
+							int paramLength = paramArg.parameterStrings[j].length;
+							sizeOfParameterSet += paramLength;
+						}
+					} else {
+						sizeOfParameterSet += 4; // for NULL literal in SQL 
+					}
+				}
+			}
+			
+			//
+			// Account for static part of values clause
+			// This is a little naiive, because the ?s will be replaced
+			// but it gives us some padding, and is less housekeeping
+			// to ignore them. We're looking for a "fuzzy" value here
+			// anyway
+			//
+			
+			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);
+	}
+
+	/**
+	 * Executes the current batch of statements by executing them one-by-one.
+	 * 
+	 * @return a list of update counts
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected int[] executeBatchSerially() throws SQLException {
+		
+		Connection locallyScopedConn = this.connection;
+		
+		if (locallyScopedConn == null) {
+			checkClosed();
+		}
+
+		int[] updateCounts = null;
+
+		if (this.batchedArgs != null) {
+			int nbrCommands = this.batchedArgs.size();
+			updateCounts = new int[nbrCommands];
+
+			for (int i = 0; i < nbrCommands; i++) {
+				updateCounts[i] = -3;
+			}
+
+			SQLException sqlEx = null;
+
+			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) });
+								}
+							} finally {
+								if (rs != null) {
+									rs.close();
+								}
+							}
+						}
+					} 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];
+	}
+
+	/**
+	 * Actually execute the prepared statement. This is here so server-side
+	 * PreparedStatements can re-use most of the code from this class.
+	 * 
+	 * @param maxRowsToRetrieve
+	 *            the max number of rows to return
+	 * @param sendPacket
+	 *            the packet to send
+	 * @param createStreamingResultSet
+	 *            should a 'streaming' result set be created?
+	 * @param queryIsSelectOnly
+	 *            is this query doing a SELECT?
+	 * @param unpackFields
+	 *            DOCUMENT ME!
+	 * 
+	 * @return the results as a ResultSet
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	protected ResultSet executeInternal(int maxRowsToRetrieve,
+			Buffer sendPacket, boolean createStreamingResultSet,
+			boolean queryIsSelectOnly, boolean unpackFields, 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);
+			}
+			
+			rs = locallyScopedConnection.execSQL(this, null, maxRowsToRetrieve, sendPacket,
+				this.resultSetType, this.resultSetConcurrency,
+				createStreamingResultSet, this.currentCatalog,
+				unpackFields, isBatch);
+			
+			if (timeoutTask != null) {
+				timeoutTask.cancel();
+				timeoutTask = null;
+			}
+		
+			if (this.wasCancelled) {
+				this.wasCancelled = false;
+				throw new MySQLTimeoutException();
+			}
+		} finally {
+			if (timeoutTask != null) {
+				timeoutTask.cancel();
+			}
+		}
+
+		return rs;
+	}
+
+	/**
+	 * A Prepared SQL query is executed and its ResultSet is returned
+	 * 
+	 * @return a ResultSet that contains the data produced by the query - never
+	 *         null
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public java.sql.ResultSet executeQuery() throws SQLException {
+		checkClosed();
+		
+		Connection locallyScopedConn = this.connection;
+		
+		checkForDml(this.originalSql, this.firstCharOfStmt);
+
+		CachedResultSetMetaData cachedMetadata = null;
+
+		// We need to execute this all together
+		// So synchronize on the Connection's mutex (because
+		// even queries going through there synchronize
+		// on the same mutex.
+		synchronized (locallyScopedConn.getMutex()) {
+			clearWarnings();
+
+			this.batchedGeneratedKeys = null;
+
+			Buffer sendPacket = fillSendPacket();
+
+			if (this.results != null) {
+				if (!this.connection.getHoldResultsOpenOverStatementClose()) {
+					if (!this.holdResultsOpenOverClose) {
+						this.results.realClose(false);
+					}
+				}
+			}
+
+			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(this.originalSql);
+			}
+
+			if (locallyScopedConn.useMaxRows()) {
+				// 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 (this.hasLimitClause) {
+					this.results = executeInternal(this.maxRows, sendPacket,
+							createStreamingResultSet(), true,
+							(cachedMetadata == null), 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);
+					} 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);
+					}
+
+					this.results = executeInternal(-1, sendPacket,
+							createStreamingResultSet(), true,
+							(cachedMetadata == null), false);
+
+					if (oldCatalog != null) {
+						this.connection.setCatalog(oldCatalog);
+					}
+				}
+			} else {
+				this.results = executeInternal(-1, sendPacket,
+						createStreamingResultSet(), true,
+						(cachedMetadata == null), false);
+			}
+
+			if (oldCatalog != null) {
+				locallyScopedConn.setCatalog(oldCatalog);
+			}
+		}
+
+		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
+	 * executed.
+	 * 
+	 * @return either the row count for INSERT, UPDATE or DELETE; or 0 for SQL
+	 *         statements that return nothing.
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public int executeUpdate() throws SQLException {
+		return executeUpdate(true, false);
+	}
+
+	/*
+	 * We need this variant, because ServerPreparedStatement calls this for
+	 * batched updates, which will end up clobbering the warnings and generated
+	 * keys we need to gather for the batch.
+	 */
+	protected int executeUpdate(
+			boolean clearBatchedGeneratedKeysAndWarnings, boolean isBatch) throws SQLException {
+		if (clearBatchedGeneratedKeysAndWarnings) {
+			clearWarnings();
+			this.batchedGeneratedKeys = null;
+		}
+
+		return executeUpdate(this.parameterValues, this.parameterStreams,
+				this.isStream, this.streamLengths, this.isNull, isBatch);
+	}
+
+	/**
+	 * Added to allow batch-updates
+	 * 
+	 * @param batchedParameterStrings
+	 *            string values used in single statement
+	 * @param batchedParameterStreams
+	 *            stream values used in single statement
+	 * @param batchedIsStream
+	 *            flags for streams used in single statement
+	 * @param batchedStreamLengths
+	 *            lengths of streams to be read.
+	 * @param batchedIsNull
+	 *            flags for parameters that are null
+	 * 
+	 * @return the update count
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	protected int executeUpdate(byte[][] batchedParameterStrings,
+			InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
+			int[] batchedStreamLengths, boolean[] batchedIsNull, boolean isReallyBatch)
+			throws SQLException {
+
+		checkClosed();
+
+		Connection locallyScopedConn = this.connection;
+		
+		if (locallyScopedConn.isReadOnly()) {
+			throw SQLError.createSQLException(Messages.getString("PreparedStatement.34") //$NON-NLS-1$
+					+ Messages.getString("PreparedStatement.35"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if ((this.firstCharOfStmt == 'S')
+				&& StringUtils.startsWithIgnoreCaseAndWs(this.originalSql,
+						"SELECT")) { //$NON-NLS-1$
+			throw SQLError.createSQLException(Messages.getString("PreparedStatement.37"), //$NON-NLS-1$
+					"01S03"); //$NON-NLS-1$
+		}
+
+		if (this.results != null) {
+			if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) {
+				this.results.realClose(false);
+			}
+		}
+
+		ResultSet rs = null;
+
+		// The checking and changing of catalogs
+		// must happen in sequence, so synchronize
+		// on the same mutex that _conn is using
+		synchronized (locallyScopedConn.getMutex()) {
+			Buffer sendPacket = fillSendPacket(batchedParameterStrings,
+					batchedParameterStreams, batchedIsStream,
+					batchedStreamLengths);
+
+			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", -1, null, //$NON-NLS-1$
+						java.sql.ResultSet.TYPE_FORWARD_ONLY,
+						java.sql.ResultSet.CONCUR_READ_ONLY, false,
+						this.currentCatalog, true);
+			}
+
+			boolean oldInfoMsgState = false;
+
+			if (this.retrieveGeneratedKeys) {
+				oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled();
+				locallyScopedConn.setReadInfoMsgEnabled(true);
+			}
+
+			rs = executeInternal(-1, sendPacket, false, false, true, isReallyBatch);
+
+			if (this.retrieveGeneratedKeys) {
+				locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState);
+				rs.setFirstCharOfQuery(this.firstCharOfStmt);
+			}
+
+			if (oldCatalog != null) {
+				locallyScopedConn.setCatalog(oldCatalog);
+			}
+		}
+
+		this.results = rs;
+
+		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;
+	}
+
+	private String extractValuesClause() throws SQLException {
+		if (this.batchedValuesClause == null) {
+			String quoteCharStr = this.connection.getMetaData()
+					.getIdentifierQuoteString();
+	
+			int indexOfValues = -1;
+	
+			if (quoteCharStr.length() > 0) {
+				indexOfValues = StringUtils.indexOfIgnoreCaseRespectQuotes(0,
+						this.originalSql, "VALUES ", quoteCharStr.charAt(0), false);
+			} else {
+				indexOfValues = StringUtils.indexOfIgnoreCase(0, this.originalSql,
+						"VALUES ");
+			}
+	
+			if (indexOfValues == -1) {
+				return null;
+			}
+	
+			int indexOfFirstParen = this.originalSql
+					.indexOf('(', indexOfValues + 7);
+	
+			if (indexOfFirstParen == -1) {
+				return null;
+			}
+	
+			int indexOfLastParen = this.originalSql.lastIndexOf(')');
+	
+			if (indexOfLastParen == -1) {
+				return null;
+			}
+	
+			this.batchedValuesClause = this.originalSql.substring(indexOfFirstParen,
+					indexOfLastParen + 1);
+		}
+			
+		return this.batchedValuesClause;
+	}
+
+	/**
+	 * Creates the packet that contains the query to be sent to the server.
+	 * 
+	 * @return A Buffer filled with the query representing the
+	 *         PreparedStatement.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	protected Buffer fillSendPacket() throws SQLException {
+		return fillSendPacket(this.parameterValues, this.parameterStreams,
+				this.isStream, this.streamLengths);
+	}
+
+	/**
+	 * Creates the packet that contains the query to be sent to the server.
+	 * 
+	 * @param batchedParameterStrings
+	 *            the parameters as strings
+	 * @param batchedParameterStreams
+	 *            the parameters as streams
+	 * @param batchedIsStream
+	 *            is the given parameter a stream?
+	 * @param batchedStreamLengths
+	 *            the lengths of the streams (if appropriate)
+	 * 
+	 * @return a Buffer filled with the query that represents this statement
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	protected Buffer fillSendPacket(byte[][] batchedParameterStrings,
+			InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
+			int[] batchedStreamLengths) throws SQLException {
+		Buffer sendPacket = this.connection.getIO().getSharedSendPacket();
+
+		sendPacket.clear();
+
+		sendPacket.writeByte((byte) MysqlDefs.QUERY);
+
+		boolean useStreamLengths = this.connection
+				.getUseStreamLengthsInPrepStmts();
+
+		//
+		// Try and get this allocation as close as possible
+		// for BLOBs
+		//
+		int ensurePacketSize = 0;
+
+		for (int i = 0; i < batchedParameterStrings.length; i++) {
+			if (batchedIsStream[i] && useStreamLengths) {
+				ensurePacketSize += batchedStreamLengths[i];
+			}
+		}
+
+		if (ensurePacketSize != 0) {
+			sendPacket.ensureCapacity(ensurePacketSize);
+		}
+
+		for (int i = 0; i < batchedParameterStrings.length; i++) {
+			if ((batchedParameterStrings[i] == null)
+					&& (batchedParameterStreams[i] == null)) {
+				throw SQLError.createSQLException(Messages
+						.getString("PreparedStatement.40") //$NON-NLS-1$
+						+ (i + 1), SQLError.SQL_STATE_WRONG_NO_OF_PARAMETERS);
+			}
+
+			sendPacket.writeBytesNoNull(this.staticSqlStrings[i]);
+
+			if (batchedIsStream[i]) {
+				streamToBytes(sendPacket, batchedParameterStreams[i], true,
+						batchedStreamLengths[i], useStreamLengths);
+			} else {
+				sendPacket.writeBytesNoNull(batchedParameterStrings[i]);
+			}
+		}
+
+		sendPacket
+				.writeBytesNoNull(this.staticSqlStrings[batchedParameterStrings.length]);
+
+		return sendPacket;
+	}
+
+	private String generateBatchedInsertSQL(String valuesClause, int numBatches) {
+		StringBuffer newStatementSql = new StringBuffer(this.originalSql
+				.length()
+				+ (numBatches * (valuesClause.length() + 1)));
+
+		newStatementSql.append(this.originalSql);
+
+		for (int i = 0; i < numBatches - 1; i++) {
+			newStatementSql.append(',');
+			newStatementSql.append(valuesClause);
+		}
+
+		return newStatementSql.toString();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param parameterIndex
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public byte[] getBytesRepresentation(int parameterIndex)
+			throws SQLException {
+		if (this.isStream[parameterIndex]) {
+			return streamToBytes(this.parameterStreams[parameterIndex], false,
+					this.streamLengths[parameterIndex], this.connection
+							.getUseStreamLengthsInPrepStmts());
+		}
+
+		byte[] parameterVal = this.parameterValues[parameterIndex];
+
+		if (parameterVal == null) {
+			return null;
+		}
+
+		if ((parameterVal[0] == '\'')
+				&& (parameterVal[parameterVal.length - 1] == '\'')) {
+			byte[] valNoQuotes = new byte[parameterVal.length - 2];
+			System.arraycopy(parameterVal, 1, valNoQuotes, 0,
+					parameterVal.length - 2);
+
+			return valNoQuotes;
+		}
+
+		return parameterVal;
+	}
+
+	// --------------------------JDBC 2.0-----------------------------
+
+	private final String getDateTimePattern(String dt, boolean toTime)
+			throws Exception {
+		//
+		// Special case
+		//
+		int dtLength = (dt != null) ? dt.length() : 0;
+
+		if ((dtLength >= 8) && (dtLength <= 10)) {
+			int dashCount = 0;
+			boolean isDateOnly = true;
+
+			for (int i = 0; i < dtLength; i++) {
+				char c = dt.charAt(i);
+
+				if (!Character.isDigit(c) && (c != '-')) {
+					isDateOnly = false;
+
+					break;
+				}
+
+				if (c == '-') {
+					dashCount++;
+				}
+			}
+
+			if (isDateOnly && (dashCount == 2)) {
+				return "yyyy-MM-dd"; //$NON-NLS-1$
+			}
+		}
+
+		//
+		// Special case - time-only
+		//
+		boolean colonsOnly = true;
+
+		for (int i = 0; i < dtLength; i++) {
+			char c = dt.charAt(i);
+
+			if (!Character.isDigit(c) && (c != ':')) {
+				colonsOnly = false;
+
+				break;
+			}
+		}
+
+		if (colonsOnly) {
+			return "HH:mm:ss"; //$NON-NLS-1$
+		}
+
+		int n;
+		int z;
+		int count;
+		int maxvecs;
+		char c;
+		char separator;
+		StringReader reader = new StringReader(dt + " "); //$NON-NLS-1$
+		ArrayList vec = new ArrayList();
+		ArrayList vecRemovelist = new ArrayList();
+		Object[] nv = new Object[3];
+		Object[] v;
+		nv[0] = new Character('y');
+		nv[1] = new StringBuffer();
+		nv[2] = new Integer(0);
+		vec.add(nv);
+
+		if (toTime) {
+			nv = new Object[3];
+			nv[0] = new Character('h');
+			nv[1] = new StringBuffer();
+			nv[2] = new Integer(0);
+			vec.add(nv);
+		}
+
+		while ((z = reader.read()) != -1) {
+			separator = (char) z;
+			maxvecs = vec.size();
+
+			for (count = 0; count < maxvecs; count++) {
+				v = (Object[]) vec.get(count);
+				n = ((Integer) v[2]).intValue();
+				c = getSuccessor(((Character) v[0]).charValue(), n);
+
+				if (!Character.isLetterOrDigit(separator)) {
+					if ((c == ((Character) v[0]).charValue()) && (c != 'S')) {
+						vecRemovelist.add(v);
+					} else {
+						((StringBuffer) v[1]).append(separator);
+
+						if ((c == 'X') || (c == 'Y')) {
+							v[2] = new Integer(4);
+						}
+					}
+				} else {
+					if (c == 'X') {
+						c = 'y';
+						nv = new Object[3];
+						nv[1] = (new StringBuffer(((StringBuffer) v[1])
+								.toString())).append('M');
+						nv[0] = new Character('M');
+						nv[2] = new Integer(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);
+						vec.add(nv);
+					}
+
+					((StringBuffer) v[1]).append(c);
+
+					if (c == ((Character) v[0]).charValue()) {
+						v[2] = new Integer(n + 1);
+					} else {
+						v[0] = new Character(c);
+						v[2] = new Integer(1);
+					}
+				}
+			}
+
+			int size = vecRemovelist.size();
+
+			for (int i = 0; i < size; i++) {
+				v = (Object[]) vecRemovelist.get(i);
+				vec.remove(v);
+			}
+
+			vecRemovelist.clear();
+		}
+
+		int size = vec.size();
+
+		for (int i = 0; i < size; i++) {
+			v = (Object[]) vec.get(i);
+			c = ((Character) v[0]).charValue();
+			n = ((Integer) v[2]).intValue();
+
+			boolean bk = getSuccessor(c, n) != c;
+			boolean atEnd = (((c == 's') || (c == 'm') || ((c == 'h') && toTime)) && bk);
+			boolean finishesAtDate = (bk && (c == 'd') && !toTime);
+			boolean containsEnd = (((StringBuffer) v[1]).toString()
+					.indexOf('W') != -1);
+
+			if ((!atEnd && !finishesAtDate) || (containsEnd)) {
+				vecRemovelist.add(v);
+			}
+		}
+
+		size = vecRemovelist.size();
+
+		for (int i = 0; i < size; i++) {
+			vec.remove(vecRemovelist.get(i));
+		}
+
+		vecRemovelist.clear();
+		v = (Object[]) vec.get(0); // might throw exception
+
+		StringBuffer format = (StringBuffer) v[1];
+		format.setLength(format.length() - 1);
+
+		return format.toString();
+	}
+
+	/**
+	 * The number, types and properties of a ResultSet's columns are provided by
+	 * the getMetaData method.
+	 * 
+	 * @return the description of a ResultSet's columns
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public java.sql.ResultSetMetaData getMetaData()
+			throws SQLException {
+
+		if (!StringUtils.startsWithIgnoreCaseAndNonAlphaNumeric(
+				this.originalSql, "SELECT")) {
+			return null;
+		}
+
+		PreparedStatement mdStmt = null;
+		java.sql.ResultSet mdRs = null;
+
+		if (this.pstmtResultMetaData == null) {
+			try {
+				mdStmt = new PreparedStatement(this.connection,
+						this.originalSql, this.currentCatalog, this.parseInfo);
+
+				mdStmt.setMaxRows(0);
+
+				int paramCount = this.parameterValues.length;
+
+				for (int i = 1; i <= paramCount; i++) {
+					mdStmt.setString(i, ""); //$NON-NLS-1$
+				}
+
+				boolean hadResults = mdStmt.execute();
+
+				if (hadResults) {
+					mdRs = mdStmt.getResultSet();
+
+					this.pstmtResultMetaData = mdRs.getMetaData();
+				} else {
+					this.pstmtResultMetaData = new ResultSetMetaData(
+							new Field[0], 
+							this.connection.getUseOldAliasMetadataBehavior());
+				}
+			} finally {
+				SQLException sqlExRethrow = null;
+
+				if (mdRs != null) {
+					try {
+						mdRs.close();
+					} catch (SQLException sqlEx) {
+						sqlExRethrow = sqlEx;
+					}
+
+					mdRs = null;
+				}
+
+				if (mdStmt != null) {
+					try {
+						mdStmt.close();
+					} catch (SQLException sqlEx) {
+						sqlExRethrow = sqlEx;
+					}
+
+					mdStmt = null;
+				}
+
+				if (sqlExRethrow != null) {
+					throw sqlExRethrow;
+				}
+			}
+		}
+
+		return this.pstmtResultMetaData;
+	}
+
+	/**
+	 * @see PreparedStatement#getParameterMetaData()
+	 */
+	public ParameterMetaData getParameterMetaData() 
+		throws SQLException {
+		if (this.parameterMetaData == null) {
+			this.parameterMetaData = new MysqlParameterMetadata(
+					null, this.parameterCount);
+		}
+		
+		return this.parameterMetaData;
+	}
+
+	ParseInfo getParseInfo() {
+		return this.parseInfo;
+	}
+
+	private final char getSuccessor(char c, int n) {
+		return ((c == 'y') && (n == 2)) ? 'X'
+				: (((c == 'y') && (n < 4)) ? 'y'
+						: ((c == 'y') ? 'M'
+								: (((c == 'M') && (n == 2)) ? 'Y'
+										: (((c == 'M') && (n < 3)) ? 'M'
+												: ((c == 'M') ? 'd'
+														: (((c == 'd') && (n < 2)) ? 'd'
+																: ((c == 'd') ? 'H'
+																		: (((c == 'H') && (n < 2)) ? 'H'
+																				: ((c == 'H') ? 'm'
+																						: (((c == 'm') && (n < 2)) ? 'm'
+																								: ((c == 'm') ? 's'
+																										: (((c == 's') && (n < 2)) ? 's'
+																												: 'W'))))))))))));
+	}
+
+	/**
+	 * Used to escape binary data with hex for mb charsets
+	 * 
+	 * @param buf
+	 * @param packet
+	 * @param size
+	 * @throws SQLException
+	 */
+	private final void hexEscapeBlock(byte[] buf, Buffer packet, int size)
+			throws SQLException {
+		for (int i = 0; i < size; i++) {
+			byte b = buf[i];
+			int lowBits = (b & 0xff) / 16;
+			int highBits = (b & 0xff) % 16;
+
+			packet.writeByte(HEX_DIGITS[lowBits]);
+			packet.writeByte(HEX_DIGITS[highBits]);
+		}
+	}
+
+	private void initializeFromParseInfo() throws SQLException {
+		this.staticSqlStrings = this.parseInfo.staticSql;
+		this.hasLimitClause = this.parseInfo.foundLimitClause;
+		this.isLoadDataQuery = this.parseInfo.foundLoadData;
+		this.firstCharOfStmt = this.parseInfo.firstStmtChar;
+
+		this.parameterCount = this.staticSqlStrings.length - 1;
+
+		this.parameterValues = new byte[this.parameterCount][];
+		this.parameterStreams = new InputStream[this.parameterCount];
+		this.isStream = new boolean[this.parameterCount];
+		this.streamLengths = new int[this.parameterCount];
+		this.isNull = new boolean[this.parameterCount];
+
+		clearParameters();
+
+		for (int j = 0; j < this.parameterCount; j++) {
+			this.isStream[j] = false;
+		}
+	}
+
+	boolean isNull(int paramIndex) {
+		return this.isNull[paramIndex];
+	}
+
+	private final int readblock(InputStream i, byte[] b) throws SQLException {
+		try {
+			return i.read(b);
+		} catch (Throwable E) {
+			throw SQLError.createSQLException(Messages.getString("PreparedStatement.56") //$NON-NLS-1$
+					+ E.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	private final int readblock(InputStream i, byte[] b, int length)
+			throws SQLException {
+		try {
+			int lengthToRead = length;
+
+			if (lengthToRead > b.length) {
+				lengthToRead = b.length;
+			}
+
+			return i.read(b, 0, lengthToRead);
+		} catch (Throwable E) {
+			throw SQLError.createSQLException(Messages.getString("PreparedStatement.55") //$NON-NLS-1$
+					+ E.getClass().getName(), SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	/**
+	 * Closes this statement, releasing all resources
+	 * 
+	 * @param calledExplicitly
+	 *            was this called by close()?
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected void realClose(boolean calledExplicitly, 
+			boolean closeOpenResults) throws SQLException {
+		if (this.useUsageAdvisor) {
+			if (this.numberOfExecutions <= 1) {
+				String message = Messages.getString("PreparedStatement.43"); //$NON-NLS-1$
+
+				this.eventSink.consumeEvent(new ProfilerEvent(
+						ProfilerEvent.TYPE_WARN, "", this.currentCatalog, //$NON-NLS-1$
+						this.connectionId, this.getId(), -1, System
+								.currentTimeMillis(), 0, null,
+						this.pointOfOrigin, message));
+			}
+		}
+		
+		super.realClose(calledExplicitly, closeOpenResults);
+
+		this.dbmd = null;
+		this.originalSql = null;
+		this.staticSqlStrings = null;
+		this.parameterValues = null;
+		this.parameterStreams = null;
+		this.isStream = null;
+		this.streamLengths = null;
+		this.isNull = null;
+		this.streamConvertBuf = null;
+	}
+
+	/**
+	 * JDBC 2.0 Set an Array parameter.
+	 * 
+	 * @param i
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            an object representing an SQL array
+	 * 
+	 * @throws SQLException
+	 *             because this method is not implemented.
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 */
+	public void setArray(int i, Array x) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * When a very large ASCII value is input to a LONGVARCHAR parameter, it may
+	 * be more practical to send it via a java.io.InputStream. 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 ASCII 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...
+	 * @param x
+	 *            the parameter value
+	 * @param length
+	 *            the number of bytes in the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setAsciiStream(int parameterIndex, InputStream x,
+			int length) throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.VARCHAR);
+		} else {
+			setBinaryStream(parameterIndex, x, length);
+		}
+	}
+
+	/**
+	 * Set a parameter to a java.math.BigDecimal value. The driver converts this
+	 * to a SQL NUMERIC value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setBigDecimal(int parameterIndex, BigDecimal x)
+			throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.DECIMAL);
+		} else {
+			setInternal(parameterIndex, StringUtils
+					.fixDecimalExponent(StringUtils.consistentToString(x)));
+		}
+	}
+
+	/**
+	 * When a very large binary value is input to a LONGVARBINARY parameter, it
+	 * may be more practical to send it via a java.io.InputStream. JDBC will
+	 * read the data from the stream as needed, until it reaches end-of-file.
+	 * 
+	 * <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...
+	 * @param x
+	 *            the parameter value
+	 * @param length
+	 *            the number of bytes to read from the stream (ignored)
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public void setBinaryStream(int parameterIndex, InputStream x, int length)
+			throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			int parameterIndexOffset = getParameterIndexOffset();
+			
+			if ((parameterIndex < 1)
+					|| (parameterIndex > this.staticSqlStrings.length)) {
+				throw SQLError.createSQLException(
+						Messages.getString("PreparedStatement.2") //$NON-NLS-1$
+								+ parameterIndex
+								+ Messages.getString("PreparedStatement.3") + this.staticSqlStrings.length + Messages.getString("PreparedStatement.4"), //$NON-NLS-1$ //$NON-NLS-2$
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			} else if (parameterIndexOffset == -1 && parameterIndex == 1) {
+				throw SQLError.createSQLException("Can't set IN parameter for return value of stored function call.", 
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+
+
+			this.parameterStreams[parameterIndex - 1 + parameterIndexOffset] = x;
+			this.isStream[parameterIndex - 1 + parameterIndexOffset] = true;
+			this.streamLengths[parameterIndex - 1 + parameterIndexOffset] = length;
+			this.isNull[parameterIndex - 1 + parameterIndexOffset] = false;
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Set a BLOB parameter.
+	 * 
+	 * @param i
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            an object representing a BLOB
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void setBlob(int i, java.sql.Blob x) throws SQLException {
+		if (x == null) {
+			setNull(i, Types.BLOB);
+		} else {
+			ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+
+			bytesOut.write('\'');
+			escapeblockFast(x.getBytes(1, (int) x.length()), bytesOut, (int) x
+					.length());
+			bytesOut.write('\'');
+
+			setInternal(i, bytesOut.toByteArray());
+		}
+	}
+
+	/**
+	 * Set a parameter to a Java boolean value. The driver converts this to a
+	 * SQL BIT value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public void setBoolean(int parameterIndex, boolean x) throws SQLException {
+		if (this.useTrueBoolean) {
+			setInternal(parameterIndex, x ? "1" : "0"); //$NON-NLS-1$ //$NON-NLS-2$
+		} else {
+			setInternal(parameterIndex, x ? "'t'" : "'f'"); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	/**
+	 * Set a parameter to a Java byte value. The driver converts this to a SQL
+	 * TINYINT value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setByte(int parameterIndex, byte x) throws SQLException {
+		setInternal(parameterIndex, String.valueOf(x));
+	}
+
+	/**
+	 * Set a parameter to a Java array of bytes. The driver converts this to a
+	 * SQL VARBINARY or LONGVARBINARY (depending on the argument's size relative
+	 * to the driver's limits on VARBINARYs) when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setBytes(int parameterIndex, byte[] x) throws SQLException {
+		setBytes(parameterIndex, x, true, true);
+	}
+
+	protected void setBytes(int parameterIndex, byte[] x,
+			boolean checkForIntroducer, boolean escapeForMBChars)
+			throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			String connectionEncoding = this.connection.getEncoding();
+
+			if (this.connection.isNoBackslashEscapesSet()
+					|| (escapeForMBChars 
+							&& this.connection.getUseUnicode()
+							&& connectionEncoding != null
+							&& CharsetMapping.isMultibyteCharset(connectionEncoding))) {
+
+				// Send as hex
+
+				ByteArrayOutputStream bOut = new ByteArrayOutputStream(
+						(x.length * 2) + 3);
+				bOut.write('x');
+				bOut.write('\'');
+
+				for (int i = 0; i < x.length; i++) {
+					int lowBits = (x[i] & 0xff) / 16;
+					int highBits = (x[i] & 0xff) % 16;
+
+					bOut.write(HEX_DIGITS[lowBits]);
+					bOut.write(HEX_DIGITS[highBits]);
+				}
+
+				bOut.write('\'');
+
+				setInternal(parameterIndex, bOut.toByteArray());
+
+				return;
+			}
+
+			// escape them
+			int numBytes = x.length;
+
+			int pad = 2;
+
+			boolean needsIntroducer = checkForIntroducer
+					&& this.connection.versionMeetsMinimum(4, 1, 0);
+
+			if (needsIntroducer) {
+				pad += 7;
+			}
+
+			ByteArrayOutputStream bOut = new ByteArrayOutputStream(numBytes
+					+ pad);
+
+			if (needsIntroducer) {
+				bOut.write('_');
+				bOut.write('b');
+				bOut.write('i');
+				bOut.write('n');
+				bOut.write('a');
+				bOut.write('r');
+				bOut.write('y');
+			}
+			bOut.write('\'');
+
+			for (int i = 0; i < numBytes; ++i) {
+				byte b = x[i];
+
+				switch (b) {
+				case 0: /* Must be escaped for 'mysql' */
+					bOut.write('\\');
+					bOut.write('0');
+
+					break;
+
+				case '\n': /* Must be escaped for logs */
+					bOut.write('\\');
+					bOut.write('n');
+
+					break;
+
+				case '\r':
+					bOut.write('\\');
+					bOut.write('r');
+
+					break;
+
+				case '\\':
+					bOut.write('\\');
+					bOut.write('\\');
+
+					break;
+
+				case '\'':
+					bOut.write('\\');
+					bOut.write('\'');
+
+					break;
+
+				case '"': /* Better safe than sorry */
+					bOut.write('\\');
+					bOut.write('"');
+
+					break;
+
+				case '\032': /* This gives problems on Win32 */
+					bOut.write('\\');
+					bOut.write('Z');
+
+					break;
+
+				default:
+					bOut.write(b);
+				}
+			}
+
+			bOut.write('\'');
+
+			setInternal(parameterIndex, bOut.toByteArray());
+		}
+	}
+
+	/**
+	 * Used by updatable result sets for refreshRow() because the parameter has
+	 * already been escaped for updater or inserter prepared statements.
+	 * 
+	 * @param parameterIndex
+	 *            the parameter to set.
+	 * @param parameterAsBytes
+	 *            the parameter as a string.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected void setBytesNoEscape(int parameterIndex, byte[] parameterAsBytes)
+			throws SQLException {
+		byte[] parameterWithQuotes = new byte[parameterAsBytes.length + 2];
+		parameterWithQuotes[0] = '\'';
+		System.arraycopy(parameterAsBytes, 0, parameterWithQuotes, 1,
+				parameterAsBytes.length);
+		parameterWithQuotes[parameterAsBytes.length + 1] = '\'';
+
+		setInternal(parameterIndex, parameterWithQuotes);
+	}
+
+	protected void setBytesNoEscapeNoQuotes(int parameterIndex,
+			byte[] parameterAsBytes) throws SQLException {
+		setInternal(parameterIndex, parameterAsBytes);
+	}
+
+	/**
+	 * 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 setCharacterStream(int parameterIndex, java.io.Reader reader,
+			int length) throws SQLException {
+		try {
+			if (reader == null) {
+				setNull(parameterIndex, Types.LONGVARCHAR);
+			} else {
+				char[] c = null;
+				int len = 0;
+
+				boolean useLength = this.connection
+						.getUseStreamLengthsInPrepStmts();
+
+				String forcedEncoding = this.connection.getClobCharacterEncoding();
+				
+				if (useLength && (length != -1)) {
+					c = new char[length];
+
+					int numCharsRead = readFully(reader, c, length); // blocks
+					// until
+					// all
+					// read
+
+					if (forcedEncoding == null) {
+						setString(parameterIndex, new String(c, 0, numCharsRead));
+					} else {
+						try {
+							setBytes(parameterIndex, new String(c, 
+									0, 
+									numCharsRead).getBytes(forcedEncoding));
+						} catch (UnsupportedEncodingException uee) {
+							throw SQLError.createSQLException("Unsupported character encoding " + 
+									forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+						}
+					}
+				} else {
+					c = new char[4096];
+
+					StringBuffer buf = new StringBuffer();
+
+					while ((len = reader.read(c)) != -1) {
+						buf.append(c, 0, len);
+					}
+
+					if (forcedEncoding == null) {
+						setString(parameterIndex, buf.toString());
+					} else {
+						try {
+							setBytes(parameterIndex, 
+									buf.toString().getBytes(forcedEncoding));
+						} catch (UnsupportedEncodingException uee) {
+							throw SQLError.createSQLException("Unsupported character encoding " + 
+									forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+						}
+					}
+				}
+			}
+		} catch (java.io.IOException ioEx) {
+			throw SQLError.createSQLException(ioEx.toString(),
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	/**
+	 * JDBC 2.0 Set a CLOB parameter.
+	 * 
+	 * @param i
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            an object representing a CLOB
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void setClob(int i, Clob x) throws SQLException {
+		if (x == null) {
+			setNull(i, Types.CLOB);
+
+			return;
+		}
+
+		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);
+			}
+		}
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Date value. The driver converts this to a
+	 * SQL DATE value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception java.sql.SQLException
+	 *                if a database access error occurs
+	 */
+	public void setDate(int parameterIndex, java.sql.Date x)
+			throws java.sql.SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.DATE);
+		} else {
+			// FIXME: Have instance version of this, problem as it's
+			// not thread-safe :(
+			SimpleDateFormat dateFormatter = new SimpleDateFormat(
+					"''yyyy-MM-dd''", Locale.US); //$NON-NLS-1$
+			setInternal(parameterIndex, dateFormatter.format(x));
+		}
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Date value. The driver converts this to a
+	 * SQL DATE value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            the parameter value
+	 * @param cal
+	 *            the calendar to interpret the date with
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public void setDate(int parameterIndex, java.sql.Date x, Calendar cal)
+			throws SQLException {
+		setDate(parameterIndex, x);
+	}
+
+	/**
+	 * Set a parameter to a Java double value. The driver converts this to a SQL
+	 * DOUBLE value when it sends it to the database
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setDouble(int parameterIndex, double x) throws SQLException {
+
+		if (!this.connection.getAllowNanAndInf()
+				&& (x == Double.POSITIVE_INFINITY
+						|| x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) {
+			throw SQLError.createSQLException("'" + x
+					+ "' is not a valid numeric or approximate numeric value",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+
+		}
+
+		setInternal(parameterIndex, StringUtils.fixDecimalExponent(String
+				.valueOf(x)));
+	}
+
+	/**
+	 * Set a parameter to a Java float value. The driver converts this to a SQL
+	 * FLOAT value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setFloat(int parameterIndex, float x) throws SQLException {
+		setInternal(parameterIndex, StringUtils.fixDecimalExponent(String
+				.valueOf(x)));
+	}
+
+	/**
+	 * Set a parameter to a Java int value. The driver converts this to a SQL
+	 * INTEGER value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setInt(int parameterIndex, int x) throws SQLException {
+		setInternal(parameterIndex, String.valueOf(x));
+	}
+
+	private 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();
+		
+		if ((paramIndex < 1)) {
+			throw SQLError.createSQLException(
+					Messages.getString("PreparedStatement.49") //$NON-NLS-1$
+							+ paramIndex
+							+ Messages.getString("PreparedStatement.50"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		} else if (paramIndex > this.parameterCount) {
+			throw SQLError.createSQLException(
+					Messages.getString("PreparedStatement.51") //$NON-NLS-1$
+							+ paramIndex
+							+ Messages.getString("PreparedStatement.52") + (this.parameterValues.length) + Messages.getString("PreparedStatement.53"), //$NON-NLS-1$ //$NON-NLS-2$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} else if (parameterIndexOffset == -1 && paramIndex == 1) {
+			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)
+			throws SQLException {
+		checkClosed();
+		
+		byte[] parameterAsBytes = null;
+
+		if (this.charConverter != null) {
+			parameterAsBytes = this.charConverter.toBytes(val);
+		} else {
+			parameterAsBytes = StringUtils.getBytes(val, this.charConverter,
+					this.charEncoding, this.connection
+							.getServerCharacterEncoding(), this.connection
+							.parserKnowsUnicode());
+		}
+
+		setInternal(paramIndex, parameterAsBytes);
+	}
+
+	/**
+	 * Set a parameter to a Java long value. The driver converts this to a SQL
+	 * BIGINT value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setLong(int parameterIndex, long x) throws SQLException {
+		setInternal(parameterIndex, String.valueOf(x));
+	}
+
+	/**
+	 * Set a parameter to SQL NULL
+	 * 
+	 * <p>
+	 * <B>Note:</B> You must specify the parameters SQL type (although MySQL
+	 * ignores it)
+	 * </p>
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, etc...
+	 * @param sqlType
+	 *            the SQL type code defined in java.sql.Types
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setNull(int parameterIndex, int sqlType) throws SQLException {
+		setInternal(parameterIndex, "null"); //$NON-NLS-1$
+		this.isNull[parameterIndex - 1] = true;
+	}
+
+	/**
+	 * Set a parameter to SQL NULL.
+	 * 
+	 * <P>
+	 * <B>Note:</B> You must specify the parameter's SQL type.
+	 * </p>
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param sqlType
+	 *            SQL type code defined by java.sql.Types
+	 * @param arg
+	 *            argument parameters for null
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public void setNull(int parameterIndex, int sqlType, String arg)
+			throws SQLException {
+		setNull(parameterIndex, sqlType);
+	}
+
+	private void setNumericObject(int parameterIndex, Object parameterObj, int targetSqlType, int scale) throws SQLException {
+		Number parameterAsNum;
+
+		if (parameterObj instanceof Boolean) {
+			parameterAsNum = ((Boolean) parameterObj)
+					.booleanValue() ? new Integer(1) : new Integer(
+					0);
+		} else if (parameterObj instanceof String) {
+			switch (targetSqlType) {
+			case Types.BIT:
+				boolean parameterAsBoolean = "true"
+						.equalsIgnoreCase((String) parameterObj);
+
+				parameterAsNum = parameterAsBoolean ? new Integer(1)
+						: new Integer(0);
+
+				break;
+
+			case Types.TINYINT:
+			case Types.SMALLINT:
+			case Types.INTEGER:
+				parameterAsNum = Integer
+						.valueOf((String) parameterObj);
+
+				break;
+
+			case Types.BIGINT:
+				parameterAsNum = Long
+						.valueOf((String) parameterObj);
+
+				break;
+
+			case Types.REAL:
+				parameterAsNum = Float
+						.valueOf((String) parameterObj);
+
+				break;
+
+			case Types.FLOAT:
+			case Types.DOUBLE:
+				parameterAsNum = Double
+						.valueOf((String) parameterObj);
+
+				break;
+
+			case Types.DECIMAL:
+			case Types.NUMERIC:
+			default:
+				parameterAsNum = new java.math.BigDecimal(
+						(String) parameterObj);
+			}
+		} else {
+			parameterAsNum = (Number) parameterObj;
+		}
+
+		switch (targetSqlType) {
+		case Types.BIT:
+		case Types.TINYINT:
+		case Types.SMALLINT:
+		case Types.INTEGER:
+			setInt(parameterIndex, parameterAsNum.intValue());
+
+			break;
+
+		case Types.BIGINT:
+			setLong(parameterIndex, parameterAsNum.longValue());
+
+			break;
+
+		case Types.REAL:
+			setFloat(parameterIndex, parameterAsNum.floatValue());
+
+			break;
+
+		case Types.FLOAT:
+		case Types.DOUBLE:
+			setDouble(parameterIndex, parameterAsNum.doubleValue());
+
+			break;
+
+		case Types.DECIMAL:
+		case Types.NUMERIC:
+
+			if (parameterAsNum instanceof java.math.BigDecimal) {
+				BigDecimal scaledBigDecimal = null;
+
+				try {
+					scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum)
+							.setScale(scale);
+				} catch (ArithmeticException ex) {
+					try {
+						scaledBigDecimal = ((java.math.BigDecimal) parameterAsNum)
+								.setScale(scale,
+										BigDecimal.ROUND_HALF_UP);
+					} catch (ArithmeticException arEx) {
+						throw SQLError.createSQLException(
+								"Can't set scale of '"
+										+ scale
+										+ "' for DECIMAL argument '"
+										+ parameterAsNum + "'",
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+				}
+
+				setBigDecimal(parameterIndex, scaledBigDecimal);
+			} else if (parameterAsNum instanceof java.math.BigInteger) {
+				setBigDecimal(
+						parameterIndex,
+						new java.math.BigDecimal(
+								(java.math.BigInteger) parameterAsNum,
+								scale));
+			} else {
+				setBigDecimal(parameterIndex,
+						new java.math.BigDecimal(parameterAsNum
+								.doubleValue()));
+			}
+
+			break;
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param parameterIndex
+	 *            DOCUMENT ME!
+	 * @param parameterObj
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void setObject(int parameterIndex, Object parameterObj)
+			throws SQLException {
+		if (parameterObj == null) {
+			setNull(parameterIndex, java.sql.Types.OTHER);
+		} else {
+			if (parameterObj instanceof Byte) {
+				setInt(parameterIndex, ((Byte) parameterObj).intValue());
+			} else if (parameterObj instanceof String) {
+				setString(parameterIndex, (String) parameterObj);
+			} else if (parameterObj instanceof BigDecimal) {
+				setBigDecimal(parameterIndex, (BigDecimal) parameterObj);
+			} else if (parameterObj instanceof Short) {
+				setShort(parameterIndex, ((Short) parameterObj).shortValue());
+			} else if (parameterObj instanceof Integer) {
+				setInt(parameterIndex, ((Integer) parameterObj).intValue());
+			} else if (parameterObj instanceof Long) {
+				setLong(parameterIndex, ((Long) parameterObj).longValue());
+			} else if (parameterObj instanceof Float) {
+				setFloat(parameterIndex, ((Float) parameterObj).floatValue());
+			} else if (parameterObj instanceof Double) {
+				setDouble(parameterIndex, ((Double) parameterObj).doubleValue());
+			} else if (parameterObj instanceof byte[]) {
+				setBytes(parameterIndex, (byte[]) parameterObj);
+			} else if (parameterObj instanceof java.sql.Date) {
+				setDate(parameterIndex, (java.sql.Date) parameterObj);
+			} else if (parameterObj instanceof Time) {
+				setTime(parameterIndex, (Time) parameterObj);
+			} else if (parameterObj instanceof Timestamp) {
+				setTimestamp(parameterIndex, (Timestamp) parameterObj);
+			} else if (parameterObj instanceof Boolean) {
+				setBoolean(parameterIndex, ((Boolean) parameterObj)
+						.booleanValue());
+			} else if (parameterObj instanceof InputStream) {
+				setBinaryStream(parameterIndex, (InputStream) parameterObj, -1);
+			} else if (parameterObj instanceof java.sql.Blob) {
+				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) {
+				setTimestamp(parameterIndex, new Timestamp(
+						((java.util.Date) parameterObj).getTime()));
+			} else if (parameterObj instanceof BigInteger) {
+				setString(parameterIndex, parameterObj.toString());
+			} else {
+				setSerializableObject(parameterIndex, parameterObj);
+			}
+		}
+	}
+	
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param parameterIndex
+	 *            DOCUMENT ME!
+	 * @param parameterObj
+	 *            DOCUMENT ME!
+	 * @param targetSqlType
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void setObject(int parameterIndex, Object parameterObj,
+			int targetSqlType) throws SQLException {
+		if (!(parameterObj instanceof BigDecimal)) {
+			setObject(parameterIndex, parameterObj, targetSqlType, 0);
+		} else {
+			setObject(parameterIndex, parameterObj, targetSqlType,
+					((BigDecimal)parameterObj).scale());
+		}
+	}
+
+	/**
+	 * Set the value of a parameter using an object; use the java.lang
+	 * equivalent objects for integral values.
+	 * 
+	 * <P>
+	 * The given Java object will be converted to the targetSqlType before being
+	 * sent to the database.
+	 * </p>
+	 * 
+	 * <P>
+	 * note that this method may be used to pass database-specific abstract data
+	 * types. This is done by using a Driver-specific Java type and using a
+	 * targetSqlType of java.sql.Types.OTHER
+	 * </p>
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param parameterObj
+	 *            the object containing the input parameter value
+	 * @param targetSqlType
+	 *            The SQL type to be send to the database
+	 * @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.
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public void setObject(int parameterIndex, Object parameterObj,
+			int targetSqlType, int scale) throws SQLException {
+		if (parameterObj == null) {
+			setNull(parameterIndex, java.sql.Types.OTHER);
+		} else {
+			try {
+				switch (targetSqlType) {
+				case Types.BOOLEAN:
+					/*
+					 From Table-B5 in the JDBC-3.0 Spec
+				
+					        T S I B R F D D N B B C V L
+					        I M N I E L O E U I O H A O
+					        N A T G A O U C M T O A R N
+					        Y L E I L A B I E   L R C G
+					        I L G N   T L M R   E   H V
+					        N I E T     E A I   A   A A
+					        T N R         L C   N   R R
+					          T                       C
+                                                      H
+				                                      A
+					                                  R
+					-----------------------------------
+					Boolean x x x x x x x x x x x x x x
+					*/
+					
+					if (parameterObj instanceof Boolean) {
+						setBoolean(parameterIndex, ((Boolean)parameterObj).booleanValue());
+						
+						break;
+					} else if (parameterObj instanceof String) {
+						setBoolean(parameterIndex, "true".equalsIgnoreCase((String)parameterObj) ||
+								!"0".equalsIgnoreCase((String)parameterObj));
+						
+						break;
+					} else if (parameterObj instanceof Number) {
+						int intValue = ((Number)parameterObj).intValue();
+						
+						setBoolean(parameterIndex, intValue != 0);
+						
+						break;
+					} else {
+						throw SQLError.createSQLException("No conversion from " + parameterObj.getClass().getName() + 
+								" to Types.BOOLEAN possible.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+					}
+					
+					
+				case Types.BIT:
+				case Types.TINYINT:
+				case Types.SMALLINT:
+				case Types.INTEGER:
+				case Types.BIGINT:
+				case Types.REAL:
+				case Types.FLOAT:
+				case Types.DOUBLE:
+				case Types.DECIMAL:
+				case Types.NUMERIC:
+
+					setNumericObject(parameterIndex, parameterObj, targetSqlType, scale);
+
+					break;
+
+				case Types.CHAR:
+				case Types.VARCHAR:
+				case Types.LONGVARCHAR:
+					if (parameterObj instanceof BigDecimal) {
+						setString(
+								parameterIndex,
+								(StringUtils
+										.fixDecimalExponent(StringUtils
+												.consistentToString((BigDecimal) parameterObj))));
+					} else {
+						setString(parameterIndex, parameterObj.toString());
+					}
+
+					break;
+
+				case Types.CLOB:
+
+					if (parameterObj instanceof java.sql.Clob) {
+						setClob(parameterIndex, (java.sql.Clob) parameterObj);
+					} else {
+						setString(parameterIndex, parameterObj.toString());
+					}
+
+					break;
+
+				case Types.BINARY:
+				case Types.VARBINARY:
+				case Types.LONGVARBINARY:
+				case Types.BLOB:
+
+					if (parameterObj instanceof byte[]) {
+						setBytes(parameterIndex, (byte[]) parameterObj);
+					} else if (parameterObj instanceof java.sql.Blob) {
+						setBlob(parameterIndex, (java.sql.Blob) parameterObj);
+					} else {
+						setBytes(parameterIndex, StringUtils.getBytes(
+								parameterObj.toString(), this.charConverter,
+								this.charEncoding, this.connection
+										.getServerCharacterEncoding(),
+								this.connection.parserKnowsUnicode()));
+					}
+
+					break;
+
+				case Types.DATE:
+				case Types.TIMESTAMP:
+
+					java.util.Date parameterAsDate;
+
+					if (parameterObj instanceof String) {
+						ParsePosition pp = new ParsePosition(0);
+						java.text.DateFormat sdf = new java.text.SimpleDateFormat(
+								getDateTimePattern((String) parameterObj, false), Locale.US);
+						parameterAsDate = sdf.parse((String) parameterObj, pp);
+					} else {
+						parameterAsDate = (java.util.Date) parameterObj;
+					}
+
+					switch (targetSqlType) {
+					case Types.DATE:
+
+						if (parameterAsDate instanceof java.sql.Date) {
+							setDate(parameterIndex,
+									(java.sql.Date) parameterAsDate);
+						} else {
+							setDate(parameterIndex, new java.sql.Date(
+									parameterAsDate.getTime()));
+						}
+
+						break;
+
+					case Types.TIMESTAMP:
+
+						if (parameterAsDate instanceof java.sql.Timestamp) {
+							setTimestamp(parameterIndex,
+									(java.sql.Timestamp) parameterAsDate);
+						} else {
+							setTimestamp(parameterIndex,
+									new java.sql.Timestamp(parameterAsDate
+											.getTime()));
+						}
+
+						break;
+					}
+
+					break;
+
+				case Types.TIME:
+
+					if (parameterObj instanceof String) {
+						java.text.DateFormat sdf = new java.text.SimpleDateFormat(
+								getDateTimePattern((String) parameterObj, true), Locale.US);
+						setTime(parameterIndex, new java.sql.Time(sdf.parse(
+								(String) parameterObj).getTime()));
+					} else if (parameterObj instanceof Timestamp) {
+						Timestamp xT = (Timestamp) parameterObj;
+						setTime(parameterIndex, new java.sql.Time(xT.getTime()));
+					} else {
+						setTime(parameterIndex, (java.sql.Time) parameterObj);
+					}
+
+					break;
+
+				case Types.OTHER:
+					setSerializableObject(parameterIndex, parameterObj);
+
+					break;
+
+				default:
+					throw SQLError.createSQLException(Messages
+							.getString("PreparedStatement.16"), //$NON-NLS-1$
+							SQLError.SQL_STATE_GENERAL_ERROR);
+				}
+			} catch (Exception ex) {
+				if (ex instanceof SQLException) {
+					throw (SQLException) ex;
+				}
+
+				throw SQLError.createSQLException(
+						Messages.getString("PreparedStatement.17") //$NON-NLS-1$
+								+ parameterObj.getClass().toString()
+								+ Messages.getString("PreparedStatement.18") //$NON-NLS-1$
+								+ ex.getClass().getName()
+								+ Messages.getString("PreparedStatement.19") + ex.getMessage(), //$NON-NLS-1$
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		}
+	}
+
+	private int setOneBatchedParameterSet(
+			java.sql.PreparedStatement batchedStatement, int batchedParamIndex,
+			BatchParams paramArg) throws SQLException {
+		boolean[] isNullBatch = paramArg.isNull;
+		boolean[] isStreamBatch = paramArg.isStream;
+
+		for (int j = 0; j < isNullBatch.length; j++) {
+			if (isNullBatch[j]) {
+				batchedStatement.setNull(batchedParamIndex++, Types.NULL);
+			} else {
+				if (isStreamBatch[j]) {
+					batchedStatement.setBinaryStream(batchedParamIndex++,
+							paramArg.parameterStreams[j],
+							paramArg.streamLengths[j]);
+				} else {
+					((com.mysql.jdbc.PreparedStatement) batchedStatement)
+							.setBytesNoEscapeNoQuotes(batchedParamIndex++,
+									paramArg.parameterStrings[j]);
+				}
+			}
+		}
+
+		return batchedParamIndex;
+	}
+
+	/**
+	 * JDBC 2.0 Set a REF(&lt;structured-type&gt;) parameter.
+	 * 
+	 * @param i
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            an object representing data of an SQL REF Type
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 */
+	public void setRef(int i, Ref x) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * 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;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param retrieveGeneratedKeys
+	 */
+	protected void setRetrieveGeneratedKeys(boolean retrieveGeneratedKeys) {
+		this.retrieveGeneratedKeys = retrieveGeneratedKeys;
+	}
+
+	/**
+	 * Sets the value for the placeholder as a serialized Java object (used by
+	 * various forms of setObject()
+	 * 
+	 * @param parameterIndex
+	 *            DOCUMENT ME!
+	 * @param parameterObj
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private final void setSerializableObject(int parameterIndex,
+			Object parameterObj) throws SQLException {
+		try {
+			ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+			ObjectOutputStream objectOut = new ObjectOutputStream(bytesOut);
+			objectOut.writeObject(parameterObj);
+			objectOut.flush();
+			objectOut.close();
+			bytesOut.flush();
+			bytesOut.close();
+
+			byte[] buf = bytesOut.toByteArray();
+			ByteArrayInputStream bytesIn = new ByteArrayInputStream(buf);
+			setBinaryStream(parameterIndex, bytesIn, buf.length);
+		} catch (Exception ex) {
+			throw SQLError.createSQLException(Messages.getString("PreparedStatement.54") //$NON-NLS-1$
+					+ ex.getClass().getName(),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+
+	/**
+	 * Set a parameter to a Java short value. The driver converts this to a SQL
+	 * SMALLINT value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setShort(int parameterIndex, short x) throws SQLException {
+		setInternal(parameterIndex, String.valueOf(x));
+	}
+
+	/**
+	 * Set a parameter to a Java String value. The driver converts this to a SQL
+	 * VARCHAR or LONGVARCHAR value (depending on the arguments size relative to
+	 * the driver's limits on VARCHARs) when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public void setString(int parameterIndex, String x) throws SQLException {
+		// if the passed string is null, then set this column to null
+		if (x == null) {
+			setNull(parameterIndex, Types.CHAR);
+		} else {
+			checkClosed();
+			
+			int stringLength = x.length();
+
+			if (this.connection.isNoBackslashEscapesSet()) {
+				// Scan for any nasty chars
+
+				boolean needsHexEscape = false;
+
+				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;
+
+					StringBuffer quotedString = new StringBuffer(x.length() + 2);
+					quotedString.append('\'');
+					quotedString.append(x);
+					quotedString.append('\'');
+					
+					if (!this.isLoadDataQuery) {
+						parameterAsBytes = StringUtils.getBytes(quotedString.toString(),
+								this.charConverter, this.charEncoding,
+								this.connection.getServerCharacterEncoding(),
+								this.connection.parserKnowsUnicode());
+					} else {
+						// Send with platform character encoding
+						parameterAsBytes = quotedString.toString().getBytes();
+					}
+					
+					setInternal(parameterIndex, parameterAsBytes);
+				} else {
+					byte[] parameterAsBytes = null;
+
+					if (!this.isLoadDataQuery) {
+						parameterAsBytes = StringUtils.getBytes(x,
+								this.charConverter, this.charEncoding,
+								this.connection.getServerCharacterEncoding(),
+								this.connection.parserKnowsUnicode());
+					} else {
+						// Send with platform character encoding
+						parameterAsBytes = x.getBytes();
+					}
+					
+					setBytes(parameterIndex, parameterAsBytes);
+				}
+
+				return;
+			}
+
+			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('\'');
+
+			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();
+			}
+
+			setInternal(parameterIndex, parameterAsBytes);
+		}
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Time value. The driver converts this to a
+	 * SQL TIME value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            the parameter value
+	 * @param cal
+	 *            the cal specifying the timezone
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs.
+	 */
+	public void setTime(int parameterIndex, java.sql.Time x, Calendar cal)
+			throws SQLException {
+		setTimeInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Time value. The driver converts this to a
+	 * SQL TIME value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...));
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @throws java.sql.SQLException
+	 *             if a database access error occurs
+	 */
+	public void setTime(int parameterIndex, Time x)
+			throws java.sql.SQLException {
+		setTimeInternal(parameterIndex, x, null, TimeZone.getDefault(), false);
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Time value. The driver converts this to a
+	 * SQL TIME value when it sends it to the database, using the given
+	 * timezone.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...));
+	 * @param x
+	 *            the parameter value
+	 * @param tz
+	 *            the timezone to use
+	 * 
+	 * @throws java.sql.SQLException
+	 *             if a database access error occurs
+	 */
+	private void setTimeInternal(int parameterIndex, Time x, Calendar targetCalendar,
+			TimeZone tz,
+			boolean rollForward) throws java.sql.SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.TIME);
+		} else {
+			checkClosed();
+			
+			Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
+			
+			synchronized (sessionCalendar) {
+				x = TimeUtil.changeTimezone(this.connection, 
+						sessionCalendar,
+						targetCalendar,
+						 x, tz, this.connection
+						.getServerTimezoneTZ(), rollForward);
+			}
+			
+			setInternal(parameterIndex, "'" + x.toString() + "'"); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
+	 * to a SQL TIMESTAMP value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            the parameter value
+	 * @param cal
+	 *            the calendar specifying the timezone to use
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs.
+	 */
+	public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
+			Calendar cal) throws SQLException {
+		setTimestampInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
+	 * to a SQL TIMESTAMP value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @throws java.sql.SQLException
+	 *             if a database access error occurs
+	 */
+	public void setTimestamp(int parameterIndex, Timestamp x)
+			throws java.sql.SQLException {
+		setTimestampInternal(parameterIndex, x, null, TimeZone.getDefault(), false);
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
+	 * to a SQL TIMESTAMP value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            the parameter value
+	 * @param tz
+	 *            the timezone to use
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs.
+	 */
+	private void setTimestampInternal(int parameterIndex,
+			Timestamp x, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward) throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.TIMESTAMP);
+		} else {
+			checkClosed();
+			
+			String timestampString = null;
+			
+			Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
+					this.connection.getUtcCalendar() : 
+						getCalendarInstanceForSessionOrNew();
+					
+			synchronized (sessionCalendar) {
+				x = TimeUtil.changeTimezone(this.connection, 
+						sessionCalendar,
+						targetCalendar,
+						x, tz, this.connection
+					.getServerTimezoneTZ(), rollForward);
+			}
+
+			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
+		}
+	}
+
+	/**
+	 * When a very large Unicode value is input to a LONGVARCHAR parameter, it
+	 * may be more practical to send it via a java.io.InputStream. 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...
+	 * @param x
+	 *            the parameter value
+	 * @param length
+	 *            the number of bytes to read from the stream
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * 
+	 * @deprecated
+	 */
+	public void setUnicodeStream(int parameterIndex, InputStream x, int length)
+			throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.VARCHAR);
+		} else {
+			setBinaryStream(parameterIndex, x, length);
+		}
+	}
+
+	/**
+	 * @see PreparedStatement#setURL(int, URL)
+	 */
+	public void setURL(int parameterIndex, URL arg) throws SQLException {
+		if (arg != null) {
+			setString(parameterIndex, arg.toString());
+		} else {
+			setNull(parameterIndex, Types.CHAR);
+		}
+	}
+
+	private final void streamToBytes(Buffer packet, InputStream in,
+			boolean escape, int streamLength, boolean useLength)
+			throws SQLException {
+		try {
+			String connectionEncoding = this.connection.getEncoding();
+
+			boolean hexEscape = false;
+
+			if (this.connection.isNoBackslashEscapesSet()
+					|| (this.connection.getUseUnicode() 
+							&& connectionEncoding != null
+							&& CharsetMapping.isMultibyteCharset(connectionEncoding)
+							&& !this.connection.parserKnowsUnicode())) {
+				hexEscape = true;
+			}
+
+			if (streamLength == -1) {
+				useLength = false;
+			}
+
+			int bc = -1;
+
+			if (useLength) {
+				bc = readblock(in, streamConvertBuf, streamLength);
+			} else {
+				bc = readblock(in, streamConvertBuf);
+			}
+
+			int lengthLeftToRead = streamLength - bc;
+
+			if (hexEscape) {
+				packet.writeStringNoNull("x");
+			} else if (this.connection.getIO().versionMeetsMinimum(4, 1, 0)) {
+				packet.writeStringNoNull("_binary");
+			}
+
+			if (escape) {
+				packet.writeByte((byte) '\'');
+			}
+
+			while (bc > 0) {
+				if (hexEscape) {
+					hexEscapeBlock(streamConvertBuf, packet, bc);
+				} else if (escape) {
+					escapeblockFast(streamConvertBuf, packet, bc);
+				} else {
+					packet.writeBytesNoNull(streamConvertBuf, 0, bc);
+				}
+
+				if (useLength) {
+					bc = readblock(in, streamConvertBuf, lengthLeftToRead);
+
+					if (bc > 0) {
+						lengthLeftToRead -= bc;
+					}
+				} else {
+					bc = readblock(in, streamConvertBuf);
+				}
+			}
+
+			if (escape) {
+				packet.writeByte((byte) '\'');
+			}
+		} finally {
+			if (this.connection.getAutoClosePStmtStreams()) {
+				try {
+					in.close();
+				} catch (IOException ioEx) {
+					;
+				}
+
+				in = null;
+			}
+		}
+	}
+
+	private final byte[] streamToBytes(InputStream in, boolean escape,
+			int streamLength, boolean useLength) throws SQLException {
+		try {
+			if (streamLength == -1) {
+				useLength = false;
+			}
+
+			ByteArrayOutputStream bytesOut = new ByteArrayOutputStream();
+
+			int bc = -1;
+
+			if (useLength) {
+				bc = readblock(in, this.streamConvertBuf, streamLength);
+			} else {
+				bc = readblock(in, this.streamConvertBuf);
+			}
+
+			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) {
+				bytesOut.write('\'');
+			}
+
+			while (bc > 0) {
+				if (escape) {
+					escapeblockFast(this.streamConvertBuf, bytesOut, bc);
+				} else {
+					bytesOut.write(this.streamConvertBuf, 0, bc);
+				}
+
+				if (useLength) {
+					bc = readblock(in, this.streamConvertBuf, lengthLeftToRead);
+
+					if (bc > 0) {
+						lengthLeftToRead -= bc;
+					}
+				} else {
+					bc = readblock(in, this.streamConvertBuf);
+				}
+			}
+
+			if (escape) {
+				bytesOut.write('\'');
+			}
+
+			return bytesOut.toByteArray();
+		} finally {
+			if (this.connection.getAutoClosePStmtStreams()) {
+				try {
+					in.close();
+				} catch (IOException ioEx) {
+					;
+				}
+
+				in = null;
+			}
+		}
+	}
+
+	/**
+	 * Returns this PreparedStatement represented as a string.
+	 * 
+	 * @return this PreparedStatement represented as a string.
+	 */
+	public String toString() {
+		StringBuffer buf = new StringBuffer();
+		buf.append(super.toString());
+		buf.append(": "); //$NON-NLS-1$
+
+		try {
+			buf.append(asSql());
+		} catch (SQLException sqlEx) {
+			buf.append("EXCEPTION: " + sqlEx.toString());
+		}
+
+		return buf.toString();
+	}
+	
+	/** 
+	 * For calling stored functions, this will be -1 as we don't really count
+	 * the first '?' parameter marker, it's only syntax, but JDBC counts it
+	 * as #1, otherwise it will return 0 
+	 *
+	 */
+	
+	protected int getParameterIndexOffset() {
+		return 0;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ReplicationConnection.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ReplicationConnection.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ReplicationConnection.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,515 @@
+/*
+ Copyright (C) 2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+package com.mysql.jdbc;
+
+import java.sql.CallableStatement;
+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.util.Map;
+import java.util.Properties;
+
+/**
+ * Connection that opens two connections, one two a replication master, and
+ * another to one or more slaves, and decides to use master when the connection
+ * is not read-only, and use slave(s) when the connection is read-only.
+ * 
+ * @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;
+
+	private Connection masterConnection;
+
+	private Connection slavesConnection;
+
+	public ReplicationConnection(Properties masterProperties,
+			Properties slaveProperties) throws SQLException {
+		Driver driver = new Driver();
+
+		StringBuffer masterUrl = new StringBuffer("jdbc:mysql://");
+        StringBuffer slaveUrl = new StringBuffer("jdbc:mysql://");
+
+        String masterHost = masterProperties
+        	.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
+        
+        if (masterHost != null) {
+        	masterUrl.append(masterHost);
+        }
+ 
+        String slaveHost = slaveProperties
+        	.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
+        	
+        if (slaveHost != null) {
+        	slaveUrl.append(slaveHost);
+        }
+        
+        String masterDb = masterProperties
+        	.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
+
+        masterUrl.append("/");
+        
+        if (masterDb != null) {
+        	masterUrl.append(masterDb);
+        }
+        
+        String slaveDb = slaveProperties
+        	.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
+        
+        slaveUrl.append("/");
+        
+        if (slaveDb != null) {
+        	slaveUrl.append(slaveDb);
+        }
+        
+        this.masterConnection = (com.mysql.jdbc.Connection) driver.connect(
+                masterUrl.toString(), masterProperties);
+        this.slavesConnection = (com.mysql.jdbc.Connection) driver.connect(
+                slaveUrl.toString(), slaveProperties);
+        
+		this.currentConnection = this.masterConnection;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#clearWarnings()
+	 */
+	public synchronized void clearWarnings() throws SQLException {
+		this.currentConnection.clearWarnings();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#close()
+	 */
+	public synchronized void close() throws SQLException {
+		this.masterConnection.close();
+		this.slavesConnection.close();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#commit()
+	 */
+	public synchronized void commit() throws SQLException {
+		this.currentConnection.commit();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#createStatement()
+	 */
+	public Statement createStatement() throws SQLException {
+		return this.currentConnection.createStatement();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#createStatement(int, int)
+	 */
+	public synchronized Statement createStatement(int resultSetType,
+			int resultSetConcurrency) throws SQLException {
+		return this.currentConnection.createStatement(resultSetType,
+				resultSetConcurrency);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#createStatement(int, int, int)
+	 */
+	public synchronized Statement createStatement(int resultSetType,
+			int resultSetConcurrency, int resultSetHoldability)
+			throws SQLException {
+		return this.currentConnection.createStatement(resultSetType,
+				resultSetConcurrency, resultSetHoldability);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#getAutoCommit()
+	 */
+	public synchronized boolean getAutoCommit() throws SQLException {
+		return this.currentConnection.getAutoCommit();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#getCatalog()
+	 */
+	public synchronized String getCatalog() throws SQLException {
+		return this.currentConnection.getCatalog();
+	}
+
+	public synchronized Connection getCurrentConnection() {
+		return this.currentConnection;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#getHoldability()
+	 */
+	public synchronized int getHoldability() throws SQLException {
+		return this.currentConnection.getHoldability();
+	}
+
+	public synchronized Connection getMasterConnection() {
+		return this.masterConnection;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#getMetaData()
+	 */
+	public synchronized DatabaseMetaData getMetaData() throws SQLException {
+		return this.currentConnection.getMetaData();
+	}
+
+	public synchronized Connection getSlavesConnection() {
+		return this.slavesConnection;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#getTransactionIsolation()
+	 */
+	public synchronized int getTransactionIsolation() throws SQLException {
+		return this.currentConnection.getTransactionIsolation();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#getTypeMap()
+	 */
+	public synchronized Map getTypeMap() throws SQLException {
+		return this.currentConnection.getTypeMap();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#getWarnings()
+	 */
+	public synchronized SQLWarning getWarnings() throws SQLException {
+		return this.currentConnection.getWarnings();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#isClosed()
+	 */
+	public synchronized boolean isClosed() throws SQLException {
+		return this.currentConnection.isClosed();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#isReadOnly()
+	 */
+	public synchronized boolean isReadOnly() throws SQLException {
+		return this.currentConnection == this.slavesConnection;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#nativeSQL(java.lang.String)
+	 */
+	public synchronized String nativeSQL(String sql) throws SQLException {
+		return this.currentConnection.nativeSQL(sql);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareCall(java.lang.String)
+	 */
+	public CallableStatement prepareCall(String sql) throws SQLException {
+		return this.currentConnection.prepareCall(sql);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareCall(java.lang.String, int, int)
+	 */
+	public synchronized CallableStatement prepareCall(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		return this.currentConnection.prepareCall(sql, resultSetType,
+				resultSetConcurrency);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareCall(java.lang.String, int, int, int)
+	 */
+	public synchronized CallableStatement prepareCall(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException {
+		return this.currentConnection.prepareCall(sql, resultSetType,
+				resultSetConcurrency, resultSetHoldability);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareStatement(java.lang.String)
+	 */
+	public PreparedStatement prepareStatement(String sql) throws SQLException {
+		return this.currentConnection.prepareStatement(sql);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareStatement(java.lang.String, int)
+	 */
+	public synchronized PreparedStatement prepareStatement(String sql,
+			int autoGeneratedKeys) throws SQLException {
+		return this.currentConnection.prepareStatement(sql, autoGeneratedKeys);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareStatement(java.lang.String, int, int)
+	 */
+	public synchronized PreparedStatement prepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		return this.currentConnection.prepareStatement(sql, resultSetType,
+				resultSetConcurrency);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareStatement(java.lang.String, int, int,
+	 *      int)
+	 */
+	public synchronized PreparedStatement prepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency,
+			int resultSetHoldability) throws SQLException {
+		return this.currentConnection.prepareStatement(sql, resultSetType,
+				resultSetConcurrency, resultSetHoldability);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareStatement(java.lang.String, int[])
+	 */
+	public synchronized PreparedStatement prepareStatement(String sql,
+			int[] columnIndexes) throws SQLException {
+		return this.currentConnection.prepareStatement(sql, columnIndexes);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#prepareStatement(java.lang.String,
+	 *      java.lang.String[])
+	 */
+	public synchronized PreparedStatement prepareStatement(String sql,
+			String[] columnNames) throws SQLException {
+		return this.currentConnection.prepareStatement(sql, columnNames);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#releaseSavepoint(java.sql.Savepoint)
+	 */
+	public synchronized void releaseSavepoint(Savepoint savepoint)
+			throws SQLException {
+		this.currentConnection.releaseSavepoint(savepoint);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#rollback()
+	 */
+	public synchronized void rollback() throws SQLException {
+		this.currentConnection.rollback();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#rollback(java.sql.Savepoint)
+	 */
+	public synchronized void rollback(Savepoint savepoint) throws SQLException {
+		this.currentConnection.rollback(savepoint);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#setAutoCommit(boolean)
+	 */
+	public synchronized void setAutoCommit(boolean autoCommit)
+			throws SQLException {
+		this.currentConnection.setAutoCommit(autoCommit);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#setCatalog(java.lang.String)
+	 */
+	public synchronized void setCatalog(String catalog) throws SQLException {
+		this.currentConnection.setCatalog(catalog);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#setHoldability(int)
+	 */
+	public synchronized void setHoldability(int holdability)
+			throws SQLException {
+		this.currentConnection.setHoldability(holdability);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#setReadOnly(boolean)
+	 */
+	public synchronized void setReadOnly(boolean readOnly) throws SQLException {
+		if (readOnly) {
+			if (currentConnection != slavesConnection) {
+				switchToSlavesConnection();
+			}
+		} else {
+			if (currentConnection != masterConnection) {
+				switchToMasterConnection();
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#setSavepoint()
+	 */
+	public synchronized Savepoint setSavepoint() throws SQLException {
+		return this.currentConnection.setSavepoint();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#setSavepoint(java.lang.String)
+	 */
+	public synchronized Savepoint setSavepoint(String name) throws SQLException {
+		return this.currentConnection.setSavepoint(name);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#setTransactionIsolation(int)
+	 */
+	public synchronized void setTransactionIsolation(int level)
+			throws SQLException {
+		this.currentConnection.setTransactionIsolation(level);
+	}
+
+	// For testing
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Connection#setTypeMap(java.util.Map)
+	 */
+	public synchronized void setTypeMap(Map arg0) throws SQLException {
+		this.currentConnection.setTypeMap(arg0);
+	}
+
+	private synchronized void switchToMasterConnection() throws SQLException {
+		swapConnections(this.masterConnection, this.slavesConnection);
+	}
+
+	private synchronized void switchToSlavesConnection() throws SQLException {
+		swapConnections(this.slavesConnection, this.masterConnection);
+	}
+	
+	/**
+	 * Swaps current context (catalog, autocommit and txn_isolation) from
+	 * sourceConnection to targetConnection, and makes targetConnection
+	 * the "current" connection that will be used for queries.
+	 * 
+	 * @param switchToConnection the connection to swap from
+	 * @param switchFromConnection the connection to swap to
+	 * 
+	 * @throws SQLException if an error occurs
+	 */
+	private synchronized void swapConnections(Connection switchToConnection, 
+			Connection switchFromConnection) throws SQLException {
+		String switchFromCatalog = switchFromConnection.getCatalog();
+		String switchToCatalog = switchToConnection.getCatalog();
+
+		if (switchToCatalog != null && !switchToCatalog.equals(switchFromCatalog)) {
+			switchToConnection.setCatalog(switchFromCatalog);
+		} else if (switchFromCatalog != null) {
+			switchToConnection.setCatalog(switchFromCatalog);
+		}
+
+		boolean switchToAutoCommit = switchToConnection.getAutoCommit();
+		boolean switchFromConnectionAutoCommit = switchFromConnection.getAutoCommit();
+		
+		if (switchFromConnectionAutoCommit != switchToAutoCommit) {
+			switchToConnection.setAutoCommit(switchFromConnectionAutoCommit);
+		}
+
+		int switchToIsolation = switchToConnection
+				.getTransactionIsolation();
+
+		int switchFromIsolation = switchFromConnection.getTransactionIsolation();
+		
+		if (switchFromIsolation != switchToIsolation) {
+			switchToConnection
+					.setTransactionIsolation(switchFromIsolation);
+		}
+		
+		this.currentConnection = switchToConnection;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ReplicationDriver.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ReplicationDriver.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ReplicationDriver.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,83 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+/**
+ * The Java SQL framework allows for multiple database drivers. Each driver
+ * should supply a class that implements the Driver interface
+ * 
+ * <p>
+ * The DriverManager will try to load as many drivers as it can find and then
+ * for any given connection request, it will ask each driver in turn to try to
+ * connect to the target URL.
+ * 
+ * <p>
+ * It is strongly recommended that each Driver class should be small and
+ * standalone so that the Driver class can be loaded and queried without
+ * bringing in vast quantities of supporting code.
+ * 
+ * <p>
+ * When a Driver class is loaded, it should create an instance of itself and
+ * register it with the DriverManager. This means that a user can load and
+ * register a driver by doing Class.forName("foo.bah.Driver")
+ * 
+ * @see org.gjt.mm.mysql.Connection
+ * @see java.sql.Driver
+ * @author Mark Matthews
+ * @version $Id: ReplicationDriver.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
+ *          Exp $
+ */
+public class ReplicationDriver extends NonRegisteringReplicationDriver
+		implements java.sql.Driver {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	//
+	// Register ourselves with the DriverManager
+	//
+	static {
+		try {
+			java.sql.DriverManager
+					.registerDriver(new NonRegisteringReplicationDriver());
+		} catch (SQLException E) {
+			throw new RuntimeException("Can't register driver!");
+		}
+	}
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Construct a new driver and register it with DriverManager
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs.
+	 */
+	public ReplicationDriver() throws SQLException {
+		// Required for Class.forName().newInstance()
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ResultSet.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ResultSet.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ResultSet.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,8438 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.StringReader;
+import java.io.UnsupportedEncodingException;
+
+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;
+
+/**
+ * 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: ResultSet.java 5900 2006-10-18 21:33:06Z mmatthews $
+ * 
+ * @see ResultSetMetaData
+ * @see java.sql.ResultSet
+ */
+public class ResultSet implements java.sql.ResultSet {
+
+	/**
+	 * 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 com.mysql.jdbc.Connection 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;
+
+	private TimeZone defaultTimeZone;
+
+	/** Are we in the middle of doing updates to the current row? */
+	protected boolean doingUpdates = false;
+
+	protected ProfileEventSink eventSink = null;
+
+	private 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 ResultSet nextResultSet = null;
+
+	/** Are we on the insert row? */
+	protected boolean onInsertRow = false;
+
+	/** The statement that created us */
+	protected com.mysql.jdbc.Statement 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;
+
+	private PreparedStatement statementUsedForFetchingRows;
+
+	/** Pointer to current row data */
+	protected Object[] 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;
+
+	/**
+	 * 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 ResultSet(long updateCount, long updateID, Connection conn,
+			Statement 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 ResultSet(String catalog, Field[] fields, RowData tuples,
+			Connection conn, Statement creatorStmt) throws SQLException {
+		this.connection = conn;
+		
+		this.fastDateCal = new GregorianCalendar(Locale.US);
+		this.fastDateCal.setTimeZone(this.getDefaultTimeZone());
+		
+		if (this.connection != null) {
+			this.useStrictFloatingPoint = this.connection
+					.getStrictFloatingPoint();
+			this.setDefaultTimeZone(this.connection.getDefaultTimeZone());
+			this.connectionId = this.connection.getId();
+		}
+
+		this.owningStatement = creatorStmt;
+
+		this.catalog = catalog;
+		this.profileSql = this.connection.getProfileSql();
+
+		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.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());
+		}
+		
+		this.retainOwningStatement = false;
+		
+		if (this.connection != null) {
+			retainOwningStatement = 
+				this.connection.getRetainStatementAfterResultSetClose();
+		}
+	}
+
+	/**
+	 * 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 (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;
+				}
+			}
+		}
+
+		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.rowData.size() != 0) {
+			this.rowData.afterLast();
+			this.thisRow = null;
+		}
+	}
+
+	/**
+	 * 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;
+		}
+
+		this.rowData.beforeFirst();
+		this.thisRow = null;
+	}
+
+	// ---------------------------------------------------------------------
+	// Traversal/Positioning
+	// ---------------------------------------------------------------------
+
+	/**
+	 * Builds a hash between column names and their indices for fast retrieval.
+	 */
+	protected void buildIndexMapping() throws SQLException {
+		int numFields = this.fields.length;
+		this.columnNameToIndex = new HashMap(numFields);
+		this.fullColumnNameToIndex = new HashMap(numFields);
+
+		// 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 = new Integer(i);
+			String columnName = this.fields[i].getName();
+			String fullColumnName = this.fields[i].getFullName();
+
+			if (columnName != null) {
+				this.columnNameToIndex.put(columnName, index);
+				this.columnNameToIndex.put(columnName.toUpperCase(), index);
+				this.columnNameToIndex.put(columnName.toLowerCase(), index);
+			}
+
+			if (fullColumnName != null) {
+				this.fullColumnNameToIndex.put(fullColumnName, index);
+				this.fullColumnNameToIndex.put(fullColumnName.toUpperCase(),
+						index);
+				this.fullColumnNameToIndex.put(fullColumnName.toLowerCase(),
+						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) || (columnIndex > this.fields.length)) {
+			throw SQLError.createSQLException(Messages.getString(
+					"ResultSet.Column_Index_out_of_range", new Object[] {
+							new Integer(columnIndex),
+							new Integer(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.rowData.isDynamic() && (this.rowData.size() == 0)) {
+			throw SQLError.createSQLException(
+					Messages
+							.getString("ResultSet.Illegal_operation_on_empty_result_set"),
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		if (this.rowData.isBeforeFirst()) {
+			throw SQLError.createSQLException(Messages
+					.getString("ResultSet.Before_start_of_result_set_146"),
+					SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$
+		}
+
+		if (this.rowData.isAfterLast()) {
+			throw SQLError.createSQLException(Messages
+					.getString("ResultSet.After_end_of_result_set_148"),
+					SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * We can't do this ourselves, otherwise the contract for
+	 * Statement.getMoreResults() won't work correctly.
+	 */
+	protected 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
+	//
+	protected final ResultSet copy() throws SQLException {
+		ResultSet rs = new ResultSet(this.catalog, this.fields, this.rowData,
+				this.connection, this.owningStatement);
+
+		return rs;
+	}
+
+	protected void redefineFieldsForDBMD(Field[] f) {
+		this.fields = f;
+		
+		for (int i = 0; i < this.fields.length; i++) {
+			this.fields[i].setUseOldNameMetadata(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[columnIndexMinusOne] instanceof String) {
+			return (String) this.thisRow[columnIndexMinusOne];
+		}
+
+		if (this.thisRow[columnIndexMinusOne] == null) {
+			this.wasNullFlag = true;
+			
+			return null;
+		}
+
+		this.wasNullFlag = false;
+		
+		String stringVal = null;
+
+		if ((this.connection != null) && this.connection.getUseUnicode()) {
+			try {
+				String encoding = this.fields[columnIndexMinusOne]
+						.getCharacterSet();
+
+				if (encoding == null) {
+					stringVal = new String(
+							(byte[]) this.thisRow[columnIndexMinusOne]);
+				} else {
+					SingleByteCharsetConverter converter = this.connection
+							.getCharsetConverter(encoding);
+
+					if (converter != null) {
+						stringVal = converter
+								.toString((byte[]) this.thisRow[columnIndexMinusOne]);
+					} else {
+						stringVal = new String(
+								(byte[]) this.thisRow[columnIndexMinusOne],
+								encoding);
+					}
+				}
+			} catch (java.io.UnsupportedEncodingException E) {
+				throw SQLError.createSQLException(
+						Messages
+								.getString("ResultSet.Unsupported_character_encoding____138") //$NON-NLS-1$
+								+ this.connection.getEncoding() + "'.", "0S100");
+			}
+		} else {
+			stringVal = StringUtils
+					.toAsciiString((byte[]) this.thisRow[columnIndexMinusOne]);
+		}
+		
+		return stringVal;
+	}
+
+	private Date fastDateCreate(Calendar cal, int year, int month,
+			int day) {
+		if (cal == null) {
+			cal = this.fastDateCal;
+		}
+
+		boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes();
+						
+		return TimeUtil.fastDateCreate(useGmtMillis,
+				useGmtMillis ? getGmtCalendar() : null,
+				cal, year, month, day);
+	}
+
+	private Time fastTimeCreate(Calendar cal, int hour,
+			int minute, int second) throws SQLException {
+		if (cal == null) {
+			cal = this.fastDateCal;
+		}
+
+		return TimeUtil.fastTimeCreate(cal, hour, minute, second);
+	}
+
+	private Timestamp fastTimestampCreate(Calendar cal, int year,
+			int month, int day, int hour, int minute, int seconds,
+			int secondsPart) {
+		if (cal == null) {
+			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();
+
+		if (this.rowData.isEmpty()) {
+			return false;
+		}
+
+		if (this.onInsertRow) {
+			this.onInsertRow = false;
+		}
+
+		if (this.doingUpdates) {
+			this.doingUpdates = false;
+		}
+
+		this.rowData.beforeFirst();
+		this.thisRow = this.rowData.next();
+
+		return true;
+	}
+
+	/**
+	 * 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,
+											new Integer(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[] { new Integer(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[] { new Integer(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,
+												new Integer(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,
+												new Integer(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,
+													new Integer(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,
+										new Integer(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) {
+			byte[] b = getBytes(columnIndex);
+
+			if (b != null) {
+				return new ByteArrayInputStream(b);
+			}
+
+			return null;
+		}
+
+		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);
+			
+			if ((columnIndex < 1) || (columnIndex > this.fields.length)) {
+				throw SQLError.createSQLException(Messages.getString(
+						"ResultSet.Column_Index_out_of_range", new Object[] {
+								new Integer(columnIndex),
+								new Integer(this.fields.length) }),
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+			}
+
+			try {
+				if (this.thisRow[columnIndex - 1] == null) {
+					this.wasNullFlag = true;
+				} else {
+					this.wasNullFlag = false;
+				}
+			} catch (NullPointerException ex) {
+				this.wasNullFlag = true;
+			}
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			if (!this.connection.getEmulateLocators()) {
+				return new Blob((byte[]) this.thisRow[columnIndex - 1]);
+			}
+
+			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[columnIndex], 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) {
+		if (this.thisRow[columnIndexMinusOne] == null) {
+			this.wasNullFlag = true;
+
+			return false;
+		}
+
+		this.wasNullFlag = false;
+
+		if (((byte[]) this.thisRow[columnIndexMinusOne]).length == 0) {
+			return false;
+		}
+
+		byte boolVal = ((byte[]) this.thisRow[columnIndexMinusOne])[0];
+
+		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
+		//
+		
+		stringVal = stringVal.trim(); 
+		
+		try {
+			int decimalIndex = stringVal.indexOf(".");
+
+			
+			if (decimalIndex != -1) {
+				double valueAsDouble = Double.parseDouble(stringVal);
+
+				if (this.connection.getJdbcCompliantTruncationForReads()) {
+					if (valueAsDouble < Byte.MIN_VALUE
+							|| valueAsDouble > Byte.MAX_VALUE) {
+						throwRangeException(stringVal, columnIndex,
+								Types.TINYINT);
+					}
+				}
+
+				return (byte) valueAsDouble;
+			}
+
+			long valueAsLong = Long.parseLong(stringVal);
+
+			if (this.connection.getJdbcCompliantTruncationForReads()) {
+				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);
+			
+			try {
+				if (this.thisRow[columnIndex - 1] == null) {
+					this.wasNullFlag = true;
+				} else {
+					this.wasNullFlag = false;
+				}
+			} catch (NullPointerException E) {
+				this.wasNullFlag = true;
+			} catch (ArrayIndexOutOfBoundsException aioobEx) {
+				throw SQLError.createSQLException(Messages.getString(
+						"ResultSet.Column_Index_out_of_range", new Object[] {
+								new Integer(columnIndex),
+								new Integer(this.fields.length) }),
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+			}
+
+			if (this.wasNullFlag) {
+				return null;
+			}
+
+			return (byte[]) this.thisRow[columnIndex - 1];
+		}
+
+		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
+	 */
+	private 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) {
+			String asString = getStringForClob(columnIndex);
+
+			if (asString == null) {
+				return null;
+			}
+
+			return new StringReader(asString);
+		}
+
+		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());
+		}
+
+		String stringVal = getStringInternal(columnIndex, false);
+
+		if (stringVal == null) {
+			return null;
+		}
+
+		return getDateFromString(stringVal, columnIndex);
+	}
+
+	/**
+	 * 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 (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+						.equals(this.connection.getZeroDateTimeBehavior())) {
+					this.wasNullFlag = true;
+
+					return null;
+				} else if (ConnectionProperties.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, new Integer(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) {
+					throw SQLError.createSQLException(Messages.getString(
+							"ResultSet.Bad_format_for_Date", new Object[] {
+									stringVal, new Integer(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,
+							new Integer(columnIndex) }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	private TimeZone getDefaultTimeZone() {
+		// Worst case we allocate this twice and the other gets GC'd,
+		// however prevents deadlock
+		if (this.defaultTimeZone == null) {
+			this.defaultTimeZone = TimeZone.getDefault();
+		}
+
+		return this.defaultTimeZone;
+	}
+
+	/**
+	 * 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, new Integer(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
+	 */
+	protected 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.connection.getJdbcCompliantTruncationForReads()) {
+					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.connection.getJdbcCompliantTruncationForReads()) {
+
+					if (this.connection.getJdbcCompliantTruncationForReads() && 
+							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) {
+			if (this.connection.getUseFastIntParsing()) {
+				checkColumnBounds(columnIndex);
+				
+				try {
+					if (this.thisRow[columnIndex - 1] == null) {
+						this.wasNullFlag = true;
+					} else {
+						this.wasNullFlag = false;
+					}
+				} catch (NullPointerException E) {
+					this.wasNullFlag = true;
+				} catch (ArrayIndexOutOfBoundsException aioobEx) {
+					throw SQLError.createSQLException(Messages.getString(
+							"ResultSet.Column_Index_out_of_range",
+							new Object[] { new Integer(columnIndex),
+									new Integer(this.fields.length) }),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				}
+
+				if (this.wasNullFlag) {
+					return 0;
+				}
+
+				byte[] intAsBytes = (byte[]) this.thisRow[columnIndex - 1];
+
+				if (intAsBytes.length == 0) {
+					return convertToZeroWithEmptyCheck();
+				}
+
+				boolean needsFullParse = false;
+
+				for (int i = 0; i < intAsBytes.length; i++) {
+					if (((char) intAsBytes[i] == 'e')
+							|| ((char) intAsBytes[i] == 'E')) {
+						needsFullParse = true;
+
+						break;
+					}
+				}
+
+				if (!needsFullParse) {
+					try {
+						return parseIntWithOverflowCheck(columnIndex,
+								intAsBytes, null);
+					} catch (NumberFormatException nfe) {
+						try {
+
+							return parseIntAsDouble(columnIndex, new String(
+									intAsBytes));
+						} 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.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")
+										+ new String(intAsBytes) //$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)) {
+						return Integer.parseInt(val);
+					}
+
+					// Convert floating point
+					return parseIntAsDouble(columnIndex, val);
+				}
+
+				return 0;
+			} catch (NumberFormatException nfe) {
+				try {
+					return parseIntAsDouble(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.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")
+								+ 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.connection.getJdbcCompliantTruncationForReads()) {
+						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.connection.getJdbcCompliantTruncationForReads()) {
+					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.connection.getJdbcCompliantTruncationForReads()) {
+					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();
+			
+			if (this.connection.getUseFastIntParsing()) {
+			
+				checkColumnBounds(columnIndex);
+				
+				try {
+					if (this.thisRow[columnIndex - 1] == null) {
+						this.wasNullFlag = true;
+					} else {
+						this.wasNullFlag = false;
+					}
+				} catch (NullPointerException E) {
+					this.wasNullFlag = true;
+				} catch (ArrayIndexOutOfBoundsException aioobEx) {
+					throw SQLError.createSQLException(Messages.getString(
+							"ResultSet.Column_Index_out_of_range",
+							new Object[] { new Integer(columnIndex),
+									new Integer(this.fields.length) }),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				}
+
+				if (this.wasNullFlag) {
+					return 0;
+				}
+
+				byte[] longAsBytes = (byte[]) this.thisRow[columnIndex - 1];
+
+				if (longAsBytes.length == 0) {
+					return convertToZeroWithEmptyCheck();
+				}
+
+				boolean needsFullParse = false;
+
+				for (int i = 0; i < longAsBytes.length; i++) {
+					if (((char) longAsBytes[i] == 'e')
+							|| ((char) longAsBytes[i] == 'E')) {
+						needsFullParse = true;
+
+						break;
+					}
+				}
+
+				if (!needsFullParse) {
+					try {
+						return parseLongWithOverflowCheck(columnIndex,
+								longAsBytes, null, overflowCheck);
+					} catch (NumberFormatException nfe) {
+						try {
+							// To do: Warn of over/underflow???
+							return parseLongAsDouble(columnIndex, new String(
+									longAsBytes));
+						} catch (NumberFormatException newNfe) {
+							// ; // ignore, it's not a number
+						}
+
+						if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+							return getNumericRepresentationOfSQLBitType(columnIndex);
+						}
+						
+						throw SQLError.createSQLException(
+								Messages
+										.getString("ResultSet.Invalid_value_for_getLong()_-____79")
+										+ new String(longAsBytes) //$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];
+		
+		if (this.thisRow[columnIndex - 1] == null) {
+			this.wasNullFlag = true;
+			
+			return null;
+		}
+		
+		this.wasNullFlag = false;
+		
+		switch (f.getSQLType()) {
+			case Types.DECIMAL:
+			case Types.NUMERIC:
+				stringVal = StringUtils
+						.toAsciiString((byte[]) this.thisRow[columnIndex - 1]);
+				break;
+			default:
+				stringVal = getNativeString(columnIndex);
+		}
+
+		return getBigDecimalFromString(stringVal, columnIndex, scale);
+	}
+
+	/**
+	 * A column value can also be retrieved as a binary strea. 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();
+
+		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);
+
+		try {
+			if (this.thisRow[columnIndex - 1] == null) {
+				this.wasNullFlag = true;
+			} else {
+				this.wasNullFlag = false;
+			}
+		} catch (NullPointerException ex) {
+			this.wasNullFlag = true;
+		}
+
+		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[]) this.thisRow[columnIndex - 1];
+
+		default:
+			dataAsBytes = getNativeBytes(columnIndex, false);
+		}
+
+		if (!this.connection.getEmulateLocators()) {
+			return new Blob(dataAsBytes);
+		}
+
+		return new BlobFromLocator(this, columnIndex);
+	}
+
+	/**
+	 * 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);
+
+		if (this.thisRow[columnIndex - 1] == null) {
+			this.wasNullFlag = true;
+
+			return 0;
+		}
+
+		try {
+			if (this.thisRow[columnIndex - 1] == null) {
+				this.wasNullFlag = true;
+			} else {
+				this.wasNullFlag = false;
+			}
+		} catch (NullPointerException E) {
+			this.wasNullFlag = true;
+		}
+
+		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.connection.getJdbcCompliantTruncationForReads() &&
+					(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[]) this.thisRow[columnIndex])[0];
+			
+			if (!field.isUnsigned()) {
+				return valueAsByte;
+			}
+
+			short valueAsShort = (valueAsByte >= 0) ? 
+					valueAsByte : (short)(valueAsByte + (short)256);
+			
+			if (overflowCheck && this.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				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[columnIndex], 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);
+
+		try {
+			if (this.thisRow[columnIndex - 1] == null) {
+				this.wasNullFlag = true;
+			} else {
+				this.wasNullFlag = false;
+			}
+		} catch (NullPointerException E) {
+			this.wasNullFlag = true;
+		}
+
+		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[]) this.thisRow[columnIndex - 1];
+
+		default:
+			int sqlType = field.getSQLType();
+		
+			if (sqlType == Types.VARBINARY || sqlType == Types.BINARY) {
+				return (byte[]) this.thisRow[columnIndex - 1];
+			}
+		
+			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 {
+		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[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____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.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 mysqlType = this.fields[columnIndex - 1].getMysqlType();
+		
+		if (mysqlType == MysqlDefs.FIELD_TYPE_DATE) {
+			byte[] bits = (byte[]) this.thisRow[columnIndex - 1];
+
+			if (bits == null) {
+				this.wasNullFlag = true;
+
+				return null;
+			}
+
+			this.wasNullFlag = false;
+
+			java.sql.Date dateToReturn = null;
+
+			int year = 0;
+			int month = 0;
+			int day = 0;
+
+			int hour = 0;
+			int minute = 0;
+			int seconds = 0;
+
+			if (bits.length != 0) {
+				year = (bits[0] & 0xff) | ((bits[1] & 0xff) << 8);
+
+				month = bits[2];
+				day = bits[3];
+			}
+
+			if ((year == 0) && (month == 0) && (day == 0)) {
+				if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+						.equals(this.connection.getZeroDateTimeBehavior())) {
+					this.wasNullFlag = true;
+
+					return null;
+				} else if (ConnectionProperties.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;
+			}
+
+			return fastDateCreate(
+					getCalendarInstanceForSessionOrNew(), year, month, day);
+		}
+
+		boolean rollForward = (tz != null && !tz.equals(this.getDefaultTimeZone()));
+
+		return (Date)getNativeDateTimeValue(columnIndex, null, Types.DATE, mysqlType, 
+					tz, rollForward);
+	}
+
+	private java.sql.Date getNativeDateViaParseConversion(int columnIndex) throws SQLException {
+		if (this.useUsageAdvisor) {
+			issueConversionViaParsingWarning("getDate()", columnIndex,
+					this.thisRow[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[columnIndex] == null) {
+			this.wasNullFlag = true;
+
+			return 0;
+		}
+
+		this.wasNullFlag = false;
+
+		Field f= this.fields[columnIndex];
+		
+		switch (f.getMysqlType()) {
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			byte[] bits = (byte[]) this.thisRow[columnIndex];
+
+			long valueAsLong = (bits[0] & 0xff)
+					| ((long) (bits[1] & 0xff) << 8)
+					| ((long) (bits[2] & 0xff) << 16)
+					| ((long) (bits[3] & 0xff) << 24)
+					| ((long) (bits[4] & 0xff) << 32)
+					| ((long) (bits[5] & 0xff) << 40)
+					| ((long) (bits[6] & 0xff) << 48)
+					| ((long) (bits[7] & 0xff) << 56);
+
+			return Double.longBitsToDouble(valueAsLong);
+		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:
+			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:
+
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getDouble()", columnIndex,
+						this.thisRow[columnIndex], 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 });
+			}
+
+			String stringVal = getNativeString(columnIndex + 1);
+
+			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[columnIndex] == null) {
+			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.connection.getJdbcCompliantTruncationForReads() && 
+					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:
+			byte[] bits = (byte[]) this.thisRow[columnIndex];
+
+			int asInt = (bits[0] & 0xff) | ((bits[1] & 0xff) << 8)
+					| ((bits[2] & 0xff) << 16) | ((bits[3] & 0xff) << 24);
+
+			return Float.intBitsToFloat(asInt);
+
+		default:
+
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getFloat()", columnIndex,
+						this.thisRow[columnIndex], 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 });
+			}
+
+			String stringVal = getNativeString(columnIndex + 1);
+
+			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[columnIndex] == null) {
+			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.connection.getJdbcCompliantTruncationForReads() &&
+					(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:
+			byte[] bits = (byte[]) this.thisRow[columnIndex];
+
+			int valueAsInt = (bits[0] & 0xff) | ((bits[1] & 0xff) << 8)
+					| ((bits[2] & 0xff) << 16) | ((bits[3] & 0xff) << 24);
+
+			if (!f.isUnsigned()) {	
+				return valueAsInt;
+			}
+			
+			valueAsLong = (valueAsInt >= 0) ? 
+					valueAsInt : valueAsInt + 4294967296L; 
+			
+			if (overflowCheck && this.connection.getJdbcCompliantTruncationForReads() &&
+					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.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				if (valueAsDouble < Integer.MIN_VALUE
+						|| valueAsDouble > Integer.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsDouble),
+							columnIndex + 1, Types.INTEGER);
+				}
+			}
+
+			return (int) valueAsDouble;
+
+		default:
+
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getInt()", columnIndex,
+						this.thisRow[columnIndex], 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 });
+			}
+
+			String stringVal = getNativeString(columnIndex + 1);
+
+			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[columnIndex] == null) {
+			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:
+
+			byte[] bits = (byte[]) this.thisRow[columnIndex];
+
+			long valueAsLong = (bits[0] & 0xff)
+					| ((long) (bits[1] & 0xff) << 8)
+					| ((long) (bits[2] & 0xff) << 16)
+					| ((long) (bits[3] & 0xff) << 24)
+					| ((long) (bits[4] & 0xff) << 32)
+					| ((long) (bits[5] & 0xff) << 40)
+					| ((long) (bits[6] & 0xff) << 48)
+					| ((long) (bits[7] & 0xff) << 56);
+
+			if (!f.isUnsigned() || !expandUnsignedLong) {
+				return valueAsLong;
+			}
+			
+			BigInteger asBigInt = convertLongToUlong(valueAsLong);
+			
+			if (overflowCheck && this.connection.getJdbcCompliantTruncationForReads() && 
+					((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.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				if (valueAsDouble < Long.MIN_VALUE
+						|| valueAsDouble > Long.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsDouble),
+							columnIndex + 1, Types.BIGINT);
+				}
+			}
+
+			return (long) valueAsDouble;
+		default:
+
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getLong()", columnIndex,
+						this.thisRow[columnIndex], 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 });
+			}
+
+			String stringVal = getNativeString(columnIndex + 1);
+
+			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[columnIndex] == null) {
+			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:
+			byte[] bits = (byte[]) this.thisRow[columnIndex];
+			
+			short asShort = (short) ((bits[0] & 0xff) | ((bits[1] & 0xff) << 8));
+
+			if (!f.isUnsigned()) {
+				return asShort;
+			}
+			
+			int valueAsInt = asShort & 0xffff;
+			
+			if (overflowCheck && this.connection.getJdbcCompliantTruncationForReads() &&
+					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.connection.getJdbcCompliantTruncationForReads() &&
+						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.connection.getJdbcCompliantTruncationForReads() &&
+					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.connection.getJdbcCompliantTruncationForReads()) {
+					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.connection.getJdbcCompliantTruncationForReads() && 
+					((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.connection.getJdbcCompliantTruncationForReads()) {
+				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.connection.getJdbcCompliantTruncationForReads()) {
+				if (valueAsFloat < Short.MIN_VALUE
+						|| valueAsFloat > Short.MAX_VALUE) {
+					throwRangeException(String.valueOf(valueAsFloat),
+							columnIndex + 1, Types.SMALLINT);
+				}
+			}
+
+			return (short) valueAsFloat;
+		default:
+
+			if (this.useUsageAdvisor) {
+				issueConversionViaParsingWarning("getShort()", columnIndex,
+						this.thisRow[columnIndex], 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 });
+			}
+
+			String stringVal = getNativeString(columnIndex + 1);
+
+			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);
+		}
+
+		try {
+			if (this.thisRow[columnIndex - 1] == null) {
+				this.wasNullFlag = true;
+
+				return null;
+			}
+
+			this.wasNullFlag = false;
+		} catch (NullPointerException E) {
+			this.wasNullFlag = true;
+
+			return null;
+		}
+
+		String stringVal = null;
+
+		if (this.thisRow[columnIndex - 1] instanceof String) {
+			return (String) this.thisRow[columnIndex - 1];
+		}
+
+		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);
+
+		if (this.thisRow[columnIndex - 1] == null) {
+			this.wasNullFlag = true;
+
+			return null;
+		} else {
+			this.wasNullFlag = false;
+		}
+
+		int mysqlType = this.fields[columnIndex - 1].getMysqlType();
+
+		if (mysqlType == MysqlDefs.FIELD_TYPE_TIME) {
+
+			byte[] bits = (byte[]) this.thisRow[columnIndex - 1];
+
+			int length = bits.length;
+			int hour = 0;
+			int minute = 0;
+			int seconds = 0;
+
+			if (length != 0) {
+				// bits[0] // skip tm->neg
+				// binaryData.readLong(); // skip daysPart
+				hour = bits[5];
+				minute = bits[6];
+				seconds = bits[7];
+			}
+
+			Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
+			
+			synchronized (sessionCalendar) {
+				Time time = TimeUtil
+						.fastTimeCreate(sessionCalendar, hour,
+								minute, seconds);
+	
+				Time adjustedTime = TimeUtil.changeTimezone(this.connection, 
+						sessionCalendar,
+						targetCalendar,
+						time,
+						this.connection.getServerTimezoneTZ(), tz, rollForward);
+
+				return adjustedTime;
+			}
+		}
+
+		return (Time)getNativeDateTimeValue(columnIndex, targetCalendar,
+				Types.TIME, mysqlType, 
+				tz, rollForward);
+	}
+	
+	private Time getNativeTimeViaParseConversion(int columnIndex, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward) throws SQLException {
+		if (this.useUsageAdvisor) {
+			issueConversionViaParsingWarning("getTime()", columnIndex,
+					this.thisRow[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);
+
+		if (this.thisRow[columnIndex - 1] == null) {
+			this.wasNullFlag = true;
+
+			return null;
+		}
+
+		this.wasNullFlag = false;
+
+		int mysqlType = this.fields[columnIndex - 1].getMysqlType();
+
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_DATETIME:
+		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+			byte[] bits = (byte[]) this.thisRow[columnIndex - 1];
+
+			int length = bits.length;
+
+			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[0] & 0xff) | ((bits[1] & 0xff) << 8);
+				month = bits[2];
+				day = bits[3];
+
+				if (length > 4) {
+					hour = bits[4];
+					minute = bits[5];
+					seconds = bits[6];
+				}
+
+				if (length > 7) {
+					nanos = (bits[7] & 0xff) | ((bits[8] & 0xff) << 8)
+							| ((bits[9] & 0xff) << 16)
+							| ((bits[10] & 0xff) << 24);
+				}
+			}
+
+			if ((year == 0) && (month == 0) && (day == 0)) {
+				if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+						.equals(this.connection.getZeroDateTimeBehavior())) {
+					this.wasNullFlag = true;
+
+					return null;
+				} else if (ConnectionProperties.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;
+			}
+
+			Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
+					this.connection.getUtcCalendar() : 
+						getCalendarInstanceForSessionOrNew();
+			
+			synchronized (sessionCalendar) {
+				Timestamp ts = fastTimestampCreate(
+						sessionCalendar, year, month, day,
+						hour, minute, seconds, nanos);
+	
+				Timestamp adjustedTs = TimeUtil.changeTimezone(
+						this.connection, sessionCalendar,
+						targetCalendar,
+						ts,
+						this.connection.getServerTimezoneTZ(), tz, rollForward);
+				
+				return adjustedTs;
+			}
+
+		default:
+			return (Timestamp)getNativeDateTimeValue(columnIndex, targetCalendar,
+					Types.TIMESTAMP, mysqlType, 
+					tz, rollForward);
+		}
+	}
+
+	private Timestamp getNativeTimestampViaParseConversion(int columnIndex, Calendar targetCalendar, 
+			TimeZone tz, boolean rollForward) throws SQLException {
+		if (this.useUsageAdvisor) {
+			issueConversionViaParsingWarning("getTimestamp()", columnIndex,
+					this.thisRow[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 ResultSet#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.
+	 */
+	protected ResultSet 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();
+
+		try {
+			if (this.thisRow[columnIndex - 1] == null) {
+				this.wasNullFlag = true;
+
+				return null;
+			}
+		} catch (ArrayIndexOutOfBoundsException aioobEx) {
+			throw SQLError.createSQLException(Messages.getString(
+					"ResultSet.Column_Index_out_of_range", new Object[] {
+							new Integer(columnIndex),
+							new Integer(this.fields.length) }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		this.wasNullFlag = false;
+
+		Field field;
+		field = this.fields[columnIndex - 1];
+
+		//
+		// If they come from a binary-encode result set,
+		// no need to create another new object to represent
+		// the value, just return it directly, unless it's
+		// a byte[], which means it could be a string or blob.
+		//
+		if (this.isBinaryEncoded
+				&& !(this.thisRow[columnIndex - 1] instanceof byte[])) {
+
+			//
+			// Special case here...If this is a 'bit' type, it will actually
+			// have
+			// been returned as an Integer by the server...
+			//
+			if (field.getSQLType() == Types.BIT && field.getLength() > 0) {
+				// valueOf would be nicer here, but it isn't
+				// present in JDK-1.3.1, which is what the CTS
+				// uses.
+				return new Boolean(getBoolean(columnIndex));
+			}
+
+			Object columnValue = this.thisRow[columnIndex - 1];
+
+			if (columnValue == null) {
+				this.wasNullFlag = true;
+
+				return null;
+			}
+
+			return columnValue;
+		}
+
+		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 new Boolean(getBoolean(columnIndex));
+
+		case Types.TINYINT:
+			if (!field.isUnsigned()) {
+				return new Integer(getByte(columnIndex));
+			}
+
+			return new Integer(getInt(columnIndex));
+
+		case Types.SMALLINT:
+
+			return new Integer(getInt(columnIndex));
+
+		case Types.INTEGER:
+
+			if (!field.isUnsigned() || 
+					field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {
+				return new Integer(getInt(columnIndex));
+			}
+
+			return new Long(getLong(columnIndex));
+			
+		case Types.BIGINT:
+
+			if (!field.isUnsigned()) {
+				return new Long(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[] {
+								new Integer(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;
+			}
+
+		case Types.DATE:
+			if (field.getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR
+					&& !this.connection.getYearIsDateType()) {
+				return new Short(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);
+	}
+
+	protected Object getObjectStoredProc(int columnIndex, int desiredSqlType)
+			throws SQLException {
+		checkRowPos();
+
+		try {
+			if (this.thisRow[columnIndex - 1] == null) {
+				this.wasNullFlag = true;
+
+				return null;
+			}
+		} catch (ArrayIndexOutOfBoundsException aioobEx) {
+			throw SQLError.createSQLException(Messages.getString(
+					"ResultSet.Column_Index_out_of_range", new Object[] {
+							new Integer(columnIndex),
+							new Integer(this.fields.length) }),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+
+		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 new Boolean(getBoolean(columnIndex));
+
+		case Types.TINYINT:
+			return new Integer(getInt(columnIndex));
+
+		case Types.SMALLINT:
+			return new Integer(getInt(columnIndex));
+
+		case Types.INTEGER:
+
+			if (!field.isUnsigned() || 
+					field.getMysqlType() == MysqlDefs.FIELD_TYPE_INT24) {
+				return new Integer(getInt(columnIndex));
+			}
+
+			return new Long(getLong(columnIndex));
+
+		case Types.BIGINT:
+
+			if (field.isUnsigned()) {
+				return getBigDecimal(columnIndex);
+			}
+
+			return new Long(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 new Short(getShort(columnIndex));
+			}
+
+			return getDate(columnIndex);
+
+		case Types.TIME:
+			return getTime(columnIndex);
+
+		case Types.TIMESTAMP:
+			return getTimestamp(columnIndex);
+
+		default:
+			return getString(columnIndex);
+		}
+	}
+
+	protected Object getObjectStoredProc(int i, java.util.Map map,
+			int desiredSqlType) throws SQLException {
+		return getObjectStoredProc(i, desiredSqlType);
+	}
+
+	protected Object getObjectStoredProc(String columnName, int desiredSqlType)
+			throws SQLException {
+		return getObjectStoredProc(findColumn(columnName), desiredSqlType);
+	}
+
+	protected 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
+	 */
+	protected String getServerInfo() {
+		return this.serverInfo;
+	}
+
+	private long getNumericRepresentationOfSQLBitType(int columnIndex) throws SQLException {
+		
+		if (this.fields[columnIndex - 1].isSingleBit() || 
+				((byte[])this.thisRow[columnIndex - 1]).length == 1) {
+			return ((byte[])this.thisRow[columnIndex - 1])[0];
+		}
+		
+		
+		byte[] asBytes = (byte[])this.thisRow[columnIndex - 1];
+		
+		
+		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.connection.getUseFastIntParsing()) {
+				
+				checkColumnBounds(columnIndex);
+				
+				try {
+					if (this.thisRow[columnIndex - 1] == null) {
+						this.wasNullFlag = true;
+					} else {
+						this.wasNullFlag = false;
+					}
+				} catch (NullPointerException E) {
+					this.wasNullFlag = true;
+				} catch (ArrayIndexOutOfBoundsException aioobEx) {
+					throw SQLError.createSQLException(Messages.getString(
+							"ResultSet.Column_Index_out_of_range",
+							new Object[] { new Integer(columnIndex),
+									new Integer(this.fields.length) }),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				}
+
+				if (this.wasNullFlag) {
+					return 0;
+				}
+
+				byte[] shortAsBytes = (byte[]) this.thisRow[columnIndex - 1];
+
+				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.connection.getJdbcCompliantTruncationForReads() &&
+									(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.connection.getJdbcCompliantTruncationForReads() &&
+							(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 {
+		return getStringInternal(columnIndex, true);
+	}
+
+	/**
+	 * 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);
+			}
+
+			try {
+				if (this.thisRow[columnIndex - 1] == null) {
+					this.wasNullFlag = true;
+
+					return null;
+				}
+
+				this.wasNullFlag = false;
+			} catch (NullPointerException E) {
+				this.wasNullFlag = true;
+
+				return null;
+			}
+
+			String stringVal = null;
+			columnIndex--; // JDBC is 1-based, Java is not !?
+
+			if (this.fields[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) {
+				if (this.fields[columnIndex].isSingleBit()) {
+					byte[] asBytes = (byte[])this.thisRow[columnIndex];
+					
+					if (asBytes.length == 0) {
+						return String.valueOf(convertToZeroWithEmptyCheck());
+					}
+					
+					return String.valueOf(asBytes[0]);
+				}
+				
+				return String.valueOf(getNumericRepresentationOfSQLBitType(columnIndex + 1));
+			}
+			
+			String encoding = this.fields[columnIndex].getCharacterSet();
+
+			if ((this.connection != null) && this.connection.getUseUnicode()) {
+				try {
+					if (encoding == null) {
+						stringVal = new String(
+								(byte[]) this.thisRow[columnIndex]);
+					} else {
+						SingleByteCharsetConverter converter = this.connection
+								.getCharsetConverter(encoding);
+
+						if (converter != null) {
+							stringVal = converter
+									.toString((byte[]) this.thisRow[columnIndex]);
+						} else {
+							stringVal = new String(
+									(byte[]) this.thisRow[columnIndex],
+									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((byte[]) this.thisRow[columnIndex]);
+			}
+
+			//
+			// Special handling for YEAR type from mysql, some people
+			// want it as a DATE, others want to treat it as a SHORT
+			//
+
+			if (this.fields[columnIndex].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) {
+				if (!this.connection.getYearIsDateType()) {
+					return stringVal;
+				}
+
+				Date dt = getDateFromString(stringVal, columnIndex + 1);
+
+				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 (this.fields[columnIndex].getSQLType()) {
+				case Types.TIME:
+					Time tm = getTimeFromString(stringVal, null, columnIndex + 1,
+							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 + 1);
+
+					if (dt == null) {
+						this.wasNullFlag = true;
+
+						return null;
+					}
+
+					this.wasNullFlag = false;
+
+					return dt.toString();
+				case Types.TIMESTAMP:
+					Timestamp ts = getTimestampFromString(columnIndex + 1,
+							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 (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+						.equals(this.connection.getZeroDateTimeBehavior())) {
+					this.wasNullFlag = true;
+
+					return null;
+				} else if (ConnectionProperties.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 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);
+		}
+
+		String timeAsString = getStringInternal(columnIndex, false);
+
+		return getTimeFromString(timeAsString, targetCalendar,
+				columnIndex, tz, rollForward);
+	}
+
+	/**
+	 * 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 {
+		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 (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+							.equals(this.connection.getZeroDateTimeBehavior())) {
+						this.wasNullFlag = true;
+						
+						return null;
+					} else if (ConnectionProperties.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, 70, 0, 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);
+		}
+		
+	}
+
+	/**
+	 * 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);
+		}
+
+		String timestampValue = getStringInternal(columnIndex, false);
+
+		return getTimestampFromString(columnIndex, targetCalendar, 
+				timestampValue, tz,
+				rollForward);
+	}
+
+	/**
+	 * 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));
+	}
+
+	long getUpdateCount() {
+		return this.updateCount;
+	}
+
+	long getUpdateID() {
+		return this.updateId;
+	}
+
+	/**
+	 * @see ResultSet#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 ResultSet#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 message = new StringBuffer();
+		message
+				.append("ResultSet type conversion via parsing detected when calling ");
+		message.append(methodName);
+		message.append(" for column ");
+		message.append((columnIndex + 1));
+		message.append(", (column named '");
+		message.append(fieldInfo.getOriginalName());
+		message.append("' in table '");
+		message.append(fieldInfo.getOriginalTableName());
+		if (this.owningStatement != null
+				&& this.owningStatement instanceof com.mysql.jdbc.PreparedStatement) {
+			message.append("' created from query:\n\n");
+			message
+					.append(((com.mysql.jdbc.PreparedStatement) this.owningStatement).originalSql);
+			message.append("\n\n");
+		} else {
+			message.append(". ");
+		}
+
+		message.append("Java class of column type is '");
+		
+		if (value != null) {
+			message.append(value.getClass().getName());
+		} else {
+			message.append(ResultSetMetaData.getClassNameForJavaType(fieldInfo.getSQLType(), 
+					fieldInfo.isUnsigned(), 
+					fieldInfo.getMysqlType(), 
+					fieldInfo.isBinary() || fieldInfo.isBlob(),
+					fieldInfo.isOpaqueBinary()));
+		}
+		
+		message.append("', MySQL field type is ");
+		message.append(MysqlDefs.typeToName(fieldInfo.getMysqlType()));
+		message
+				.append(".\n\nTypes that could be converted directly without parsing are:\n");
+
+		for (int i = 0; i < typesWithNoParseConversion.length; i++) {
+			message.append(MysqlDefs.typeToName(typesWithNoParseConversion[i]));
+			message.append("\n");
+		}
+
+		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, null, this.pointOfOrigin,
+				message.toString()));
+
+	}
+
+	private void issueDataTruncationWarningIfConfigured(int columnIndex,
+			int readSize, int truncatedToSize) {
+		DataTruncation dt = new DataTruncation(columnIndex, false, true,
+				readSize, truncatedToSize);
+	}
+
+	/**
+	 * 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();
+
+		if (this.rowData.size() == 0) {
+			return false;
+		}
+
+		if (this.onInsertRow) {
+			this.onInsertRow = false;
+		}
+
+		if (this.doingUpdates) {
+			this.doingUpdates = false;
+		}
+
+		this.rowData.beforeLast();
+		this.thisRow = this.rowData.next();
+
+		return true;
+	}
+
+	// /////////////////////////////////////////
+	//
+	// 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.rowData.size() == 0) {
+			b = false;
+		} else {
+			if (!this.rowData.hasNext()) {
+				// force scroll past end
+				this.rowData.next();
+				b = false;
+			} else {
+				clearWarnings();
+				this.thisRow = this.rowData.next();
+				b = true;
+			}
+		}
+
+		return b;
+	}
+
+	private int parseIntAsDouble(int columnIndex, String val)
+			throws NumberFormatException, SQLException {
+		if (val == null) {
+			return 0;
+		}
+
+		double valueAsDouble = Double.parseDouble(val);
+
+		if (this.connection.getJdbcCompliantTruncationForReads()) {
+			if (valueAsDouble < Integer.MIN_VALUE
+					|| valueAsDouble > Integer.MAX_VALUE) {
+				throwRangeException(String.valueOf(valueAsDouble), columnIndex,
+						Types.INTEGER);
+			}
+		}
+
+		return (int) valueAsDouble;
+	}
+
+	private int parseIntWithOverflowCheck(int columnIndex, byte[] valueAsBytes,
+			String valueAsString) throws NumberFormatException, SQLException {
+
+		int intValue = 0;
+
+		if (valueAsBytes == null && valueAsString == null) {
+			return 0;
+		}
+
+		if (valueAsBytes != null) {
+			intValue = StringUtils.getInt(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(); 
+			
+			intValue = Integer.parseInt(valueAsString);
+		}
+
+		if (this.connection.getJdbcCompliantTruncationForReads()) {
+			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);
+				}
+			}
+		}
+
+		return intValue;
+	}
+
+	private long parseLongAsDouble(int columnIndex, String val)
+			throws NumberFormatException, SQLException {
+		if (val == null) {
+			return 0;
+		}
+
+		double valueAsDouble = Double.parseDouble(val);
+
+		if (this.connection.getJdbcCompliantTruncationForReads()) {
+			if (valueAsDouble < Long.MIN_VALUE
+					|| valueAsDouble > Long.MAX_VALUE) {
+				throwRangeException(val, columnIndex, Types.BIGINT);
+			}
+		}
+
+		return (long) valueAsDouble;
+	}
+
+	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.connection.getJdbcCompliantTruncationForReads()) {
+			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);
+				}
+			}
+		}
+
+		return longValue;
+	}
+
+	private short parseShortAsDouble(int columnIndex, String val)
+			throws NumberFormatException, SQLException {
+		if (val == null) {
+			return 0;
+		}
+
+		double valueAsDouble = Double.parseDouble(val);
+
+		if (this.connection.getJdbcCompliantTruncationForReads()) {
+			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.connection.getJdbcCompliantTruncationForReads()) {
+			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 ((rowIndex - 1) >= 0) {
+			rowIndex--;
+			this.rowData.setCurrentRow(rowIndex);
+			this.thisRow = this.rowData.getAt(rowIndex);
+
+			return true;
+		} else if ((rowIndex - 1) == -1) {
+			rowIndex--;
+			this.rowData.setCurrentRow(rowIndex);
+			this.thisRow = null;
+
+			return false;
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 * 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
+	 */
+	protected void realClose(boolean calledExplicitly) throws SQLException {
+		if (this.isClosed) {
+			return;
+		}
+
+		try {
+			if (this.useUsageAdvisor) {
+				if (!calledExplicitly) {
+					String message = Messages
+							.getString("ResultSet.ResultSet_implicitly_closed_by_driver._150") //$NON-NLS-1$
+							+ Messages
+									.getString("ResultSet._n_nYou_should_close_ResultSets_explicitly_from_your_code_to_free_up_resources_in_a_more_efficient_manner._151"); //$NON-NLS-1$
+
+					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, null,
+							this.pointOfOrigin, message));
+				}
+
+				if (this.rowData instanceof RowDataStatic && !isLast()
+						&& !isAfterLast() && (this.rowData.size() != 0)) {
+					StringBuffer messageBuf = new StringBuffer(
+							Messages
+									.getString("ResultSet.Possible_incomplete_traversal_of_result_set._Cursor_was_left_on_row__154")); //$NON-NLS-1$
+					messageBuf.append(getRow());
+					messageBuf.append(Messages.getString("ResultSet._of__155")); //$NON-NLS-1$
+					messageBuf.append(this.rowData.size());
+					messageBuf
+							.append(Messages
+									.getString("ResultSet._rows_when_it_was_closed._156")); //$NON-NLS-1$
+					messageBuf
+							.append(Messages
+									.getString("ResultSet._n_nYou_should_consider_re-formulating_your_query_to_return_only_the_rows_you_are_interested_in_using._157")); //$NON-NLS-1$
+
+					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, null,
+							this.pointOfOrigin, messageBuf.toString()));
+				}
+
+				//
+				// Report on any columns that were selected but
+				// not referenced
+				//
+				if (this.columnUsed.length > 0) {
+					StringBuffer buf = new StringBuffer(
+							Messages
+									.getString("ResultSet.The_following_columns_were__160")); //$NON-NLS-1$
+					buf
+							.append(Messages
+									.getString("ResultSet._part_of_the_SELECT_statement_for_this_result_set,_but_were_161")); //$NON-NLS-1$
+					buf.append(Messages
+							.getString("ResultSet._never_referenced___162")); //$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, 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;
+			}
+		}
+	}
+
+	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) {
+			return false;
+		}
+
+		this.rowData.moveRowRelative(rows);
+		this.thisRow = this.rowData.getAt(this.rowData.getCurrentRowNumber());
+
+		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
+	 */
+	protected 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(ResultSet nextResultSet) {
+		this.nextResultSet = nextResultSet;
+	}
+
+	protected void setOwningStatement(com.mysql.jdbc.Statement 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;
+	}
+
+	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 ResultSet#updateArray(int, Array)
+	 */
+	public void updateArray(int arg0, Array arg1) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see ResultSet#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 ResultSet#updateBlob(int, Blob)
+	 */
+	public void updateBlob(int arg0, java.sql.Blob arg1) throws SQLException {
+		throw new NotUpdatable();
+	}
+
+	/**
+	 * @see ResultSet#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 ResultSet#updateClob(int, Clob)
+	 */
+	public void updateClob(int arg0, java.sql.Clob arg1) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see ResultSet#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 ResultSet#updateRef(int, Ref)
+	 */
+	public void updateRef(int arg0, Ref arg1) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see ResultSet#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;
+	}
+	
+	private Object getNativeDateTimeValue(int columnIndex, Calendar targetCalendar,
+				int jdbcType,
+			int mysqlType, TimeZone tz, boolean rollForward)
+			throws SQLException {
+
+		int year = 0;
+		int month = 0;
+		int day = 0;
+
+		int hour = 0;
+		int minute = 0;
+		int seconds = 0;
+
+		int nanos = 0;
+
+		byte[] bits = (byte[]) this.thisRow[columnIndex - 1];
+
+		if (bits == null) {
+			this.wasNullFlag = true;
+
+			return null;
+		}
+
+		Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
+				this.connection.getUtcCalendar() : 
+					getCalendarInstanceForSessionOrNew();
+				
+		this.wasNullFlag = false;
+		
+		boolean populatedFromDateTimeValue = false;
+
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_DATETIME:
+		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+			populatedFromDateTimeValue = true;
+
+			int length = bits.length;
+
+			if (length != 0) {
+				year = (bits[0] & 0xff) | ((bits[1] & 0xff) << 8);
+				month = bits[2];
+				day = bits[3];
+
+				if (length > 4) {
+					hour = bits[4];
+					minute = bits[5];
+					seconds = bits[6];
+				}
+
+				if (length > 7) {
+					nanos = (bits[7] & 0xff) | ((bits[8] & 0xff) << 8)
+							| ((bits[9] & 0xff) << 16)
+							| ((bits[10] & 0xff) << 24);
+				}
+			}
+
+			break;
+		case MysqlDefs.FIELD_TYPE_DATE:
+			populatedFromDateTimeValue = true;
+
+			if (bits.length != 0) {
+				year = (bits[0] & 0xff) | ((bits[1] & 0xff) << 8);
+				month = bits[2];
+				day = bits[3];
+			}
+
+			break;
+		case MysqlDefs.FIELD_TYPE_TIME:
+			populatedFromDateTimeValue = true;
+
+			if (bits.length != 0) {
+				// bits[0] // skip tm->neg
+				// binaryData.readLong(); // skip daysPart
+				hour = bits[5];
+				minute = bits[6];
+				seconds = bits[7];
+			}
+
+			year = 1970;
+			month = 1;
+			day = 1;
+			
+			break;
+		default:
+			populatedFromDateTimeValue = false;
+		}
+
+		switch (jdbcType) {
+		case Types.TIME:
+			if (populatedFromDateTimeValue) {
+				Time time = TimeUtil.fastTimeCreate(
+						getCalendarInstanceForSessionOrNew(), hour, minute,
+						seconds);
+
+				Time adjustedTime = TimeUtil.changeTimezone(this.connection,
+						sessionCalendar, 
+						targetCalendar,
+						time, this.connection.getServerTimezoneTZ(), tz,
+						rollForward);
+
+				return adjustedTime;
+			}
+			
+			return getNativeTimeViaParseConversion(columnIndex, targetCalendar,
+					tz, rollForward);
+
+		case Types.DATE:
+			if (populatedFromDateTimeValue) {
+				if ((year == 0) && (month == 0) && (day == 0)) {
+					if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+							.equals(this.connection.getZeroDateTimeBehavior())) {
+						this.wasNullFlag = true;
+
+						return null;
+					} else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+							.equals(this.connection.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 fastDateCreate(getCalendarInstanceForSessionOrNew(),
+						year, month, day);
+			}
+
+			return getNativeDateViaParseConversion(columnIndex);
+		case Types.TIMESTAMP:
+			if (populatedFromDateTimeValue) {
+				if ((year == 0) && (month == 0) && (day == 0)) {
+					if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_CONVERT_TO_NULL
+							.equals(this.connection.getZeroDateTimeBehavior())) {
+						this.wasNullFlag = true;
+
+						return null;
+					} else if (ConnectionProperties.ZERO_DATETIME_BEHAVIOR_EXCEPTION
+							.equals(this.connection.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 = fastTimestampCreate(
+						getCalendarInstanceForSessionOrNew(), year, month, day,
+						hour, minute, seconds, nanos);
+
+				Timestamp adjustedTs = TimeUtil.changeTimezone(this.connection,
+						sessionCalendar, 
+						targetCalendar,
+						ts, this.connection.getServerTimezoneTZ(), tz,
+						rollForward);
+
+				return adjustedTs;
+			}
+
+			return getNativeTimestampViaParseConversion(columnIndex, targetCalendar, tz, rollForward);
+			
+		default:
+			throw new SQLException("Internal error - conversion method doesn't support this type", 
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ResultSetMetaData.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ResultSetMetaData.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ResultSetMetaData.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,809 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.sql.SQLException;
+import java.sql.Types;
+
+/**
+ * A ResultSetMetaData object can be used to find out about the types and
+ * properties of the columns in a ResultSet
+ * 
+ * @author Mark Matthews
+ * @version $Id: ResultSetMetaData.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
+ *          Exp $
+ * 
+ * @see java.sql.ResultSetMetaData
+ */
+public class ResultSetMetaData implements java.sql.ResultSetMetaData {
+	private static int clampedGetLength(Field f) {
+		long fieldLength = f.getLength();
+
+		if (fieldLength > Integer.MAX_VALUE) {
+			fieldLength = Integer.MAX_VALUE;
+		}
+
+		return (int) fieldLength;
+	}
+
+	/**
+	 * Checks if the SQL Type is a Decimal/Number Type
+	 * 
+	 * @param type
+	 *            SQL Type
+	 * 
+	 * @return ...
+	 */
+	private static final boolean isDecimalType(int type) {
+		switch (type) {
+		case Types.BIT:
+		case Types.TINYINT:
+		case Types.SMALLINT:
+		case Types.INTEGER:
+		case Types.BIGINT:
+		case Types.FLOAT:
+		case Types.REAL:
+		case Types.DOUBLE:
+		case Types.NUMERIC:
+		case Types.DECIMAL:
+			return true;
+		}
+
+		return false;
+	}
+
+	Field[] fields;
+	boolean useOldAliasBehavior = false;
+	
+	/**
+	 * Initialise for a result with a tuple set and a field descriptor set
+	 * 
+	 * @param fields
+	 *            the array of field descriptors
+	 */
+	public ResultSetMetaData(Field[] fields, boolean useOldAliasBehavior) {
+		this.fields = fields;
+		this.useOldAliasBehavior = useOldAliasBehavior;
+	}
+
+	/**
+	 * What's a column's table's catalog name?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return catalog name, or "" if not applicable
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public String getCatalogName(int column) throws SQLException {
+		Field f = getField(column);
+
+		String database = f.getDatabaseName();
+
+		return (database == null) ? "" : database; //$NON-NLS-1$
+	}
+
+	/**
+	 * What's the Java character encoding name for the given column?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return the Java character encoding name for the given column, or null if
+	 *         no Java character encoding maps to the MySQL character set for
+	 *         the given column.
+	 * 
+	 * @throws SQLException
+	 *             if an invalid column index is given.
+	 */
+	public String getColumnCharacterEncoding(int column) throws SQLException {
+		String mysqlName = getColumnCharacterSet(column);
+
+		String javaName = null;
+
+		if (mysqlName != null) {
+			javaName = CharsetMapping.getJavaEncodingForMysqlEncoding(
+					mysqlName, null);
+		}
+
+		return javaName;
+	}
+
+	/**
+	 * What's the MySQL character set name for the given column?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return the MySQL character set name for the given column
+	 * 
+	 * @throws SQLException
+	 *             if an invalid column index is given.
+	 */
+	public String getColumnCharacterSet(int column) throws SQLException {
+		return getField(column).getCharacterSet();
+	}
+
+	// --------------------------JDBC 2.0-----------------------------------
+
+	/**
+	 * JDBC 2.0
+	 * 
+	 * <p>
+	 * Return the fully qualified name of the Java class whose instances are
+	 * manufactured if ResultSet.getObject() is called to retrieve a value from
+	 * the column. ResultSet.getObject() may return a subClass of the class
+	 * returned by this method.
+	 * </p>
+	 * 
+	 * @param column
+	 *            the column number to retrieve information for
+	 * 
+	 * @return the fully qualified name of the Java class whose instances are
+	 *         manufactured if ResultSet.getObject() is called to retrieve a
+	 *         value from the column.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public String getColumnClassName(int column) throws SQLException {
+		Field f = getField(column);
+
+		return getClassNameForJavaType(f.getSQLType(), 
+				f.isUnsigned(), 
+				f.getMysqlType(), 
+				f.isBinary() || f.isBlob(),
+				f.isOpaqueBinary()); 
+	}
+
+	/**
+	 * Whats the number of columns in the ResultSet?
+	 * 
+	 * @return the number
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public int getColumnCount() throws SQLException {
+		return this.fields.length;
+	}
+
+	/**
+	 * What is the column's normal maximum width in characters?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return the maximum width
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public int getColumnDisplaySize(int column) throws SQLException {
+		Field f = getField(column);
+
+		int lengthInBytes = clampedGetLength(f);
+
+		return lengthInBytes / f.getMaxBytesPerCharacter();
+	}
+
+	/**
+	 * What is the suggested column title for use in printouts and displays?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return the column label
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public String getColumnLabel(int column) throws SQLException {
+		if (this.useOldAliasBehavior) {
+			return getColumnName(column);
+		}
+		
+		return getField(column).getColumnLabel();
+	}
+
+	/**
+	 * What's a column's name?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return the column name
+	 * 
+	 * @throws SQLException
+	 *             if a databvase access error occurs
+	 */
+	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) {
+			return getField(column).getName();
+		}
+		
+		return name;
+	}
+
+	/**
+	 * What is a column's SQL Type? (java.sql.Type int)
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return the java.sql.Type value
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 * 
+	 * @see java.sql.Types
+	 */
+	public int getColumnType(int column) throws SQLException {
+		return getField(column).getSQLType();
+	}
+
+	/**
+	 * Whats is the column's data source specific type name?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return the type name
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public String getColumnTypeName(int column) throws java.sql.SQLException {
+		Field field = getField(column);
+
+		int mysqlType = field.getMysqlType();
+		int jdbcType = field.getSQLType();
+
+		switch (mysqlType) {
+		case MysqlDefs.FIELD_TYPE_BIT:
+			return "BIT";
+		case MysqlDefs.FIELD_TYPE_DECIMAL:
+		case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
+			return field.isUnsigned() ? "DECIMAL UNSIGNED" : "DECIMAL";
+
+		case MysqlDefs.FIELD_TYPE_TINY:
+			return field.isUnsigned() ? "TINYINT UNSIGNED" : "TINYINT";
+
+		case MysqlDefs.FIELD_TYPE_SHORT:
+			return field.isUnsigned() ? "SMALLINT UNSIGNED" : "SMALLINT";
+
+		case MysqlDefs.FIELD_TYPE_LONG:
+			return field.isUnsigned() ? "INTEGER UNSIGNED" : "INTEGER";
+
+		case MysqlDefs.FIELD_TYPE_FLOAT:
+			return field.isUnsigned() ? "FLOAT UNSIGNED" : "FLOAT";
+
+		case MysqlDefs.FIELD_TYPE_DOUBLE:
+			return field.isUnsigned() ? "DOUBLE UNSIGNED" : "DOUBLE";
+
+		case MysqlDefs.FIELD_TYPE_NULL:
+			return "NULL"; //$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+			return "TIMESTAMP"; //$NON-NLS-1$       
+
+		case MysqlDefs.FIELD_TYPE_LONGLONG:
+			return field.isUnsigned() ? "BIGINT UNSIGNED" : "BIGINT";
+
+		case MysqlDefs.FIELD_TYPE_INT24:
+			return field.isUnsigned() ? "MEDIUMINT UNSIGNED" : "MEDIUMINT";
+
+		case MysqlDefs.FIELD_TYPE_DATE:
+			return "DATE"; //$NON-NLS-1$       
+
+		case MysqlDefs.FIELD_TYPE_TIME:
+			return "TIME"; //$NON-NLS-1$       
+
+		case MysqlDefs.FIELD_TYPE_DATETIME:
+			return "DATETIME"; //$NON-NLS-1$      
+
+		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
+			return "TINYBLOB"; //$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
+			return "MEDIUMBLOB"; //$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
+			return "LONGBLOB"; //$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_BLOB:
+			if (getField(column).isBinary()) {
+				return "BLOB";//$NON-NLS-1$
+			}
+
+			return "TEXT";//$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_VARCHAR:
+			return "VARCHAR"; //$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_VAR_STRING:
+			if (jdbcType == Types.VARBINARY) {
+				return "VARBINARY";
+			}
+			
+			return "VARCHAR"; //$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_STRING:
+			if (jdbcType == Types.BINARY) {
+				return "BINARY";
+			}
+			
+			return "CHAR"; //$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_ENUM:
+			return "ENUM"; //$NON-NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_YEAR:
+			return "YEAR"; // $NON_NLS-1$
+
+		case MysqlDefs.FIELD_TYPE_SET:
+			return "SET"; //$NON-NLS-1$
+
+		default:
+			return "UNKNOWN"; //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Returns the field instance for the given column index
+	 * 
+	 * @param columnIndex
+	 *            the column number to retrieve a field instance for
+	 * 
+	 * @return the field instance for the given column index
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected Field getField(int columnIndex) throws SQLException {
+		if ((columnIndex < 1) || (columnIndex > this.fields.length)) {
+			throw SQLError.createSQLException(Messages.getString("ResultSetMetaData.46"), //$NON-NLS-1$
+					SQLError.SQL_STATE_INVALID_COLUMN_NUMBER);
+		}
+
+		return this.fields[columnIndex - 1];
+	}
+
+	/**
+	 * What is a column's number of decimal digits.
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the precision
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public int getPrecision(int column) throws SQLException {
+		Field f = getField(column);
+
+		// if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_NEW_DECIMAL) {
+		// return f.getLength();
+		// }
+
+		if (isDecimalType(f.getSQLType())) {
+			if (f.getDecimals() > 0) {
+				return clampedGetLength(f) - 1 + f.getPrecisionAdjustFactor();
+			}
+
+			return clampedGetLength(f) + f.getPrecisionAdjustFactor();
+		}
+
+		switch (f.getMysqlType()) {
+		case MysqlDefs.FIELD_TYPE_TINY_BLOB:
+		case MysqlDefs.FIELD_TYPE_BLOB:
+		case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB:
+		case MysqlDefs.FIELD_TYPE_LONG_BLOB:
+			return clampedGetLength(f); // this may change in the future
+		// for now, the server only
+		// returns FIELD_TYPE_BLOB for _all_
+		// BLOB types, but varying lengths
+		// indicating the _maximum_ size
+		// for each BLOB type.
+		default:
+			return clampedGetLength(f) / f.getMaxBytesPerCharacter();
+
+		}
+	}
+
+	/**
+	 * What is a column's number of digits to the right of the decimal point?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the scale
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public int getScale(int column) throws SQLException {
+		Field f = getField(column);
+
+		if (isDecimalType(f.getSQLType())) {
+			return f.getDecimals();
+		}
+
+		return 0;
+	}
+
+	/**
+	 * What is a column's table's schema? This relies on us knowing the table
+	 * name. The JDBC specification allows us to return "" if this is not
+	 * applicable.
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return the Schema
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public String getSchemaName(int column) throws SQLException {
+		return ""; //$NON-NLS-1$
+	}
+
+	/**
+	 * Whats a column's table's name?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return column name, or "" if not applicable
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public String getTableName(int column) throws SQLException {
+		if (this.useOldAliasBehavior) {
+			return getField(column).getTableName();
+		}
+		
+		return getField(column).getTableNameNoAliases();
+	}
+
+	/**
+	 * Is the column automatically numbered (and thus read-only)
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return true if so
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public boolean isAutoIncrement(int column) throws SQLException {
+		Field f = getField(column);
+
+		return f.isAutoIncrement();
+	}
+
+	/**
+	 * Does a column's case matter?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return true if so
+	 * 
+	 * @throws java.sql.SQLException
+	 *             if a database access error occurs
+	 */
+	public boolean isCaseSensitive(int column) throws java.sql.SQLException {
+		Field field = getField(column);
+
+		int sqlType = field.getSQLType();
+
+		switch (sqlType) {
+		case Types.BIT:
+		case Types.TINYINT:
+		case Types.SMALLINT:
+		case Types.INTEGER:
+		case Types.BIGINT:
+		case Types.FLOAT:
+		case Types.REAL:
+		case Types.DOUBLE:
+		case Types.DATE:
+		case Types.TIME:
+		case Types.TIMESTAMP:
+			return false;
+
+		case Types.CHAR:
+		case Types.VARCHAR:
+		case Types.LONGVARCHAR:
+
+			if (field.isBinary()) {
+				return true;
+			}
+
+			String collationName = field.getCollation();
+
+			return ((collationName != null) && !collationName.endsWith("_ci"));
+
+		default:
+			return true;
+		}
+	}
+
+	/**
+	 * Is the column a cash value?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return true if its a cash column
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public boolean isCurrency(int column) throws SQLException {
+		return false;
+	}
+
+	/**
+	 * Will a write on this column definately succeed?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc..
+	 * 
+	 * @return true if so
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public boolean isDefinitelyWritable(int column) throws SQLException {
+		return isWritable(column);
+	}
+
+	/**
+	 * Can you put a NULL in this column?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return one of the columnNullable values
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public int isNullable(int column) throws SQLException {
+		if (!getField(column).isNotNull()) {
+			return java.sql.ResultSetMetaData.columnNullable;
+		}
+
+		return java.sql.ResultSetMetaData.columnNoNulls;
+	}
+
+	/**
+	 * Is the column definitely not writable?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return true if so
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public boolean isReadOnly(int column) throws SQLException {
+		return getField(column).isReadOnly();
+	}
+
+	/**
+	 * Can the column be used in a WHERE clause? Basically for this, I split the
+	 * functions into two types: recognised types (which are always useable),
+	 * and OTHER types (which may or may not be useable). The OTHER types, for
+	 * now, I will assume they are useable. We should really query the catalog
+	 * to see if they are useable.
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return true if they can be used in a WHERE clause
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public boolean isSearchable(int column) throws SQLException {
+		return true;
+	}
+
+	/**
+	 * Is the column a signed number?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2...
+	 * 
+	 * @return true if so
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public boolean isSigned(int column) throws SQLException {
+		Field f = getField(column);
+		int sqlType = f.getSQLType();
+
+		switch (sqlType) {
+		case Types.TINYINT:
+		case Types.SMALLINT:
+		case Types.INTEGER:
+		case Types.BIGINT:
+		case Types.FLOAT:
+		case Types.REAL:
+		case Types.DOUBLE:
+		case Types.NUMERIC:
+		case Types.DECIMAL:
+			return !f.isUnsigned();
+
+		case Types.DATE:
+		case Types.TIME:
+		case Types.TIMESTAMP:
+			return false;
+
+		default:
+			return false;
+		}
+	}
+
+	/**
+	 * Is it possible for a write on the column to succeed?
+	 * 
+	 * @param column
+	 *            the first column is 1, the second is 2, etc.
+	 * 
+	 * @return true if so
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public boolean isWritable(int column) throws SQLException {
+		return !isReadOnly(column);
+	}
+
+	/**
+	 * Returns a string representation of this object
+	 * 
+	 * @return ...
+	 */
+	public String toString() {
+		StringBuffer toStringBuf = new StringBuffer();
+		toStringBuf.append(super.toString());
+		toStringBuf.append(" - Field level information: "); //$NON-NLS-1$
+
+		for (int i = 0; i < this.fields.length; i++) {
+			toStringBuf.append("\n\t"); //$NON-NLS-1$
+			toStringBuf.append(this.fields[i].toString());
+		}
+
+		return toStringBuf.toString();
+	}
+	
+	static String getClassNameForJavaType(int javaType, 
+			boolean isUnsigned, int mysqlTypeIfKnown, 
+			boolean isBinaryOrBlob,
+			boolean isOpaqueBinary) {
+		switch (javaType) {
+		case Types.BIT:
+		case Types.BOOLEAN:
+			return "java.lang.Boolean"; //$NON-NLS-1$
+
+		case Types.TINYINT:
+
+			if (isUnsigned) {
+				return "java.lang.Integer"; //$NON-NLS-1$
+			}
+
+			return "java.lang.Integer"; //$NON-NLS-1$
+
+		case Types.SMALLINT:
+
+			if (isUnsigned) {
+				return "java.lang.Integer"; //$NON-NLS-1$
+			}
+
+			return "java.lang.Integer"; //$NON-NLS-1$
+
+		case Types.INTEGER:
+
+			if (!isUnsigned || 
+					mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_INT24) {
+				return "java.lang.Integer"; //$NON-NLS-1$
+			}
+
+			return "java.lang.Long"; //$NON-NLS-1$
+
+		case Types.BIGINT:
+
+			if (!isUnsigned) {
+				return "java.lang.Long"; //$NON-NLS-1$
+			}
+
+			return "java.math.BigInteger"; //$NON-NLS-1$
+
+		case Types.DECIMAL:
+		case Types.NUMERIC:
+			return "java.math.BigDecimal"; //$NON-NLS-1$
+
+		case Types.REAL:
+			return "java.lang.Float"; //$NON-NLS-1$
+
+		case Types.FLOAT:
+		case Types.DOUBLE:
+			return "java.lang.Double"; //$NON-NLS-1$
+
+		case Types.CHAR:
+		case Types.VARCHAR:
+		case Types.LONGVARCHAR:
+			if (!isOpaqueBinary) {
+				return "java.lang.String"; //$NON-NLS-1$
+			}
+
+			return "[B";
+
+		case Types.BINARY:
+		case Types.VARBINARY:
+		case Types.LONGVARBINARY:
+
+			if (mysqlTypeIfKnown == MysqlDefs.FIELD_TYPE_GEOMETRY) {
+				return "[B";
+			} else if (isBinaryOrBlob) {
+				return "[B";
+			} else {
+				return "java.lang.String";
+			}
+
+		case Types.DATE:
+			return "java.sql.Date"; //$NON-NLS-1$
+
+		case Types.TIME:
+			return "java.sql.Time"; //$NON-NLS-1$
+
+		case Types.TIMESTAMP:
+			return "java.sql.Timestamp"; //$NON-NLS-1$
+
+		default:
+			return "java.lang.Object"; //$NON-NLS-1$
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowData.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowData.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowData.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,237 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+/**
+ * This interface abstracts away how row data is accessed by the result set. It
+ * is meant to allow a static implementation (Current version), and a streaming
+ * one.
+ * 
+ * @author dgan
+ */
+public interface RowData {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	/**
+	 * What's returned for the size of a result set when its size can not be
+	 * determined.
+	 */
+	public static final int RESULT_SET_SIZE_UNKNOWN = -1;
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Adds a row to this row data.
+	 * 
+	 * @param row
+	 *            the row to add
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	void addRow(byte[][] row) throws SQLException;
+
+	/**
+	 * Moves to after last.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	void afterLast() throws SQLException;
+
+	/**
+	 * Moves to before first.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	void beforeFirst() throws SQLException;
+
+	/**
+	 * Moves to before last so next el is the last el.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	void beforeLast() throws SQLException;
+
+	/**
+	 * We're done.
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	void close() throws SQLException;
+
+	/**
+	 * 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
+	 */
+	Object[] getAt(int index) throws SQLException;
+
+	/**
+	 * Returns the current position in the result set as a row number.
+	 * 
+	 * @return the current row number
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	int getCurrentRowNumber() throws SQLException;
+
+	/**
+	 * Returns the result set that 'owns' this RowData
+	 */
+	ResultSet getOwner();
+
+	/**
+	 * Returns true if another row exsists.
+	 * 
+	 * @return true if more rows
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	boolean hasNext() throws SQLException;
+
+	/**
+	 * Returns true if we got the last element.
+	 * 
+	 * @return true if after last row
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	boolean isAfterLast() throws SQLException;
+
+	/**
+	 * Returns if iteration has not occured yet.
+	 * 
+	 * @return true if before first row
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	boolean isBeforeFirst() throws SQLException;
+
+	/**
+	 * 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
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	boolean isDynamic() throws SQLException;
+
+	/**
+	 * Has no records.
+	 * 
+	 * @return true if no records
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	boolean isEmpty() throws SQLException;
+
+	/**
+	 * Are we on the first row of the result set?
+	 * 
+	 * @return true if on first row
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	boolean isFirst() throws SQLException;
+
+	/**
+	 * Are we on the last row of the result set?
+	 * 
+	 * @return true if on last row
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	boolean isLast() throws SQLException;
+
+	/**
+	 * 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
+	 */
+	void moveRowRelative(int rows) throws SQLException;
+
+	/**
+	 * Returns the next row.
+	 * 
+	 * @return the next row value
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	Object[] next() throws SQLException;
+
+	/**
+	 * Removes the row at the given index.
+	 * 
+	 * @param index
+	 *            the row to move to
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	void removeRow(int index) throws SQLException;
+
+	/**
+	 * 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
+	 */
+	void setCurrentRow(int rowNumber) throws SQLException;
+
+	/**
+	 * Set the result set that 'owns' this RowData
+	 * 
+	 * @param rs
+	 *            the result set that 'owns' this RowData
+	 */
+	void setOwner(ResultSet rs);
+
+	/**
+	 * Only works on non dynamic result sets.
+	 * 
+	 * @return the size of this row data
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	int size() throws SQLException;
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowDataDynamic.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowDataDynamic.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowDataDynamic.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,436 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+/**
+ * Allows streaming of MySQL data.
+ * 
+ * @author dgan
+ * @version $Id: RowDataDynamic.java 4891 2006-02-03 19:10:02Z mmatthews $
+ */
+public class RowDataDynamic implements RowData {
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	class OperationNotSupportedException extends SQLException {
+		OperationNotSupportedException() {
+			super(
+					Messages.getString("RowDataDynamic.10"), SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	private int columnCount;
+
+	private Field[] fields;
+
+	private int index = -1;
+
+	private MysqlIO io;
+
+	private boolean isAfterEnd = false;
+
+	private boolean isAtEnd = false;
+
+	private boolean isBinaryEncoded = false;
+
+	private Object[] nextRow;
+
+	private ResultSet owner;
+
+	private boolean streamerClosed = false;
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * 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 isBinaryEncoded
+	 *            is this data in native format?
+	 * @param colCount
+	 *            the number of columns
+	 * @throws SQLException
+	 *             if the next record can not be found
+	 */
+	public RowDataDynamic(MysqlIO io, int colCount, Field[] fields,
+			boolean isBinaryEncoded) throws SQLException {
+		this.io = io;
+		this.columnCount = colCount;
+		this.isBinaryEncoded = isBinaryEncoded;
+		this.fields = fields;
+		nextRecord();
+	}
+
+	/**
+	 * Adds a row to this row data.
+	 * 
+	 * @param row
+	 *            the row to add
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public void addRow(byte[][] 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 {
+
+		boolean hadMore = false;
+		int howMuchMore = 0;
+
+		// drain the rest of the records.
+		while (this.hasNext()) {
+			this.next();
+			hadMore = true;
+			howMuchMore++;
+
+			if (howMuchMore % 100 == 0) {
+				Thread.yield();
+			}
+		}
+
+		if (this.owner != null) {
+			Connection conn = this.owner.connection;
+
+			if (conn != null && conn.getUseUsageAdvisor()) {
+				if (hadMore) {
+
+					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,
+									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.owner = null;
+	}
+
+	/**
+	 * 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 Object[] getAt(int ind) throws SQLException {
+		notSupported();
+
+		return null;
+	}
+
+	/**
+	 * 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 {
+		notSupported();
+
+		return -1;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.RowData#getOwner()
+	 */
+	public ResultSet getOwner() {
+		return this.owner;
+	}
+
+	/**
+	 * Returns true if another row exsists.
+	 * 
+	 * @return true if more rows
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public boolean hasNext() throws SQLException {
+		boolean hasNext = (this.nextRow != null);
+
+		if (!hasNext && !this.streamerClosed) {
+			this.io.closeStreamer(this);
+			this.streamerClosed = true;
+		}
+
+		return hasNext;
+	}
+
+	/**
+	 * Returns true if we got the last element.
+	 * 
+	 * @return true if after last row
+	 * @throws SQLException
+	 *             if a database error occurs
+	 */
+	public boolean isAfterLast() throws SQLException {
+		return this.isAfterEnd;
+	}
+
+	/**
+	 * 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.index < 0;
+	}
+
+	/**
+	 * 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 {
+		notSupported();
+
+		return false;
+	}
+
+	/**
+	 * 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 {
+		notSupported();
+
+		return false;
+	}
+
+	/**
+	 * 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 {
+		notSupported();
+
+		return false;
+	}
+
+	/**
+	 * 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 Object[] next() throws SQLException {
+		if (this.index != Integer.MAX_VALUE) {
+			this.index++;
+		}
+
+		Object[] ret = this.nextRow;
+		nextRecord();
+
+		return ret;
+	}
+
+	private void nextRecord() throws SQLException {
+
+		try {
+			if (!this.isAtEnd) {
+
+				this.nextRow = this.io.nextRow(this.fields, this.columnCount,
+						this.isBinaryEncoded,
+						java.sql.ResultSet.CONCUR_READ_ONLY);
+
+				if (this.nextRow == null) {
+					this.isAtEnd = true;
+				}
+			} else {
+				this.isAfterEnd = true;
+			}
+		} catch (SQLException sqlEx) {
+			// don't wrap SQLExceptions
+			throw sqlEx;
+		} catch (Exception ex) {
+			String exceptionType = ex.getClass().getName();
+			String exceptionMessage = ex.getMessage();
+
+			exceptionMessage += Messages.getString("RowDataDynamic.7"); //$NON-NLS-1$
+			exceptionMessage += Util.stackTraceToString(ex);
+
+			throw new java.sql.SQLException(
+					Messages.getString("RowDataDynamic.8") //$NON-NLS-1$
+							+ exceptionType
+							+ Messages.getString("RowDataDynamic.9") + exceptionMessage, SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$
+		}
+	}
+
+	private void notSupported() throws SQLException {
+		throw new OperationNotSupportedException();
+	}
+
+	/**
+	 * 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();
+	}
+
+	// ~ Inner Classes
+	// ----------------------------------------------------------
+
+	/**
+	 * 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();
+	}
+
+	/**
+	 * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSet)
+	 */
+	public void setOwner(ResultSet rs) {
+		this.owner = rs;
+	}
+
+	/**
+	 * Only works on non dynamic result sets.
+	 * 
+	 * @return the size of this row data
+	 */
+	public int size() {
+		return RESULT_SET_SIZE_UNKNOWN;
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowDataStatic.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowDataStatic.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/RowDataStatic.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,256 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents an in-memory result set
+ * 
+ * @author dgan
+ * @version $Id: RowDataStatic.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class RowDataStatic implements RowData {
+	private int index;
+
+	ResultSet owner;
+
+	private List rows;
+
+	/**
+	 * Creates a new RowDataStatic object.
+	 * 
+	 * @param rows
+	 *            DOCUMENT ME!
+	 */
+	public RowDataStatic(ArrayList rows) {
+		this.index = -1;
+		this.rows = rows;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param row
+	 *            DOCUMENT ME!
+	 */
+	public void addRow(byte[][] row) {
+		this.rows.add(row);
+	}
+
+	/**
+	 * Moves to after last.
+	 */
+	public void afterLast() {
+		this.index = this.rows.size();
+	}
+
+	/**
+	 * Moves to before first.
+	 */
+	public void beforeFirst() {
+		this.index = -1;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 */
+	public void beforeLast() {
+		this.index = this.rows.size() - 2;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 */
+	public void close() {
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param atIndex
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public Object[] getAt(int atIndex) {
+		if ((atIndex < 0) || (atIndex >= this.rows.size())) {
+			return null;
+		}
+
+		return (Object[]) this.rows.get(atIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public int getCurrentRowNumber() {
+		return this.index;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.RowData#getOwner()
+	 */
+	public ResultSet getOwner() {
+		return this.owner;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean hasNext() {
+		boolean hasMore = (this.index + 1) < this.rows.size();
+
+		return hasMore;
+	}
+
+	/**
+	 * Returns true if we got the last element.
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isAfterLast() {
+		return this.index >= this.rows.size();
+	}
+
+	/**
+	 * Returns if iteration has not occured yet.
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isBeforeFirst() {
+		return (this.index == -1) && (this.rows.size() != 0);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isDynamic() {
+		return false;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isEmpty() {
+		return this.rows.size() == 0;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isFirst() {
+		return this.index == 0;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public boolean isLast() {
+		//
+		// You can never be on the 'last' row of
+		// an empty result set
+		//
+		if (this.rows.size() == 0) {
+			return false;
+		}
+
+		return (this.index == (this.rows.size() - 1));
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param rows
+	 *            DOCUMENT ME!
+	 */
+	public void moveRowRelative(int rowsToMove) {
+		this.index += rowsToMove;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public Object[] next() {
+		this.index++;
+
+		if (this.index < this.rows.size()) {
+			return (Object[]) this.rows.get(this.index);
+		}
+
+		return null;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param atIndex
+	 *            DOCUMENT ME!
+	 */
+	public void removeRow(int atIndex) {
+		this.rows.remove(atIndex);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param newIndex
+	 *            DOCUMENT ME!
+	 */
+	public void setCurrentRow(int newIndex) {
+		this.index = newIndex;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.RowData#setOwner(com.mysql.jdbc.ResultSet)
+	 */
+	public void setOwner(ResultSet rs) {
+		this.owner = rs;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public int size() {
+		return this.rows.size();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SQLError.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SQLError.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SQLError.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,948 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.DataTruncation;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+import com.mysql.jdbc.exceptions.MySQLDataException;
+import com.mysql.jdbc.exceptions.MySQLIntegrityConstraintViolationException;
+import com.mysql.jdbc.exceptions.MySQLNonTransientConnectionException;
+import com.mysql.jdbc.exceptions.MySQLSyntaxErrorException;
+import com.mysql.jdbc.exceptions.MySQLTransactionRollbackException;
+
+/**
+ * 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 $
+ */
+public class SQLError {
+	static final int ER_WARNING_NOT_COMPLETE_ROLLBACK = 1196;
+
+	private static Map mysqlToSql99State;
+
+	private static Map mysqlToSqlState;
+
+	public static final String SQL_STATE_BASE_TABLE_NOT_FOUND = "S0002"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS = "S0001"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND = "42S02"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_COLUMN_ALREADY_EXISTS = "S0021"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_COLUMN_NOT_FOUND = "S0022"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_COMMUNICATION_LINK_FAILURE = "08S01"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_CONNECTION_FAIL_DURING_TX = "08007"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_CONNECTION_IN_USE = "08002"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_CONNECTION_NOT_OPEN = "08003"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_CONNECTION_REJECTED = "08004"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_DATE_TRUNCATED = "01004"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_DATETIME_FIELD_OVERFLOW = "22008"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_DEADLOCK = "41000"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_DISCONNECT_ERROR = "01002"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_DIVISION_BY_ZERO = "22012"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_DRIVER_NOT_CAPABLE = "S1C00"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_ERROR_IN_ROW = "01S01"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_GENERAL_ERROR = "S1000"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_ILLEGAL_ARGUMENT = "S1009"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_INDEX_ALREADY_EXISTS = "S0011"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_INDEX_NOT_FOUND = "S0012"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST = "21S01"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_INVALID_AUTH_SPEC = "28000"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST = "22018"; // $NON_NLS-1$
+
+	public static final String SQL_STATE_INVALID_COLUMN_NUMBER = "S1002"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_INVALID_CONNECTION_ATTRIBUTE = "01S00"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_MEMORY_ALLOCATION_FAILURE = "S1001"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED = "01S04"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_NO_DEFAULT_FOR_COLUMN = "S0023"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_NO_ROWS_UPDATED_OR_DELETED = "01S03"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE = "22003"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_PRIVILEGE_NOT_REVOKED = "01006"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_SYNTAX_ERROR = "42000"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_TIMEOUT_EXPIRED = "S1T00"; //$NON-NLS-1$
+
+	public static final String SQL_STATE_TRANSACTION_RESOLUTION_UNKNOWN = "08007"; // $NON_NLS-1$
+
+	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$
+
+	private static Map sqlStateMessages;
+
+	static {
+		sqlStateMessages = new HashMap();
+		sqlStateMessages.put(SQL_STATE_DISCONNECT_ERROR, Messages
+				.getString("SQLError.35")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_DATE_TRUNCATED, Messages
+				.getString("SQLError.36")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_PRIVILEGE_NOT_REVOKED, Messages
+				.getString("SQLError.37")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_INVALID_CONNECTION_ATTRIBUTE, Messages
+				.getString("SQLError.38")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_ERROR_IN_ROW, Messages
+				.getString("SQLError.39")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_NO_ROWS_UPDATED_OR_DELETED, Messages
+				.getString("SQLError.40")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_MORE_THAN_ONE_ROW_UPDATED_OR_DELETED,
+				Messages.getString("SQLError.41")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_WRONG_NO_OF_PARAMETERS, Messages
+				.getString("SQLError.42")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_UNABLE_TO_CONNECT_TO_DATASOURCE,
+				Messages.getString("SQLError.43")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_CONNECTION_IN_USE, Messages
+				.getString("SQLError.44")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_CONNECTION_NOT_OPEN, Messages
+				.getString("SQLError.45")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_CONNECTION_REJECTED, Messages
+				.getString("SQLError.46")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_CONNECTION_FAIL_DURING_TX, Messages
+				.getString("SQLError.47")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_COMMUNICATION_LINK_FAILURE, Messages
+				.getString("SQLError.48")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST,
+				Messages.getString("SQLError.49")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE, Messages
+				.getString("SQLError.50")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_DATETIME_FIELD_OVERFLOW, Messages
+				.getString("SQLError.51")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_DIVISION_BY_ZERO, Messages
+				.getString("SQLError.52")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_DEADLOCK, Messages
+				.getString("SQLError.53")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_INVALID_AUTH_SPEC, Messages
+				.getString("SQLError.54")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_SYNTAX_ERROR, Messages
+				.getString("SQLError.55")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_BASE_TABLE_OR_VIEW_NOT_FOUND, Messages
+				.getString("SQLError.56")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_BASE_TABLE_OR_VIEW_ALREADY_EXISTS,
+				Messages.getString("SQLError.57")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_BASE_TABLE_NOT_FOUND, Messages
+				.getString("SQLError.58")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_INDEX_ALREADY_EXISTS, Messages
+				.getString("SQLError.59")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_INDEX_NOT_FOUND, Messages
+				.getString("SQLError.60")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_COLUMN_ALREADY_EXISTS, Messages
+				.getString("SQLError.61")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_COLUMN_NOT_FOUND, Messages
+				.getString("SQLError.62")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_NO_DEFAULT_FOR_COLUMN, Messages
+				.getString("SQLError.63")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_GENERAL_ERROR, Messages
+				.getString("SQLError.64")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_MEMORY_ALLOCATION_FAILURE, Messages
+				.getString("SQLError.65")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_INVALID_COLUMN_NUMBER, Messages
+				.getString("SQLError.66")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_ILLEGAL_ARGUMENT, Messages
+				.getString("SQLError.67")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_DRIVER_NOT_CAPABLE, Messages
+				.getString("SQLError.68")); //$NON-NLS-1$
+		sqlStateMessages.put(SQL_STATE_TIMEOUT_EXPIRED, Messages
+				.getString("SQLError.69")); //$NON-NLS-1$
+
+		mysqlToSqlState = new Hashtable();
+
+		//
+		// Communications Errors
+		//
+		// ER_CON_COUNT_ERROR 1040
+		// ER_BAD_HOST_ERROR 1042
+		// ER_HANDSHAKE_ERROR 1043
+		// 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),
+				SQL_STATE_COMMUNICATION_LINK_FAILURE);
+		mysqlToSqlState.put(new Integer(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);
+
+		//
+		// Authentication Errors
+		//
+		// ER_ACCESS_DENIED_ERROR 1045
+		//
+		mysqlToSqlState.put(new Integer(1045), SQL_STATE_INVALID_AUTH_SPEC);
+
+		//
+		// Resource errors
+		//
+		// ER_CANT_CREATE_FILE 1004
+		// ER_CANT_CREATE_TABLE 1005
+		// ER_CANT_LOCK 1015
+		// ER_DISK_FULL 1021
+		// ER_CON_COUNT_ERROR 1040
+		// ER_OUT_OF_RESOURCES 1041
+		//
+		// Out-of-memory errors
+		//
+		// ER_OUTOFMEMORY 1037
+		// ER_OUT_OF_SORTMEMORY 1038
+		//
+		mysqlToSqlState.put(new Integer(1037),
+				SQL_STATE_MEMORY_ALLOCATION_FAILURE);
+		mysqlToSqlState.put(new Integer(1038),
+				SQL_STATE_MEMORY_ALLOCATION_FAILURE);
+
+		//
+		// Syntax Errors
+		//
+		// 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);
+
+		//
+		// Invalid argument errors
+		//
+		// ER_WRONG_FIELD_WITH_GROUP 1055
+		// ER_WRONG_GROUP_FIELD 1056
+		// ER_WRONG_SUM_SELECT 1057
+		// ER_TOO_LONG_IDENT 1059
+		// ER_DUP_FIELDNAME 1060
+		// ER_DUP_KEYNAME 1061
+		// ER_DUP_ENTRY 1062
+		// ER_WRONG_FIELD_SPEC 1063
+		// ER_NONUNIQ_TABLE 1066
+		// ER_INVALID_DEFAULT 1067
+		// ER_MULTIPLE_PRI_KEY 1068
+		// ER_TOO_MANY_KEYS 1069
+		// ER_TOO_MANY_KEY_PARTS 1070
+		// ER_TOO_LONG_KEY 1071
+		// ER_KEY_COLUMN_DOES_NOT_EXIST 1072
+		// ER_BLOB_USED_AS_KEY 1073
+		// ER_TOO_BIG_FIELDLENGTH 1074
+		// ER_WRONG_AUTO_KEY 1075
+		// ER_NO_SUCH_INDEX 1082
+		// 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);
+
+		//
+		// ER_WRONG_VALUE_COUNT 1058
+		//
+		mysqlToSqlState.put(new Integer(1058),
+				SQL_STATE_INSERT_VALUE_LIST_NO_MATCH_COL_LIST);
+
+		// ER_CANT_CREATE_DB 1006
+		// ER_DB_CREATE_EXISTS 1007
+		// ER_DB_DROP_EXISTS 1008
+		// ER_DB_DROP_DELETE 1009
+		// ER_DB_DROP_RMDIR 1010
+		// ER_CANT_DELETE_FILE 1011
+		// ER_CANT_FIND_SYSTEM_REC 1012
+		// ER_CANT_GET_STAT 1013
+		// ER_CANT_GET_WD 1014
+		// ER_UNEXPECTED_EOF 1039
+		// ER_CANT_OPEN_FILE 1016
+		// ER_FILE_NOT_FOUND 1017
+		// ER_CANT_READ_DIR 1018
+		// ER_CANT_SET_WD 1019
+		// ER_CHECKREAD 1020
+		// ER_DUP_KEY 1022
+		// ER_ERROR_ON_CLOSE 1023
+		// ER_ERROR_ON_READ 1024
+		// ER_ERROR_ON_RENAME 1025
+		// ER_ERROR_ON_WRITE 1026
+		// ER_FILE_USED 1027
+		// ER_FILSORT_ABORT 1028
+		// ER_FORM_NOT_FOUND 1029
+		// ER_GET_ERRNO 1030
+		// ER_ILLEGAL_HA 1031
+		// ER_KEY_NOT_FOUND 1032
+		// ER_NOT_FORM_FILE 1033
+		// ER_DBACCESS_DENIED_ERROR 1044
+		// ER_NO_DB_ERROR 1046
+		// ER_BAD_NULL_ERROR 1048
+		// ER_BAD_DB_ERROR 1049
+		// ER_TABLE_EXISTS_ERROR 1050
+		// ER_BAD_TABLE_ERROR 1051
+		mysqlToSqlState.put(new Integer(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);
+
+		// ER_TEXTFILE_NOT_READABLE 1085
+		// ER_FILE_EXISTS_ERROR 1086
+		// ER_LOAD_INFO 1087
+		// ER_ALTER_INFO 1088
+		// ER_WRONG_SUB_KEY 1089
+		// ER_CANT_REMOVE_ALL_FIELDS 1090
+		// ER_CANT_DROP_FIELD_OR_KEY 1091
+		// 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);
+
+		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),
+				"23000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_OUTOFMEMORY),
+				"HY001");
+		mysqlToSql99State.put(new Integer(
+				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),
+				"08S01");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_HANDSHAKE_ERROR), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_DBACCESS_DENIED_ERROR), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_ACCESS_DENIED_ERROR), "28000");
+		mysqlToSql99State.put(new Integer(
+				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),
+				"23000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_SERVER_SHUTDOWN), "08S01");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_BAD_FIELD_ERROR), "42S22");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_FIELD_WITH_GROUP), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_GROUP_FIELD), "42000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_WRONG_SUM_SELECT), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_VALUE_COUNT), "21S01");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_LONG_IDENT),
+				"42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_FIELDNAME),
+				"42S21");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_KEYNAME),
+				"42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_ENTRY),
+				"23000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_WRONG_FIELD_SPEC), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_PARSE_ERROR),
+				"42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_EMPTY_QUERY),
+				"42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NONUNIQ_TABLE),
+				"42000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_INVALID_DEFAULT), "42000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_MULTIPLE_PRI_KEY), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_MANY_KEYS),
+				"42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_TOO_MANY_KEY_PARTS), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_LONG_KEY),
+				"42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_KEY_COLUMN_DOES_NOT_EXITS), "42000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_BLOB_USED_AS_KEY), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_TOO_BIG_FIELDLENGTH), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_WRONG_AUTO_KEY),
+				"42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_FORCING_CLOSE),
+				"08S01");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_IPSOCK_ERROR),
+				"08S01");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NO_SUCH_INDEX),
+				"42S12");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_FIELD_TERMINATORS), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_BLOBS_AND_NO_TERMINATED), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_CANT_REMOVE_ALL_FIELDS), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_CANT_DROP_FIELD_OR_KEY), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_BLOB_CANT_HAVE_DEFAULT), "42000");
+		mysqlToSql99State.put(new Integer(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),
+				"42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_UNKNOWN_PROCEDURE), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_PARAMCOUNT_TO_PROCEDURE), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_UNKNOWN_TABLE),
+				"42S02");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_FIELD_SPECIFIED_TWICE), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_UNSUPPORTED_EXTENSION), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_TABLE_MUST_HAVE_COLUMNS), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_UNKNOWN_CHARACTER_SET), "42000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_TOO_BIG_ROWSIZE), "42000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_WRONG_OUTER_JOIN), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NULL_COLUMN_IN_INDEX), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_PASSWORD_ANONYMOUS_USER), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_PASSWORD_NOT_ALLOWED), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_PASSWORD_NO_MATCH), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_VALUE_COUNT_ON_ROW), "21S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_INVALID_USE_OF_NULL), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_REGEXP_ERROR),
+				"42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_MIX_OF_GROUP_FUNC_AND_FIELDS), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NONEXISTING_GRANT), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_TABLEACCESS_DENIED_ERROR), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_COLUMNACCESS_DENIED_ERROR), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_ILLEGAL_GRANT_FOR_TABLE), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_GRANT_WRONG_HOST_OR_USER), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NO_SUCH_TABLE),
+				"42S02");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NONEXISTING_TABLE_GRANT), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NOT_ALLOWED_COMMAND), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_SYNTAX_ERROR),
+				"42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_ABORTING_CONNECTION), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NET_PACKET_TOO_LARGE), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NET_READ_ERROR_FROM_PIPE), "08S01");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_NET_FCNTL_ERROR), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NET_PACKETS_OUT_OF_ORDER), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NET_UNCOMPRESS_ERROR), "08S01");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NET_READ_ERROR),
+				"08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NET_READ_INTERRUPTED), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NET_ERROR_ON_WRITE), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NET_WRITE_INTERRUPTED), "08S01");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_TOO_LONG_STRING), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_BLOB), "42000");
+		mysqlToSql99State
+				.put(new Integer(
+						MysqlErrorNumbers.ER_TABLE_CANT_HANDLE_AUTO_INCREMENT),
+						"42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_COLUMN_NAME), "42000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_WRONG_KEY_COLUMN), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_DUP_UNIQUE),
+				"23000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_BLOB_KEY_WITHOUT_LENGTH), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_PRIMARY_CANT_HAVE_NULL), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_TOO_MANY_ROWS),
+				"42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_REQUIRES_PRIMARY_KEY), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_CHECK_NO_SUCH_TABLE), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_CHECK_NOT_IMPLEMENTED), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_CANT_DO_THIS_DURING_AN_TRANSACTION),
+				"25000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NEW_ABORTING_CONNECTION), "08S01");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_MASTER_NET_READ), "08S01");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_MASTER_NET_WRITE), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_TOO_MANY_USER_CONNECTIONS), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_READ_ONLY_TRANSACTION), "25000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NO_PERMISSION_TO_CREATE_USER), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_LOCK_DEADLOCK),
+				"40001");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NO_REFERENCED_ROW), "23000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_ROW_IS_REFERENCED), "23000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_CONNECT_TO_MASTER), "08S01");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT),
+				"21000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_USER_LIMIT_REACHED), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_NO_DEFAULT),
+				"42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_VALUE_FOR_VAR), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_TYPE_FOR_VAR), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_CANT_USE_OPTION_HERE), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NOT_SUPPORTED_YET), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_WRONG_FK_DEF),
+				"42000");
+		mysqlToSql99State.put(
+				new Integer(MysqlErrorNumbers.ER_OPERAND_COLUMNS), "21000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_SUBQUERY_NO_1_ROW), "21000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_ILLEGAL_REFERENCE), "42S22");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_DERIVED_MUST_HAVE_ALIAS), "42000");
+		mysqlToSql99State.put(new Integer(MysqlErrorNumbers.ER_SELECT_REDUCED),
+				"01000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_TABLENAME_NOT_ALLOWED_HERE), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_NOT_SUPPORTED_AUTH_MODE), "08004");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_SPATIAL_CANT_HAVE_NULL), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_COLLATION_CHARSET_MISMATCH), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WARN_TOO_FEW_RECORDS), "01000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WARN_TOO_MANY_RECORDS), "01000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WARN_NULL_TO_NOTNULL), "01000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WARN_DATA_OUT_OF_RANGE), "01000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WARN_DATA_TRUNCATED), "01000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_NAME_FOR_INDEX), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_WRONG_NAME_FOR_CATALOG), "42000");
+		mysqlToSql99State.put(new Integer(
+				MysqlErrorNumbers.ER_UNKNOWN_STORAGE_ENGINE), "42000");
+	}
+
+	/**
+	 * Turns output of 'SHOW WARNINGS' into JDBC SQLWarning instances.
+	 * 
+	 * If 'forTruncationOnly' is true, only looks for truncation warnings, and
+	 * actually throws DataTruncation as an exception.
+	 * 
+	 * @param connection
+	 *            the connection to use for getting warnings.
+	 * 
+	 * @return the SQLWarning chain (or null if no warnings)
+	 * 
+	 * @throws SQLException
+	 *             if the warnings could not be retrieved
+	 */
+	static SQLWarning convertShowWarningsToSQLWarnings(Connection connection)
+			throws SQLException {
+		return convertShowWarningsToSQLWarnings(connection, 0, false);
+	}
+
+	/**
+	 * Turns output of 'SHOW WARNINGS' into JDBC SQLWarning instances.
+	 * 
+	 * If 'forTruncationOnly' is true, only looks for truncation warnings, and
+	 * actually throws DataTruncation as an exception.
+	 * 
+	 * @param connection
+	 *            the connection to use for getting warnings.
+	 * @param warningCountIfKnown
+	 *            the warning count (if known), otherwise set it to 0.
+	 * @param forTruncationOnly
+	 *            if this method should only scan for data truncation warnings
+	 * 
+	 * @return the SQLWarning chain (or null if no warnings)
+	 * 
+	 * @throws SQLException
+	 *             if the warnings could not be retrieved, or if data truncation
+	 *             is being scanned for and truncations were found.
+	 */
+	static SQLWarning convertShowWarningsToSQLWarnings(Connection connection,
+			int warningCountIfKnown, boolean forTruncationOnly)
+			throws SQLException {
+		java.sql.Statement stmt = null;
+		java.sql.ResultSet warnRs = null;
+
+		SQLWarning currentWarning = null;
+
+		try {
+			if (warningCountIfKnown < 100) {
+				stmt = connection.createStatement();
+
+				if (stmt.getMaxRows() != 0) {
+					stmt.setMaxRows(0);
+				}
+			} else {
+				// stream large warning counts
+				stmt = connection.createStatement(
+						java.sql.ResultSet.TYPE_FORWARD_ONLY,
+						java.sql.ResultSet.CONCUR_READ_ONLY);
+				stmt.setFetchSize(Integer.MIN_VALUE);
+			}
+
+			/*
+			 * +---------+------+---------------------------------------------+ |
+			 * Level | Code | Message |
+			 * +---------+------+---------------------------------------------+ |
+			 * Warning | 1265 | Data truncated for column 'field1' at row 1 |
+			 * +---------+------+---------------------------------------------+
+			 */
+			warnRs = stmt.executeQuery("SHOW WARNINGS"); //$NON-NLS-1$
+
+			while (warnRs.next()) {
+				int code = warnRs.getInt("Code"); //$NON-NLS-1$
+
+				if (forTruncationOnly) {
+					if (code == 1265 || code == 1264) {
+						DataTruncation newTruncation = new MysqlDataTruncation(
+								warnRs.getString("Message"), 0, false, false, 0, 0); //$NON-NLS-1$
+
+						if (currentWarning == null) {
+							currentWarning = newTruncation;
+						} else {
+							currentWarning.setNextWarning(newTruncation);
+						}
+					}
+				} else {
+					String level = warnRs.getString("Level"); //$NON-NLS-1$
+					String message = warnRs.getString("Message"); //$NON-NLS-1$
+
+					SQLWarning newWarning = new SQLWarning(message, SQLError
+							.mysqlToSqlState(code, connection
+									.getUseSqlStateCodes()), code);
+
+					if (currentWarning == null) {
+						currentWarning = newWarning;
+					} else {
+						currentWarning.setNextWarning(newWarning);
+					}
+				}
+			}
+
+			if (forTruncationOnly && (currentWarning != null)) {
+				throw currentWarning;
+			}
+
+			return currentWarning;
+		} finally {
+			SQLException reThrow = null;
+
+			if (warnRs != null) {
+				try {
+					warnRs.close();
+				} catch (SQLException sqlEx) {
+					reThrow = sqlEx;
+				}
+			}
+
+			if (stmt != null) {
+				try {
+					stmt.close();
+				} catch (SQLException sqlEx) {
+					// ideally, we'd use chained exceptions here,
+					// but we still support JDK-1.2.x with this driver
+					// which doesn't have them....
+					reThrow = sqlEx;
+				}
+			}
+
+			if (reThrow != null) {
+				throw reThrow;
+			}
+		}
+	}
+
+	public static void dumpSqlStatesMappingsAsXml() throws Exception {
+		TreeMap allErrorNumbers = new TreeMap();
+		Map mysqlErrorNumbersToNames = new HashMap();
+
+		Integer errorNumber = null;
+
+		// 
+		// First create a list of all 'known' error numbers that
+		// are mapped.
+		//
+		for (Iterator mysqlErrorNumbers = mysqlToSql99State.keySet().iterator(); mysqlErrorNumbers
+				.hasNext();) {
+			errorNumber = (Integer) mysqlErrorNumbers.next();
+			allErrorNumbers.put(errorNumber, errorNumber);
+		}
+
+		for (Iterator mysqlErrorNumbers = mysqlToSqlState.keySet().iterator(); mysqlErrorNumbers
+				.hasNext();) {
+			errorNumber = (Integer) mysqlErrorNumbers.next();
+			allErrorNumbers.put(errorNumber, errorNumber);
+		}
+
+		//
+		// Now create a list of the actual MySQL error numbers we know about
+		//
+		java.lang.reflect.Field[] possibleFields = MysqlErrorNumbers.class
+				.getDeclaredFields();
+
+		for (int i = 0; i < possibleFields.length; i++) {
+			String fieldName = possibleFields[i].getName();
+
+			if (fieldName.startsWith("ER_")) {
+				mysqlErrorNumbersToNames.put(possibleFields[i].get(null),
+						fieldName);
+			}
+		}
+
+		System.out.println("<ErrorMappings>");
+
+		for (Iterator allErrorNumbersIter = allErrorNumbers.keySet().iterator(); allErrorNumbersIter
+				.hasNext();) {
+			errorNumber = (Integer) allErrorNumbersIter.next();
+
+			String sql92State = mysqlToSql99(errorNumber.intValue());
+			String oldSqlState = mysqlToXOpen(errorNumber.intValue());
+
+			System.out.println("   <ErrorMapping mysqlErrorNumber=\""
+					+ errorNumber + "\" mysqlErrorName=\""
+					+ mysqlErrorNumbersToNames.get(errorNumber)
+					+ "\" legacySqlState=\""
+					+ ((oldSqlState == null) ? "" : oldSqlState)
+					+ "\" sql92SqlState=\""
+					+ ((sql92State == null) ? "" : sql92State) + "\"/>");
+		}
+
+		System.out.println("</ErrorMappings>");
+	}
+
+	static String get(String stateCode) {
+		return (String) sqlStateMessages.get(stateCode);
+	}
+
+	private static String mysqlToSql99(int errno) {
+		Integer err = new Integer(errno);
+
+		if (mysqlToSql99State.containsKey(err)) {
+			return (String) mysqlToSql99State.get(err);
+		}
+
+		return "HY000";
+	}
+
+	/**
+	 * Map MySQL error codes to X/Open or SQL-92 error codes
+	 * 
+	 * @param errno
+	 *            the MySQL error code
+	 * 
+	 * @return the corresponding X/Open or SQL-92 error code
+	 */
+	static String mysqlToSqlState(int errno, boolean useSql92States) {
+		if (useSql92States) {
+			return mysqlToSql99(errno);
+		}
+
+		return mysqlToXOpen(errno);
+	}
+
+	private static String mysqlToXOpen(int errno) {
+		Integer err = new Integer(errno);
+
+		if (mysqlToSqlState.containsKey(err)) {
+			return (String) mysqlToSqlState.get(err);
+		}
+
+		return SQL_STATE_GENERAL_ERROR;
+	}
+
+	/*
+	 * SQL State Class SQLNonTransientException Subclass 08
+	 * SQLNonTransientConnectionException 22 SQLDataException 23
+	 * SQLIntegrityConstraintViolationException N/A
+	 * SQLInvalidAuthorizationException 42 SQLSyntaxErrorException
+	 * 
+	 * SQL State Class SQLTransientException Subclass 08
+	 * SQLTransientConnectionException 40 SQLTransactionRollbackException N/A
+	 * SQLTimeoutException
+	 */
+
+	public static SQLException createSQLException(String message,
+			String sqlState) {
+		if (sqlState != null) {
+			if (sqlState.startsWith("08")) {
+				return new MySQLNonTransientConnectionException(message,
+						sqlState);
+			}
+
+			if (sqlState.startsWith("22")) {
+				return new MySQLDataException(message, sqlState);
+			}
+
+			if (sqlState.startsWith("23")) {
+				return new MySQLIntegrityConstraintViolationException(message,
+						sqlState);
+			}
+
+			if (sqlState.startsWith("42")) {
+				return new MySQLSyntaxErrorException(message, sqlState);
+			}
+
+			if (sqlState.startsWith("40")) {
+				return new MySQLTransactionRollbackException(message, sqlState);
+			}
+		}
+
+		return new SQLException(message, sqlState);
+	}
+
+	public static SQLException createSQLException(String message) {
+		return new SQLException(message);
+	}
+
+	public static SQLException createSQLException(String message,
+			String sqlState, int vendorErrorCode) {
+		if (sqlState != null) {
+			if (sqlState.startsWith("08")) {
+				return new MySQLNonTransientConnectionException(message,
+						sqlState, vendorErrorCode);
+			}
+
+			if (sqlState.startsWith("22")) {
+				return new MySQLDataException(message, sqlState,
+						vendorErrorCode);
+			}
+
+			if (sqlState.startsWith("23")) {
+				return new MySQLIntegrityConstraintViolationException(message,
+						sqlState, vendorErrorCode);
+			}
+
+			if (sqlState.startsWith("42")) {
+				return new MySQLSyntaxErrorException(message, sqlState,
+						vendorErrorCode);
+			}
+
+			if (sqlState.startsWith("40")) {
+				return new MySQLTransactionRollbackException(message, sqlState,
+						vendorErrorCode);
+			}
+		}
+
+		return new SQLException(message, sqlState, vendorErrorCode);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Security.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Security.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Security.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,353 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * Methods for doing secure authentication with MySQL-4.1 and newer.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: Security.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+class Security {
+	private static final char PVERSION41_CHAR = '*';
+
+	private static final int SHA1_HASH_SIZE = 20;
+
+	/**
+	 * Returns hex value for given char
+	 */
+	private static int charVal(char c) {
+		return ((c >= '0') && (c <= '9')) ? (c - '0')
+				: (((c >= 'A') && (c <= 'Z')) ? (c - 'A' + 10) : (c - 'a' + 10));
+	}
+
+	/*
+	 * Convert password in salted form to binary string password and hash-salt
+	 * For old password this involes one more hashing
+	 * 
+	 * SYNOPSIS get_hash_and_password() salt IN Salt to convert from pversion IN
+	 * Password version to use hash OUT Store zero ended hash here bin_password
+	 * OUT Store binary password here (no zero at the end)
+	 * 
+	 * RETURN 0 for pre 4.1 passwords !0 password version char for newer
+	 * passwords
+	 */
+
+	/**
+	 * Creates key from old password to decode scramble Used in 4.1
+	 * authentication with passwords stored pre-4.1 hashing.
+	 * 
+	 * @param passwd
+	 *            the password to create the key from
+	 * 
+	 * @return 20 byte generated key
+	 * 
+	 * @throws NoSuchAlgorithmException
+	 *             if the message digest 'SHA-1' is not available.
+	 */
+	static byte[] createKeyFromOldPassword(String passwd)
+			throws NoSuchAlgorithmException {
+		/* At first hash password to the string stored in password */
+		passwd = makeScrambledPassword(passwd);
+
+		/* Now convert it to the salt form */
+		int[] salt = getSaltFromPassword(passwd);
+
+		/* Finally get hash and bin password from salt */
+		return getBinaryPassword(salt, false);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param salt
+	 *            DOCUMENT ME!
+	 * @param usingNewPasswords
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws NoSuchAlgorithmException
+	 *             if the message digest 'SHA-1' is not available.
+	 */
+	static byte[] getBinaryPassword(int[] salt, boolean usingNewPasswords)
+			throws NoSuchAlgorithmException {
+		int val = 0;
+
+		byte[] binaryPassword = new byte[SHA1_HASH_SIZE]; /*
+															 * Binary password
+															 * loop pointer
+															 */
+
+		if (usingNewPasswords) /* New password version assumed */{
+			int pos = 0;
+
+			for (int i = 0; i < 4; i++) /* Iterate over these elements */{
+				val = salt[i];
+
+				for (int t = 3; t >= 0; t--) {
+					binaryPassword[pos++] = (byte) (val & 255);
+					val >>= 8; /* Scroll 8 bits to get next part */
+				}
+			}
+
+			return binaryPassword;
+		}
+
+		int offset = 0;
+
+		for (int i = 0; i < 2; i++) /* Iterate over these elements */{
+			val = salt[i];
+
+			for (int t = 3; t >= 0; t--) {
+				binaryPassword[t + offset] = (byte) (val % 256);
+				val >>= 8; /* Scroll 8 bits to get next part */
+			}
+
+			offset += 4;
+		}
+
+		MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
+
+		md.update(binaryPassword, 0, 8);
+
+		return md.digest();
+	}
+
+	private static int[] getSaltFromPassword(String password) {
+		int[] result = new int[6];
+
+		if ((password == null) || (password.length() == 0)) {
+			return result;
+		}
+
+		if (password.charAt(0) == PVERSION41_CHAR) {
+			// new password
+			String saltInHex = password.substring(1, 5);
+
+			int val = 0;
+
+			for (int i = 0; i < 4; i++) {
+				val = (val << 4) + charVal(saltInHex.charAt(i));
+			}
+
+			return result;
+		}
+
+		int resultPos = 0;
+		int pos = 0;
+		int length = password.length();
+
+		while (pos < length) {
+			int val = 0;
+
+			for (int i = 0; i < 8; i++) {
+				val = (val << 4) + charVal(password.charAt(pos++));
+			}
+
+			result[resultPos++] = val;
+		}
+
+		return result;
+	}
+
+	private static String longToHex(long val) {
+		String longHex = Long.toHexString(val);
+
+		int length = longHex.length();
+
+		if (length < 8) {
+			int padding = 8 - length;
+			StringBuffer buf = new StringBuffer();
+
+			for (int i = 0; i < padding; i++) {
+				buf.append("0"); //$NON-NLS-1$
+			}
+
+			buf.append(longHex);
+
+			return buf.toString();
+		}
+
+		return longHex.substring(0, 8);
+	}
+
+	/**
+	 * Creates password to be stored in user database from raw string.
+	 * 
+	 * Handles Pre-MySQL 4.1 passwords.
+	 * 
+	 * @param password
+	 *            plaintext password
+	 * 
+	 * @return scrambled password
+	 * 
+	 * @throws NoSuchAlgorithmException
+	 *             if the message digest 'SHA-1' is not available.
+	 */
+	static String makeScrambledPassword(String password)
+			throws NoSuchAlgorithmException {
+		long[] passwordHash = Util.newHash(password);
+		StringBuffer scramble = new StringBuffer();
+
+		scramble.append(longToHex(passwordHash[0]));
+		scramble.append(longToHex(passwordHash[1]));
+
+		return scramble.toString();
+	}
+
+	/**
+	 * Encrypt/Decrypt function used for password encryption in authentication
+	 * 
+	 * Simple XOR is used here but it is OK as we crypt random strings
+	 * 
+	 * @param from
+	 *            IN Data for encryption
+	 * @param to
+	 *            OUT Encrypt data to the buffer (may be the same)
+	 * @param password
+	 *            IN Password used for encryption (same length)
+	 * @param length
+	 *            IN Length of data to encrypt
+	 */
+	static void passwordCrypt(byte[] from, byte[] to, byte[] password,
+			int length) {
+		int pos = 0;
+
+		while ((pos < from.length) && (pos < length)) {
+			to[pos] = (byte) (from[pos] ^ password[pos]);
+			pos++;
+		}
+	}
+
+	/**
+	 * Stage one password hashing, used in MySQL 4.1 password handling
+	 * 
+	 * @param password
+	 *            plaintext password
+	 * 
+	 * @return stage one hash of password
+	 * 
+	 * @throws NoSuchAlgorithmException
+	 *             if the message digest 'SHA-1' is not available.
+	 */
+	static byte[] passwordHashStage1(String password)
+			throws NoSuchAlgorithmException {
+		MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
+		StringBuffer cleansedPassword = new StringBuffer();
+
+		int passwordLength = password.length();
+
+		for (int i = 0; i < passwordLength; i++) {
+			char c = password.charAt(i);
+
+			if ((c == ' ') || (c == '\t')) {
+				continue; /* skip space in password */
+			}
+
+			cleansedPassword.append(c);
+		}
+
+		return md.digest(cleansedPassword.toString().getBytes());
+	}
+
+	/**
+	 * Stage two password hashing used in MySQL 4.1 password handling
+	 * 
+	 * @param hash
+	 *            from passwordHashStage1
+	 * @param salt
+	 *            salt used for stage two hashing
+	 * 
+	 * @return result of stage two password hash
+	 * 
+	 * @throws NoSuchAlgorithmException
+	 *             if the message digest 'SHA-1' is not available.
+	 */
+	static byte[] passwordHashStage2(byte[] hashedPassword, byte[] salt)
+			throws NoSuchAlgorithmException {
+		MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
+
+		// hash 4 bytes of salt
+		md.update(salt, 0, 4);
+
+		md.update(hashedPassword, 0, SHA1_HASH_SIZE);
+
+		return md.digest();
+	}
+
+	// SERVER: public_seed=create_random_string()
+	// send(public_seed)
+	//
+	// CLIENT: recv(public_seed)
+	// hash_stage1=sha1("password")
+	// hash_stage2=sha1(hash_stage1)
+	// reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
+	//
+	// // this three steps are done in scramble()
+	//
+	// send(reply)
+	//
+	//
+	// SERVER: recv(reply)
+	// hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
+	// candidate_hash2=sha1(hash_stage1)
+	// check(candidate_hash2==hash_stage2)
+	static byte[] scramble411(String password, String seed)
+			throws NoSuchAlgorithmException {
+		MessageDigest md = MessageDigest.getInstance("SHA-1"); //$NON-NLS-1$
+
+		byte[] passwordHashStage1 = md.digest(password.getBytes());
+		md.reset();
+
+		byte[] passwordHashStage2 = md.digest(passwordHashStage1);
+		md.reset();
+
+		byte[] seedAsBytes = seed.getBytes(); // for debugging
+		md.update(seedAsBytes);
+		md.update(passwordHashStage2);
+
+		byte[] toBeXord = md.digest();
+
+		int numToXor = toBeXord.length;
+
+		for (int i = 0; i < numToXor; i++) {
+			toBeXord[i] = (byte) (toBeXord[i] ^ passwordHashStage1[i]);
+		}
+
+		return toBeXord;
+	}
+
+	/**
+	 * Prevent construction.
+	 */
+	private Security() {
+		super();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ServerPreparedStatement.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ServerPreparedStatement.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/ServerPreparedStatement.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,2445 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.Statement.CancelTask;
+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.math.BigDecimal;
+
+import java.net.URL;
+
+import java.sql.Array;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.ParameterMetaData;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.sql.Types;
+
+import java.util.ArrayList;
+import java.util.BitSet;
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * JDBC Interface for MySQL-4.1 and newer server-side PreparedStatements.
+ * 
+ * @author Mark Matthews
+ * @version $Id: ServerPreparedStatement.java,v 1.1.2.2 2005/05/17 14:58:56
+ *          mmatthews Exp $
+ */
+public class ServerPreparedStatement extends PreparedStatement {
+	protected static final int BLOB_STREAM_READ_BUF_SIZE = 8192;
+
+	static class BatchedBindValues {
+		BindValue[] batchedParameterValues;
+
+		BatchedBindValues(BindValue[] paramVals) {
+			int numParams = paramVals.length;
+
+			this.batchedParameterValues = new BindValue[numParams];
+
+			for (int i = 0; i < numParams; i++) {
+				this.batchedParameterValues[i] = new BindValue(paramVals[i]);
+			}
+		}
+	}
+
+	static class BindValue {
+
+		long boundBeforeExecutionNum = 0;
+		
+		long bindLength; /* Default length of data */
+
+		int bufferType; /* buffer type */
+
+		byte byteBinding;
+
+		double doubleBinding;
+
+		float floatBinding;
+
+		int intBinding;
+
+		boolean isLongData; /* long data indicator */
+
+		boolean isNull; /* NULL indicator */
+
+		boolean isSet = false; /* has this parameter been set? */
+
+		long longBinding;
+
+		short shortBinding;
+
+		Object value; /* The value to store */
+
+		BindValue() {
+		}
+
+		BindValue(BindValue copyMe) {
+			this.value = copyMe.value;
+			this.isSet = copyMe.isSet;
+			this.isLongData = copyMe.isLongData;
+			this.isNull = copyMe.isNull;
+			this.bufferType = copyMe.bufferType;
+			this.bindLength = copyMe.bindLength;
+			this.byteBinding = copyMe.byteBinding;
+			this.shortBinding = copyMe.shortBinding;
+			this.intBinding = copyMe.intBinding;
+			this.longBinding = copyMe.longBinding;
+			this.floatBinding = copyMe.floatBinding;
+			this.doubleBinding = copyMe.doubleBinding;
+		}
+
+		void reset() {
+			this.isSet = false;
+			this.value = null;
+			this.isLongData = false;
+
+			this.byteBinding = 0;
+			this.shortBinding = 0;
+			this.intBinding = 0;
+			this.longBinding = 0L;
+			this.floatBinding = 0;
+			this.doubleBinding = 0D;
+		}
+
+		public String toString() {
+			return toString(false);
+		}
+
+		public String toString(boolean quoteIfNeeded) {
+			if (this.isLongData) {
+				return "' STREAM DATA '";
+			}
+
+			switch (this.bufferType) {
+			case MysqlDefs.FIELD_TYPE_TINY:
+				return String.valueOf(byteBinding);
+			case MysqlDefs.FIELD_TYPE_SHORT:
+				return String.valueOf(shortBinding);
+			case MysqlDefs.FIELD_TYPE_LONG:
+				return String.valueOf(intBinding);
+			case MysqlDefs.FIELD_TYPE_LONGLONG:
+				return String.valueOf(longBinding);
+			case MysqlDefs.FIELD_TYPE_FLOAT:
+				return String.valueOf(floatBinding);
+			case MysqlDefs.FIELD_TYPE_DOUBLE:
+				return String.valueOf(doubleBinding);
+			case MysqlDefs.FIELD_TYPE_TIME:
+			case MysqlDefs.FIELD_TYPE_DATE:
+			case MysqlDefs.FIELD_TYPE_DATETIME:
+			case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+			case MysqlDefs.FIELD_TYPE_VAR_STRING:
+			case MysqlDefs.FIELD_TYPE_STRING:
+			case MysqlDefs.FIELD_TYPE_VARCHAR:
+				if (quoteIfNeeded) {
+					return "'" + String.valueOf(value) + "'";
+				} else {
+					return String.valueOf(value);
+				}
+			default:
+				if (value instanceof byte[]) {
+					return "byte data";
+
+				} else {
+					if (quoteIfNeeded) {
+						return "'" + String.valueOf(value) + "'";
+					} else {
+						return String.valueOf(value);
+					}
+				}
+			}
+		}
+	}
+
+	/* 1 (length) + 2 (year) + 1 (month) + 1 (day) */
+	private static final byte MAX_DATE_REP_LENGTH = (byte) 5;
+
+	/*
+	 * 1 (length) + 2 (year) + 1 (month) + 1 (day) + 1 (hour) + 1 (minute) + 1
+	 * (second) + 4 (microseconds)
+	 */
+	private static final byte MAX_DATETIME_REP_LENGTH = 12;
+
+	/*
+	 * 1 (length) + 1 (is negative) + 4 (day count) + 1 (hour) + 1 (minute) + 1
+	 * (seconds) + 4 (microseconds)
+	 */
+	private static final byte MAX_TIME_REP_LENGTH = 13;
+
+	private void storeTime(Buffer intoBuf, Time tm) throws SQLException {
+		
+		intoBuf.ensureCapacity(9);
+		intoBuf.writeByte((byte) 8); // length
+		intoBuf.writeByte((byte) 0); // neg flag
+		intoBuf.writeLong(0); // tm->day, not used
+
+		Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
+		
+		synchronized (sessionCalendar) {
+			java.util.Date oldTime = sessionCalendar.getTime();
+			try {
+				sessionCalendar.setTime(tm);
+				intoBuf.writeByte((byte) sessionCalendar.get(Calendar.HOUR_OF_DAY));
+				intoBuf.writeByte((byte) sessionCalendar.get(Calendar.MINUTE));
+				intoBuf.writeByte((byte) sessionCalendar.get(Calendar.SECOND));
+
+				// intoBuf.writeLongInt(0); // tm-second_part
+			} finally {
+				sessionCalendar.setTime(oldTime);
+			}
+		}
+	}
+
+	/**
+	 * Flag indicating whether or not the long parameters have been 'switched'
+	 * back to normal parameters. We can not execute() if clearParameters()
+	 * hasn't been called in this case.
+	 */
+	private boolean detectedLongParameterSwitch = false;
+
+	/**
+	 * The number of fields in the result set (if any) for this
+	 * PreparedStatement.
+	 */
+	private int fieldCount;
+
+	/** Has this prepared statement been marked invalid? */
+	private boolean invalid = false;
+
+	/** If this statement has been marked invalid, what was the reason? */
+	private SQLException invalidationException;
+
+	/** Does this query modify data? */
+	private boolean isSelectQuery;
+
+	private Buffer outByteBuffer;
+
+	/** Bind values for individual fields */
+	private BindValue[] parameterBindings;
+
+	/** Field-level metadata for parameters */
+	private Field[] parameterFields;
+
+	/** Field-level metadata for result sets. */
+	private Field[] resultFields;
+
+	/** Do we need to send/resend types to the server? */
+	private boolean sendTypesToServer = false;
+
+	/** The ID that the server uses to identify this PreparedStatement */
+	private long serverStatementId;
+
+	/** The type used for string bindings, changes from version-to-version */
+	private int stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
+
+	private boolean serverNeedsResetBeforeEachExecution;
+
+	/**
+	 * Creates a new ServerPreparedStatement object.
+	 * 
+	 * @param conn
+	 *            the connection creating us.
+	 * @param sql
+	 *            the SQL containing the statement to prepare.
+	 * @param catalog
+	 *            the catalog in use when we were created.
+	 * 
+	 * @throws SQLException
+	 *             If an error occurs
+	 */
+	public ServerPreparedStatement(Connection 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$
+		
+		if (this.connection.versionMeetsMinimum(5, 0, 0)) {
+			this.serverNeedsResetBeforeEachExecution = 
+				!this.connection.versionMeetsMinimum(5, 0, 3);
+		} else {
+			this.serverNeedsResetBeforeEachExecution =
+				!this.connection.versionMeetsMinimum(4, 1, 10);
+		}
+		
+		this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23);
+		this.hasLimitClause = (StringUtils.indexOfIgnoreCase(sql, "LIMIT") != -1); //$NON-NLS-1$
+		this.firstCharOfStmt = StringUtils.firstNonWsCharUc(sql);
+		this.originalSql = sql;
+
+		if (this.connection.versionMeetsMinimum(4, 1, 2)) {
+			this.stringTypeCode = MysqlDefs.FIELD_TYPE_VAR_STRING;
+		} else {
+			this.stringTypeCode = MysqlDefs.FIELD_TYPE_STRING;
+		}
+
+		try {
+			serverPrepare(sql);
+		} catch (SQLException sqlEx) {
+			realClose(false, true);
+			// don't wrap SQLExceptions
+			throw sqlEx;
+		} catch (Exception ex) {
+			realClose(false, true);
+
+			throw SQLError.createSQLException(ex.toString(),
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+		
+		setResultSetType(resultSetType);
+		setResultSetConcurrency(resultSetConcurrency);
+	}
+
+	/**
+	 * JDBC 2.0 Add a set of parameters to the batch.
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 * 
+	 * @see Statement#addBatch
+	 */
+	public synchronized void addBatch() throws SQLException {
+		checkClosed();
+
+		if (this.batchedArgs == null) {
+			this.batchedArgs = new ArrayList();
+		}
+
+		this.batchedArgs.add(new BatchedBindValues(this.parameterBindings));
+	}
+
+	protected String asSql(boolean quoteStreamsAndUnknowns) throws SQLException {
+
+		if (this.isClosed) {
+			return "statement has been closed, no further internal information available";
+		}
+		
+		PreparedStatement pStmtForSub = null;
+
+		try {
+			pStmtForSub = new PreparedStatement(this.connection,
+					this.originalSql, this.currentCatalog);
+
+			int numParameters = pStmtForSub.parameterCount;
+			int ourNumParameters = this.parameterCount;
+
+			for (int i = 0; (i < numParameters) && (i < ourNumParameters); i++) {
+				if (this.parameterBindings[i] != null) {
+					if (this.parameterBindings[i].isNull) {
+						pStmtForSub.setNull(i + 1, Types.NULL);
+					} else {
+						BindValue bindValue = this.parameterBindings[i];
+
+						//
+						// Handle primitives first
+						//
+						switch (bindValue.bufferType) {
+
+						case MysqlDefs.FIELD_TYPE_TINY:
+							pStmtForSub.setByte(i + 1, bindValue.byteBinding);
+							break;
+						case MysqlDefs.FIELD_TYPE_SHORT:
+							pStmtForSub.setShort(i + 1, bindValue.shortBinding);
+							break;
+						case MysqlDefs.FIELD_TYPE_LONG:
+							pStmtForSub.setInt(i + 1, bindValue.intBinding);
+							break;
+						case MysqlDefs.FIELD_TYPE_LONGLONG:
+							pStmtForSub.setLong(i + 1, bindValue.longBinding);
+							break;
+						case MysqlDefs.FIELD_TYPE_FLOAT:
+							pStmtForSub.setFloat(i + 1, bindValue.floatBinding);
+							break;
+						case MysqlDefs.FIELD_TYPE_DOUBLE:
+							pStmtForSub.setDouble(i + 1,
+									bindValue.doubleBinding);
+							break;
+						default:
+							pStmtForSub.setObject(i + 1,
+									this.parameterBindings[i].value);
+							break;
+						}
+					}
+				}
+			}
+
+			return pStmtForSub.asSql(quoteStreamsAndUnknowns);
+		} finally {
+			if (pStmtForSub != null) {
+				try {
+					pStmtForSub.close();
+				} catch (SQLException sqlEx) {
+					; // ignore
+				}
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.Statement#checkClosed()
+	 */
+	protected void checkClosed() throws SQLException {
+		if (this.invalid) {
+			throw this.invalidationException;
+		}
+
+		super.checkClosed();
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#clearParameters()
+	 */
+	public void clearParameters() throws SQLException {
+		checkClosed();
+		clearParametersInternal(true);
+	}
+
+	private void clearParametersInternal(boolean clearServerParameters)
+			throws SQLException {
+		boolean hadLongData = false;
+
+		if (this.parameterBindings != null) {
+			for (int i = 0; i < this.parameterCount; i++) {
+				if ((this.parameterBindings[i] != null)
+						&& this.parameterBindings[i].isLongData) {
+					hadLongData = true;
+				}
+
+				this.parameterBindings[i].reset();
+			}
+		}
+
+		if (clearServerParameters && hadLongData) {
+			serverResetStatement();
+
+			this.detectedLongParameterSwitch = false;
+		}
+	}
+
+	protected boolean isCached = false;
+
+	protected void setClosed(boolean flag) {
+		this.isClosed = flag;
+	}
+	/**
+	 * @see java.sql.Statement#close()
+	 */
+	public void close() throws SQLException {
+		if (this.isCached) {
+			this.isClosed = true;
+			this.connection.recachePreparedStatement(this);
+			return;
+		}
+		
+		realClose(true, true);
+	}
+
+	private void dumpCloseForTestcase() {
+		StringBuffer buf = new StringBuffer();
+		this.connection.generateConnectionCommentBlock(buf);
+		buf.append("DEALLOCATE PREPARE debug_stmt_");
+		buf.append(this.statementId);
+		buf.append(";\n");
+
+		this.connection.dumpTestcaseQuery(buf.toString());
+	}
+
+	private void dumpExecuteForTestcase() throws SQLException {
+		StringBuffer buf = new StringBuffer();
+
+		for (int i = 0; i < this.parameterCount; i++) {
+			this.connection.generateConnectionCommentBlock(buf);
+
+			buf.append("SET @debug_stmt_param");
+			buf.append(this.statementId);
+			buf.append("_");
+			buf.append(i);
+			buf.append("=");
+
+			if (this.parameterBindings[i].isNull) {
+				buf.append("NULL");
+			} else {
+				buf.append(this.parameterBindings[i].toString(true));
+			}
+
+			buf.append(";\n");
+		}
+
+		this.connection.generateConnectionCommentBlock(buf);
+
+		buf.append("EXECUTE debug_stmt_");
+		buf.append(this.statementId);
+
+		if (this.parameterCount > 0) {
+			buf.append(" USING ");
+			for (int i = 0; i < this.parameterCount; i++) {
+				if (i > 0) {
+					buf.append(", ");
+				}
+
+				buf.append("@debug_stmt_param");
+				buf.append(this.statementId);
+				buf.append("_");
+				buf.append(i);
+
+			}
+		}
+
+		buf.append(";\n");
+
+		this.connection.dumpTestcaseQuery(buf.toString());
+	}
+
+	private void dumpPrepareForTestcase() throws SQLException {
+
+		StringBuffer buf = new StringBuffer(this.originalSql.length() + 64);
+
+		this.connection.generateConnectionCommentBlock(buf);
+
+		buf.append("PREPARE debug_stmt_");
+		buf.append(this.statementId);
+		buf.append(" FROM \"");
+		buf.append(this.originalSql);
+		buf.append("\";\n");
+
+		this.connection.dumpTestcaseQuery(buf.toString());
+	}
+
+	/**
+	 * @see java.sql.Statement#executeBatch()
+	 */
+	public synchronized int[] executeBatch() throws SQLException {
+		if (this.connection.isReadOnly()) {
+			throw SQLError.createSQLException(Messages
+					.getString("ServerPreparedStatement.2") //$NON-NLS-1$
+					+ Messages.getString("ServerPreparedStatement.3"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		checkClosed();
+
+		synchronized (this.connection.getMutex()) {
+			clearWarnings();
+
+			// Store this for later, we're going to 'swap' them out
+			// as we execute each batched statement...
+			BindValue[] oldBindValues = this.parameterBindings;
+
+			try {
+				int[] updateCounts = null;
+
+				if (this.batchedArgs != null) {
+					int nbrCommands = this.batchedArgs.size();
+					updateCounts = new int[nbrCommands];
+
+					if (this.retrieveGeneratedKeys) {
+						this.batchedGeneratedKeys = new ArrayList(nbrCommands);
+					}
+
+					for (int i = 0; i < nbrCommands; i++) {
+						updateCounts[i] = -3;
+					}
+
+					SQLException sqlEx = null;
+
+					int commandIndex = 0;
+
+					BindValue[] previousBindValuesForBatch = null;
+
+					for (commandIndex = 0; commandIndex < nbrCommands; commandIndex++) {
+						Object arg = this.batchedArgs.get(commandIndex);
+
+						if (arg instanceof String) {
+							updateCounts[commandIndex] = executeUpdate((String) arg);
+						} else {
+							this.parameterBindings = ((BatchedBindValues) arg).batchedParameterValues;
+
+							try {
+								// We need to check types each time, as
+								// the user might have bound different
+								// types in each addBatch()
+
+								if (previousBindValuesForBatch != null) {
+									for (int j = 0; j < this.parameterBindings.length; j++) {
+										if (this.parameterBindings[j].bufferType != previousBindValuesForBatch[j].bufferType) {
+											this.sendTypesToServer = true;
+
+											break;
+										}
+									}
+								}
+
+								try {
+									updateCounts[commandIndex] = executeUpdate(false, true);
+								} finally {
+									previousBindValuesForBatch = this.parameterBindings;
+								}
+
+								if (this.retrieveGeneratedKeys) {
+									java.sql.ResultSet rs = null;
+
+									try {
+										// we don't want to use our version,
+										// because we've altered the behavior of
+										// ours to support batch updates
+										// (catch-22)
+										// Ideally, what we need here is
+										// super.super.getGeneratedKeys()
+										// but that construct doesn't exist in
+										// Java, so that's why there's
+										// this kludge.
+										rs = getGeneratedKeysInternal();
+
+										while (rs.next()) {
+											this.batchedGeneratedKeys
+													.add(new byte[][] { rs
+															.getBytes(1) });
+										}
+									} finally {
+										if (rs != null) {
+											rs.close();
+										}
+									}
+								}
+							} catch (SQLException ex) {
+								updateCounts[commandIndex] = EXECUTE_FAILED;
+
+								if (this.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.parameterBindings = oldBindValues;
+				this.sendTypesToServer = true;
+
+				clearBatch();
+			}
+		}
+	}
+
+	/**
+	 * @see com.mysql.jdbc.PreparedStatement#executeInternal(int,
+	 *      com.mysql.jdbc.Buffer, boolean, boolean)
+	 */
+	protected com.mysql.jdbc.ResultSet executeInternal(int maxRowsToRetrieve,
+			Buffer sendPacket, boolean createStreamingResultSet,
+			boolean queryIsSelectOnly, boolean unpackFields, boolean isBatch)
+			throws SQLException {
+		this.numberOfExecutions++;
+
+		// We defer to server-side execution
+		try {
+			return serverExecute(maxRowsToRetrieve, createStreamingResultSet);
+		} catch (SQLException sqlEx) {
+			// don't wrap SQLExceptions
+			if (this.connection.getEnablePacketDebug()) {
+				this.connection.getIO().dumpPacketRingBuffer();
+			}
+
+			if (this.connection.getDumpQueriesOnException()) {
+				String extractedSql = toString();
+				StringBuffer messageBuf = new StringBuffer(extractedSql
+						.length() + 32);
+				messageBuf
+						.append("\n\nQuery being executed when exception was thrown:\n\n");
+				messageBuf.append(extractedSql);
+
+				sqlEx = Connection.appendMessageToException(sqlEx, messageBuf
+						.toString());
+			}
+
+			throw sqlEx;
+		} catch (Exception ex) {
+			if (this.connection.getEnablePacketDebug()) {
+				this.connection.getIO().dumpPacketRingBuffer();
+			}
+
+			SQLException sqlEx = SQLError.createSQLException(ex.toString(),
+					SQLError.SQL_STATE_GENERAL_ERROR);
+
+			if (this.connection.getDumpQueriesOnException()) {
+				String extractedSql = toString();
+				StringBuffer messageBuf = new StringBuffer(extractedSql
+						.length() + 32);
+				messageBuf
+						.append("\n\nQuery being executed when exception was thrown:\n\n");
+				messageBuf.append(extractedSql);
+
+				sqlEx = Connection.appendMessageToException(sqlEx, messageBuf
+						.toString());
+			}
+
+			throw sqlEx;
+		}
+	}
+
+	/**
+	 * @see com.mysql.jdbc.PreparedStatement#fillSendPacket()
+	 */
+	protected Buffer fillSendPacket() throws SQLException {
+		return null; // we don't use this type of packet
+	}
+
+	/**
+	 * @see com.mysql.jdbc.PreparedStatement#fillSendPacket(byte,
+	 *      java.io.InputStream, boolean, int)
+	 */
+	protected Buffer fillSendPacket(byte[][] batchedParameterStrings,
+			InputStream[] batchedParameterStreams, boolean[] batchedIsStream,
+			int[] batchedStreamLengths) throws SQLException {
+		return null; // we don't use this type of packet
+	}
+
+	private BindValue getBinding(int parameterIndex, boolean forLongData)
+			throws SQLException {
+		checkClosed();
+		
+		if (this.parameterBindings.length == 0) {
+			throw SQLError.createSQLException(Messages
+					.getString("ServerPreparedStatement.8"), //$NON-NLS-1$
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		parameterIndex--;
+
+		if ((parameterIndex < 0)
+				|| (parameterIndex >= this.parameterBindings.length)) {
+			throw SQLError.createSQLException(Messages
+					.getString("ServerPreparedStatement.9") //$NON-NLS-1$
+					+ (parameterIndex + 1)
+					+ Messages.getString("ServerPreparedStatement.10") //$NON-NLS-1$
+					+ this.parameterBindings.length,
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if (this.parameterBindings[parameterIndex] == null) {
+			this.parameterBindings[parameterIndex] = new BindValue();
+		} else {
+			if (this.parameterBindings[parameterIndex].isLongData
+					&& !forLongData) {
+				this.detectedLongParameterSwitch = true;
+			}
+		}
+
+		this.parameterBindings[parameterIndex].isSet = true;
+		this.parameterBindings[parameterIndex].boundBeforeExecutionNum = this.numberOfExecutions;
+
+		return this.parameterBindings[parameterIndex];
+	}
+
+	/**
+	 * @see com.mysql.jdbc.PreparedStatement#getBytes(int)
+	 */
+	byte[] getBytes(int parameterIndex) throws SQLException {
+		BindValue bindValue = getBinding(parameterIndex, false);
+
+		if (bindValue.isNull) {
+			return null;
+		} else if (bindValue.isLongData) {
+			throw new NotImplemented();
+		} else {
+			if (this.outByteBuffer == null) {
+				this.outByteBuffer = new Buffer(this.connection
+						.getNetBufferLength());
+			}
+
+			this.outByteBuffer.clear();
+
+			int originalPosition = this.outByteBuffer.getPosition();
+
+			storeBinding(this.outByteBuffer, bindValue, this.connection.getIO());
+
+			int newPosition = this.outByteBuffer.getPosition();
+
+			int length = newPosition - originalPosition;
+
+			byte[] valueAsBytes = new byte[length];
+
+			System.arraycopy(this.outByteBuffer.getByteBuffer(),
+					originalPosition, valueAsBytes, 0, length);
+
+			return valueAsBytes;
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#getMetaData()
+	 */
+	public java.sql.ResultSetMetaData getMetaData() throws SQLException {
+		checkClosed();
+
+		if (this.resultFields == null) {
+			return null;
+		}
+
+		return new ResultSetMetaData(this.resultFields, 
+				this.connection.getUseOldAliasMetadataBehavior());
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#getParameterMetaData()
+	 */
+	public ParameterMetaData getParameterMetaData() throws SQLException {
+		checkClosed();
+		
+		if (this.parameterMetaData == null) {
+			this.parameterMetaData = new MysqlParameterMetadata(
+					this.parameterFields, this.parameterCount);
+		}
+		
+		return this.parameterMetaData;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.PreparedStatement#isNull(int)
+	 */
+	boolean isNull(int paramIndex) {
+		throw new IllegalArgumentException(Messages
+				.getString("ServerPreparedStatement.7")); //$NON-NLS-1$
+	}
+
+	/**
+	 * Closes this connection and frees all 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.connection != null) {
+			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) {
+					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);
+
+				clearParametersInternal(false);
+				this.parameterBindings = null;
+				
+				this.parameterFields = null;
+				this.resultFields = null;
+				
+				if (exceptionDuringClose != null) {
+					throw exceptionDuringClose;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Used by Connection when auto-reconnecting to retrieve 'lost' prepared
+	 * statements.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	protected void rePrepare() throws SQLException {
+		this.invalidationException = null;
+
+		try {
+			serverPrepare(this.originalSql);
+		} catch (SQLException sqlEx) {
+			// don't wrap SQLExceptions
+			this.invalidationException = sqlEx;
+		} catch (Exception ex) {
+			this.invalidationException = SQLError.createSQLException(ex.toString(),
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+
+		if (this.invalidationException != null) {
+			this.invalid = true;
+
+			this.parameterBindings = null;
+
+			this.parameterFields = null;
+			this.resultFields = null;
+
+			if (this.results != null) {
+				try {
+					this.results.close();
+				} catch (Exception ex) {
+					;
+				}
+			}
+
+			if (this.connection != null) {
+				if (this.maxRowsChanged) {
+					this.connection.unsetMaxRows(this);
+				}
+
+				if (!this.connection.getDontTrackOpenResources()) {
+					this.connection.unregisterStatement(this);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tells the server to execute this prepared statement with the current
+	 * parameter bindings.
+	 * 
+	 * <pre>
+	 * 
+	 * 
+	 *    -   Server gets the command 'COM_EXECUTE' to execute the
+	 *        previously         prepared query. If there is any param markers;
+	 *  then client will send the data in the following format:
+	 * 
+	 *  [COM_EXECUTE:1]
+	 *  [STMT_ID:4]
+	 *  [NULL_BITS:(param_count+7)/8)]
+	 *  [TYPES_SUPPLIED_BY_CLIENT(0/1):1]
+	 *  [[length]data]
+	 *  [[length]data] .. [[length]data].
+	 * 
+	 *  (Note: Except for string/binary types; all other types will not be
+	 *  supplied with length field)
+	 * 
+	 *  
+	 * </pre>
+	 * 
+	 * @param maxRowsToRetrieve
+	 *            DOCUMENT ME!
+	 * @param createStreamingResultSet
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 */
+	private com.mysql.jdbc.ResultSet serverExecute(int maxRowsToRetrieve,
+			boolean createStreamingResultSet) throws SQLException {
+		synchronized (this.connection.getMutex()) {
+			if (this.detectedLongParameterSwitch) {
+				// Check when values were bound
+				boolean firstFound = false;
+				long boundTimeToCheck = 0;
+				
+				for (int i = 0; i < this.parameterCount - 1; i++) {
+					if (this.parameterBindings[i].isLongData) {
+						if (firstFound && boundTimeToCheck != 
+							this.parameterBindings[i].boundBeforeExecutionNum) { 					
+							throw SQLError.createSQLException(Messages
+									.getString("ServerPreparedStatement.11") //$NON-NLS-1$
+									+ Messages.getString("ServerPreparedStatement.12"), //$NON-NLS-1$
+									SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+						} else {
+							firstFound = true;
+							boundTimeToCheck = this.parameterBindings[i].boundBeforeExecutionNum;
+						}
+					}
+				}
+				
+				// Okay, we've got all "newly"-bound streams, so reset 
+				// server-side state to clear out previous bindings
+				
+				serverResetStatement();
+			}
+
+
+			// Check bindings
+			for (int i = 0; i < this.parameterCount; i++) {
+				if (!this.parameterBindings[i].isSet) {
+					throw SQLError.createSQLException(Messages
+							.getString("ServerPreparedStatement.13") + (i + 1) //$NON-NLS-1$
+							+ Messages.getString("ServerPreparedStatement.14"),
+							SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+				}
+			}
+
+			//
+			// Send all long data
+			//
+			for (int i = 0; i < this.parameterCount; i++) {
+				if (this.parameterBindings[i].isLongData) {
+					serverLongData(i, this.parameterBindings[i]);
+				}
+			}
+
+			if (this.connection.getAutoGenerateTestcaseScript()) {
+				dumpExecuteForTestcase();
+			}
+
+			//
+			// store the parameter values
+			//
+			MysqlIO mysql = this.connection.getIO();
+
+			Buffer packet = mysql.getSharedSendPacket();
+
+			packet.clear();
+			packet.writeByte((byte) MysqlDefs.COM_EXECUTE);
+			packet.writeLong(this.serverStatementId);
+
+			boolean usingCursor = false;
+
+			if (this.connection.versionMeetsMinimum(4, 1, 2)) {
+				// we only create cursor-backed result sets if
+				// a) The query is a SELECT
+				// b) The server supports it
+				// c) We know it is forward-only (note this doesn't
+				// preclude updatable result sets)
+				// d) The user has set a fetch size
+				if (this.resultFields != null &&
+						this.connection.isCursorFetchEnabled()
+						&& getResultSetType() == ResultSet.TYPE_FORWARD_ONLY
+						&& getResultSetConcurrency() == ResultSet.CONCUR_READ_ONLY
+						&& getFetchSize() > 0) {
+					packet.writeByte(MysqlDefs.OPEN_CURSOR_FLAG);
+					usingCursor = true;
+				} else {
+					packet.writeByte((byte) 0); // placeholder for flags
+				}
+
+				packet.writeLong(1); // placeholder for parameter
+				                     // iterations
+			}
+
+			/* Reserve place for null-marker bytes */
+			int nullCount = (this.parameterCount + 7) / 8;
+
+			// if (mysql.versionMeetsMinimum(4, 1, 2)) {
+			// nullCount = (this.parameterCount + 9) / 8;
+			// }
+			int nullBitsPosition = packet.getPosition();
+
+			for (int i = 0; i < nullCount; i++) {
+				packet.writeByte((byte) 0);
+			}
+
+			byte[] nullBitsBuffer = new byte[nullCount];
+
+			/* In case if buffers (type) altered, indicate to server */
+			packet.writeByte(this.sendTypesToServer ? (byte) 1 : (byte) 0);
+
+			if (this.sendTypesToServer) {
+				/*
+				 * Store types of parameters in first in first package that is
+				 * sent to the server.
+				 */
+				for (int i = 0; i < this.parameterCount; i++) {
+					packet.writeInt(this.parameterBindings[i].bufferType);
+				}
+			}
+
+			//
+			// store the parameter values
+			//
+			for (int i = 0; i < this.parameterCount; i++) {
+				if (!this.parameterBindings[i].isLongData) {
+					if (!this.parameterBindings[i].isNull) {
+						storeBinding(packet, this.parameterBindings[i], mysql);
+					} else {
+						nullBitsBuffer[i / 8] |= (1 << (i & 7));
+					}
+				}
+			}
+
+			//
+			// Go back and write the NULL flags
+			// to the beginning of the packet
+			//
+			int endPosition = packet.getPosition();
+			packet.setPosition(nullBitsPosition);
+			packet.writeBytesNoNull(nullBitsBuffer);
+			packet.setPosition(endPosition);
+
+			long begin = 0;
+
+			if (this.connection.getProfileSql()
+					|| this.connection.getLogSlowQueries()
+					|| this.connection.getGatherPerformanceMetrics()) {
+				begin = System.currentTimeMillis();
+			}
+
+			this.wasCancelled = false;
+			
+			CancelTask timeoutTask = null;
+
+			try {
+				if (this.timeoutInMillis != 0
+						&& this.connection.versionMeetsMinimum(5, 0, 0)) {
+					timeoutTask = new CancelTask();
+					this.connection.getCancelTimer().schedule(timeoutTask, 
+							this.timeoutInMillis);
+				}
+				
+				Buffer resultPacket = mysql.sendCommand(MysqlDefs.COM_EXECUTE,
+					null, packet, false, null);
+				
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+					timeoutTask = null;
+				}
+				
+				if (this.wasCancelled) {
+					this.wasCancelled = false;
+					throw new MySQLTimeoutException();
+				}
+			
+				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);
+	
+				
+				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())) {
+						StringBuffer mesgBuf = new StringBuffer(
+								48 + this.originalSql.length());
+						mesgBuf.append(Messages
+								.getString("ServerPreparedStatement.15")); //$NON-NLS-1$
+						mesgBuf.append(this.connection
+								.getSlowQueryThresholdMillis());
+						mesgBuf.append(Messages
+								.getString("ServerPreparedStatement.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);
+						}
+					}
+	
+					if (this.connection.getGatherPerformanceMetrics()) {
+						this.connection.registerQueryExecutionTime(elapsedTime);
+					}
+				}
+				
+				if (mysql.hadWarnings()) {
+		            mysql.scanForAndThrowDataTruncation();
+		        }
+				
+				return rs;
+			} finally {
+				if (timeoutTask != null) {
+					timeoutTask.cancel();
+				}
+			}
+		}
+	}
+
+
+	/**
+	 * Sends stream-type data parameters to the server.
+	 * 
+	 * <pre>
+	 * 
+	 *  Long data handling:
+	 * 
+	 *  - Server gets the long data in pieces with command type 'COM_LONG_DATA'.
+	 *  - The packet recieved will have the format as:
+	 *    [COM_LONG_DATA:     1][STMT_ID:4][parameter_number:2][type:2][data]
+	 *  - Checks if the type is specified by client, and if yes reads the type,
+	 *    and  stores the data in that format.
+	 *  - It's up to the client to check for read data ended. The server doesn't
+	 *    care;  and also server doesn't notify to the client that it got the
+	 *    data  or not; if there is any error; then during execute; the error
+	 *    will  be returned
+	 *  
+	 * </pre>
+	 * 
+	 * @param parameterIndex
+	 *            DOCUMENT ME!
+	 * @param longData
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	private void serverLongData(int parameterIndex, BindValue longData)
+			throws SQLException {
+		synchronized (this.connection.getMutex()) {
+			MysqlIO mysql = this.connection.getIO();
+
+			Buffer packet = mysql.getSharedSendPacket();
+
+			Object value = longData.value;
+
+			if (value instanceof byte[]) {
+				packet.clear();
+				packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
+				packet.writeLong(this.serverStatementId);
+				packet.writeInt((parameterIndex));
+
+				packet.writeBytesNoNull((byte[]) longData.value);
+
+				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
+						null);
+			} else if (value instanceof InputStream) {
+				storeStream(mysql, parameterIndex, packet, (InputStream) value);
+			} else if (value instanceof java.sql.Blob) {
+				storeStream(mysql, parameterIndex, packet,
+						((java.sql.Blob) value).getBinaryStream());
+			} else if (value instanceof Reader) {
+				storeReader(mysql, parameterIndex, packet, (Reader) value);
+			} else {
+				throw SQLError.createSQLException(Messages
+						.getString("ServerPreparedStatement.18") //$NON-NLS-1$
+						+ value.getClass().getName() + "'", //$NON-NLS-1$
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		}
+	}
+
+	private void serverPrepare(String sql) throws SQLException {
+		synchronized (this.connection.getMutex()) {
+			MysqlIO mysql = this.connection.getIO();
+
+			if (this.connection.getAutoGenerateTestcaseScript()) {
+				dumpPrepareForTestcase();
+			}
+
+			try {
+				long begin = 0;
+
+				if (StringUtils.startsWithIgnoreCaseAndWs(sql, "LOAD DATA")) { //$NON-NLS-1$
+					this.isLoadDataQuery = true;
+				} else {
+					this.isLoadDataQuery = false;
+				}
+
+				if (this.connection.getProfileSql()) {
+					begin = System.currentTimeMillis();
+				}
+
+				String characterEncoding = null;
+				String connectionEncoding = this.connection.getEncoding();
+
+				if (!this.isLoadDataQuery && this.connection.getUseUnicode()
+						&& (connectionEncoding != null)) {
+					characterEncoding = connectionEncoding;
+				}
+
+				Buffer prepareResultPacket = mysql.sendCommand(
+						MysqlDefs.COM_PREPARE, sql, null, false,
+						characterEncoding);
+
+				if (this.connection.versionMeetsMinimum(4, 1, 1)) {
+					// 4.1.1 and newer use the first byte
+					// as an 'ok' or 'error' flag, so move
+					// the buffer pointer past it to
+					// start reading the statement id.
+					prepareResultPacket.setPosition(1);
+				} else {
+					// 4.1.0 doesn't use the first byte as an
+					// 'ok' or 'error' flag
+					prepareResultPacket.setPosition(0);
+				}
+
+				this.serverStatementId = prepareResultPacket.readLong();
+				this.fieldCount = prepareResultPacket.readInt();
+				this.parameterCount = prepareResultPacket.readInt();
+				this.parameterBindings = new BindValue[this.parameterCount];
+
+				for (int i = 0; i < this.parameterCount; i++) {
+					this.parameterBindings[i] = new BindValue();
+				}
+
+				this.connection.incrementNumberOfPrepares();
+
+				if (this.connection.getProfileSql()) {
+					this.eventSink = ProfileEventSink
+							.getInstance(this.connection);
+
+					this.eventSink.consumeEvent(new ProfilerEvent(
+							ProfilerEvent.TYPE_PREPARE,
+							"", this.currentCatalog, //$NON-NLS-1$
+							this.connectionId, this.statementId, -1,
+							System.currentTimeMillis(), (int) (System
+									.currentTimeMillis() - begin), null,
+							new Throwable(), truncateQueryToLog(sql)));
+				}
+
+				if (this.parameterCount > 0) {
+					if (this.connection.versionMeetsMinimum(4, 1, 2)
+							&& !mysql.isVersion(5, 0, 0)) {
+						this.parameterFields = new Field[this.parameterCount];
+
+						Buffer metaDataPacket = mysql.readPacket();
+
+						int i = 0;
+
+						while (!metaDataPacket.isLastDataPacket()
+								&& (i < this.parameterCount)) {
+							this.parameterFields[i++] = mysql.unpackField(
+									metaDataPacket, false);
+							metaDataPacket = mysql.readPacket();
+						}
+					}
+				}
+
+				if (this.fieldCount > 0) {
+					this.resultFields = new Field[this.fieldCount];
+
+					Buffer fieldPacket = mysql.readPacket();
+
+					int i = 0;
+
+					// Read in the result set column information
+					while (!fieldPacket.isLastDataPacket()
+							&& (i < this.fieldCount)) {
+						this.resultFields[i++] = mysql.unpackField(fieldPacket,
+								false);
+						fieldPacket = mysql.readPacket();
+					}
+				}
+			} catch (SQLException sqlEx) {
+				if (this.connection.getDumpQueriesOnException()) {
+					StringBuffer messageBuf = new StringBuffer(this.originalSql
+							.length() + 32);
+					messageBuf
+							.append("\n\nQuery being prepared when exception was thrown:\n\n");
+					messageBuf.append(this.originalSql);
+
+					sqlEx = Connection.appendMessageToException(sqlEx,
+							messageBuf.toString());
+				}
+
+				throw sqlEx;
+			} finally {
+				// Leave the I/O channel in a known state...there might be
+				// packets out there
+				// that we're not interested in
+				this.connection.getIO().clearInputStream();
+			}
+		}
+	}
+
+	private String truncateQueryToLog(String sql) {
+		String query = null;
+		
+		if (sql.length() > this.connection.getMaxQuerySizeToLog()) {
+			StringBuffer queryBuf = new StringBuffer(
+					this.connection.getMaxQuerySizeToLog() + 12);
+			queryBuf.append(sql.substring(0, this.connection.getMaxQuerySizeToLog()));
+			queryBuf.append(Messages.getString("MysqlIO.25"));
+			
+			query = queryBuf.toString();
+		} else {
+			query = sql;
+		}
+		
+		return query;
+	}
+
+	private void serverResetStatement() throws SQLException {
+		synchronized (this.connection.getMutex()) {
+
+			MysqlIO mysql = this.connection.getIO();
+
+			Buffer packet = mysql.getSharedSendPacket();
+
+			packet.clear();
+			packet.writeByte((byte) MysqlDefs.COM_RESET_STMT);
+			packet.writeLong(this.serverStatementId);
+
+			try {
+				mysql.sendCommand(MysqlDefs.COM_RESET_STMT, null, packet,
+						!this.connection.versionMeetsMinimum(4, 1, 2), null);
+			} catch (SQLException sqlEx) {
+				throw sqlEx;
+			} catch (Exception ex) {
+				throw SQLError.createSQLException(ex.toString(),
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			} finally {
+				mysql.clearInputStream();
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setArray(int, java.sql.Array)
+	 */
+	public void setArray(int i, Array x) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream,
+	 *      int)
+	 */
+	public void setAsciiStream(int parameterIndex, InputStream x, int length)
+			throws SQLException {
+		checkClosed();
+
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			BindValue binding = getBinding(parameterIndex, true);
+			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
+
+			binding.value = x;
+			binding.isNull = false;
+			binding.isLongData = true;
+
+			if (this.connection.getUseStreamLengthsInPrepStmts()) {
+				binding.bindLength = length;
+			} else {
+				binding.bindLength = -1;
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal)
+	 */
+	public void setBigDecimal(int parameterIndex, BigDecimal x)
+			throws SQLException {
+		checkClosed();
+
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.DECIMAL);
+		} else {
+		
+			BindValue binding = getBinding(parameterIndex, false);
+
+			if (this.connection.versionMeetsMinimum(5, 0, 3)) {
+				setType(binding, MysqlDefs.FIELD_TYPE_NEW_DECIMAL);
+			} else {
+				setType(binding, this.stringTypeCode);
+			}
+
+			binding.value = StringUtils.fixDecimalExponent(x.toString());
+			binding.isNull = false;
+			binding.isLongData = false;
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream,
+	 *      int)
+	 */
+	public void setBinaryStream(int parameterIndex, InputStream x, int length)
+			throws SQLException {
+		checkClosed();
+
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			BindValue binding = getBinding(parameterIndex, true);
+			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
+
+			binding.value = x;
+			binding.isNull = false;
+			binding.isLongData = true;
+
+			if (this.connection.getUseStreamLengthsInPrepStmts()) {
+				binding.bindLength = length;
+			} else {
+				binding.bindLength = -1;
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob)
+	 */
+	public void setBlob(int parameterIndex, Blob x) throws SQLException {
+		checkClosed();
+
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			BindValue binding = getBinding(parameterIndex, true);
+			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
+
+			binding.value = x;
+			binding.isNull = false;
+			binding.isLongData = true;
+
+			if (this.connection.getUseStreamLengthsInPrepStmts()) {
+				binding.bindLength = x.length();
+			} else {
+				binding.bindLength = -1;
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setBoolean(int, boolean)
+	 */
+	public void setBoolean(int parameterIndex, boolean x) throws SQLException {
+		setByte(parameterIndex, (x ? (byte) 1 : (byte) 0));
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setByte(int, byte)
+	 */
+	public void setByte(int parameterIndex, byte x) throws SQLException {
+		checkClosed();
+
+		BindValue binding = getBinding(parameterIndex, false);
+		setType(binding, MysqlDefs.FIELD_TYPE_TINY);
+
+		binding.value = null;
+		binding.byteBinding = x;
+		binding.isNull = false;
+		binding.isLongData = false;
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setBytes(int, byte)
+	 */
+	public void setBytes(int parameterIndex, byte[] x) throws SQLException {
+		checkClosed();
+
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			BindValue binding = getBinding(parameterIndex, false);
+			setType(binding, MysqlDefs.FIELD_TYPE_VAR_STRING);
+
+			binding.value = x;
+			binding.isNull = false;
+			binding.isLongData = false;
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader,
+	 *      int)
+	 */
+	public void setCharacterStream(int parameterIndex, Reader reader, int length)
+			throws SQLException {
+		checkClosed();
+
+		if (reader == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			BindValue binding = getBinding(parameterIndex, true);
+			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
+
+			binding.value = reader;
+			binding.isNull = false;
+			binding.isLongData = true;
+
+			if (this.connection.getUseStreamLengthsInPrepStmts()) {
+				binding.bindLength = length;
+			} else {
+				binding.bindLength = -1;
+			}
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob)
+	 */
+	public void setClob(int parameterIndex, Clob x) throws SQLException {
+		checkClosed();
+
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.BINARY);
+		} else {
+			BindValue binding = getBinding(parameterIndex, true);
+			setType(binding, MysqlDefs.FIELD_TYPE_BLOB);
+
+			binding.value = x.getCharacterStream();
+			binding.isNull = false;
+			binding.isLongData = true;
+
+			if (this.connection.getUseStreamLengthsInPrepStmts()) {
+				binding.bindLength = x.length();
+			} else {
+				binding.bindLength = -1;
+			}
+		}
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Date value. The driver converts this to a
+	 * SQL DATE value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public void setDate(int parameterIndex, Date x) throws SQLException {
+		setDate(parameterIndex, x, null);
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Date value. The driver converts this to a
+	 * SQL DATE value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            the parameter value
+	 * @param cal
+	 *            the calendar to interpret the date with
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs.
+	 */
+	public void setDate(int parameterIndex, Date x, Calendar cal)
+			throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.DATE);
+		} else {
+			BindValue binding = getBinding(parameterIndex, false);
+			setType(binding, MysqlDefs.FIELD_TYPE_DATE);
+
+			binding.value = x;
+			binding.isNull = false;
+			binding.isLongData = false;
+		}
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setDouble(int, double)
+	 */
+	public void setDouble(int parameterIndex, double x) throws SQLException {
+		checkClosed();
+
+		if (!this.connection.getAllowNanAndInf()
+				&& (x == Double.POSITIVE_INFINITY
+						|| x == Double.NEGATIVE_INFINITY || Double.isNaN(x))) {
+			throw SQLError.createSQLException("'" + x
+					+ "' is not a valid numeric or approximate numeric value",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+
+		}
+
+		BindValue binding = getBinding(parameterIndex, false);
+		setType(binding, MysqlDefs.FIELD_TYPE_DOUBLE);
+
+		binding.value = null;
+		binding.doubleBinding = x;
+		binding.isNull = false;
+		binding.isLongData = false;
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setFloat(int, float)
+	 */
+	public void setFloat(int parameterIndex, float x) throws SQLException {
+		checkClosed();
+
+		BindValue binding = getBinding(parameterIndex, false);
+		setType(binding, MysqlDefs.FIELD_TYPE_FLOAT);
+
+		binding.value = null;
+		binding.floatBinding = x;
+		binding.isNull = false;
+		binding.isLongData = false;
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setInt(int, int)
+	 */
+	public void setInt(int parameterIndex, int x) throws SQLException {
+		checkClosed();
+
+		BindValue binding = getBinding(parameterIndex, false);
+		setType(binding, MysqlDefs.FIELD_TYPE_LONG);
+
+		binding.value = null;
+		binding.intBinding = x;
+		binding.isNull = false;
+		binding.isLongData = false;
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setLong(int, long)
+	 */
+	public void setLong(int parameterIndex, long x) throws SQLException {
+		checkClosed();
+
+		BindValue binding = getBinding(parameterIndex, false);
+		setType(binding, MysqlDefs.FIELD_TYPE_LONGLONG);
+
+		binding.value = null;
+		binding.longBinding = x;
+		binding.isNull = false;
+		binding.isLongData = false;
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setNull(int, int)
+	 */
+	public void setNull(int parameterIndex, int sqlType) throws SQLException {
+		checkClosed();
+
+		BindValue binding = getBinding(parameterIndex, false);
+
+		//
+		// Don't re-set types, but use something if this
+		// parameter was never specified
+		//
+		if (binding.bufferType == 0) {
+			setType(binding, MysqlDefs.FIELD_TYPE_NULL);
+		}
+
+		binding.value = null;
+		binding.isNull = true;
+		binding.isLongData = false;
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String)
+	 */
+	public void setNull(int parameterIndex, int sqlType, String typeName)
+			throws SQLException {
+		checkClosed();
+
+		BindValue binding = getBinding(parameterIndex, false);
+
+		//
+		// Don't re-set types, but use something if this
+		// parameter was never specified
+		//
+		if (binding.bufferType == 0) {
+			setType(binding, MysqlDefs.FIELD_TYPE_NULL);
+		}
+
+		binding.value = null;
+		binding.isNull = true;
+		binding.isLongData = false;
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref)
+	 */
+	public void setRef(int i, Ref x) throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setShort(int, short)
+	 */
+	public void setShort(int parameterIndex, short x) throws SQLException {
+		checkClosed();
+
+		BindValue binding = getBinding(parameterIndex, false);
+		setType(binding, MysqlDefs.FIELD_TYPE_SHORT);
+
+		binding.value = null;
+		binding.shortBinding = x;
+		binding.isNull = false;
+		binding.isLongData = false;
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setString(int, java.lang.String)
+	 */
+	public void setString(int parameterIndex, String x) throws SQLException {
+		checkClosed();
+
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.CHAR);
+		} else {
+			BindValue binding = getBinding(parameterIndex, false);
+
+			setType(binding, this.stringTypeCode);
+
+			binding.value = x;
+			binding.isNull = false;
+			binding.isLongData = false;
+		}
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Time value.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...));
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public void setTime(int parameterIndex, java.sql.Time x)
+			throws SQLException {
+		setTimeInternal(parameterIndex, x, null, TimeZone.getDefault(), false);
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Time value. The driver converts this to a
+	 * SQL TIME value when it sends it to the database, using the given
+	 * timezone.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...));
+	 * @param x
+	 *            the parameter value
+	 * @param cal
+	 *            the timezone to use
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public void setTime(int parameterIndex, java.sql.Time x, Calendar cal)
+			throws SQLException {
+		setTimeInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Time value. The driver converts this to a
+	 * SQL TIME value when it sends it to the database, using the given
+	 * timezone.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1...));
+	 * @param x
+	 *            the parameter value
+	 * @param tz
+	 *            the timezone to use
+	 * 
+	 * @throws SQLException
+	 *             if a database access error occurs
+	 */
+	public void setTimeInternal(int parameterIndex, java.sql.Time x,
+			Calendar targetCalendar,
+			TimeZone tz, boolean rollForward) throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.TIME);
+		} else {
+			BindValue binding = getBinding(parameterIndex, false);
+			setType(binding, MysqlDefs.FIELD_TYPE_TIME);
+
+			Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
+			
+			synchronized (sessionCalendar) {
+				binding.value = TimeUtil.changeTimezone(this.connection, 
+						sessionCalendar,
+						targetCalendar,
+						x, tz,
+						this.connection.getServerTimezoneTZ(), 
+						rollForward);
+			}
+			
+			binding.isNull = false;
+			binding.isLongData = false;
+		}
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
+	 * to a SQL TIMESTAMP value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            the parameter value
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs.
+	 */
+	public void setTimestamp(int parameterIndex, java.sql.Timestamp x)
+			throws SQLException {
+		setTimestampInternal(parameterIndex, x, null, TimeZone.getDefault(), false);
+	}
+
+	/**
+	 * Set a parameter to a java.sql.Timestamp value. The driver converts this
+	 * to a SQL TIMESTAMP value when it sends it to the database.
+	 * 
+	 * @param parameterIndex
+	 *            the first parameter is 1, the second is 2, ...
+	 * @param x
+	 *            the parameter value
+	 * @param cal
+	 *            the timezone to use
+	 * 
+	 * @throws SQLException
+	 *             if a database-access error occurs.
+	 */
+	public void setTimestamp(int parameterIndex, java.sql.Timestamp x,
+			Calendar cal) throws SQLException {
+		setTimestampInternal(parameterIndex, x, cal, cal.getTimeZone(), true);
+	}
+
+	protected void setTimestampInternal(int parameterIndex,
+			java.sql.Timestamp x, Calendar targetCalendar,
+			TimeZone tz, boolean rollForward)
+			throws SQLException {
+		if (x == null) {
+			setNull(parameterIndex, java.sql.Types.TIMESTAMP);
+		} else {
+			BindValue binding = getBinding(parameterIndex, false);
+			setType(binding, MysqlDefs.FIELD_TYPE_DATETIME);
+
+			Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ?
+					this.connection.getUtcCalendar() : 
+						getCalendarInstanceForSessionOrNew();
+			
+			synchronized (sessionCalendar) {
+				binding.value = TimeUtil.changeTimezone(this.connection, 
+						sessionCalendar,
+						targetCalendar,
+						x, tz,
+						this.connection.getServerTimezoneTZ(), 
+						rollForward);
+			}
+			
+			binding.isNull = false;
+			binding.isLongData = false;
+		}
+	}
+
+	private void setType(BindValue oldValue, int bufferType) {
+		if (oldValue.bufferType != bufferType) {
+			this.sendTypesToServer = true;
+		}
+
+		oldValue.bufferType = bufferType;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param parameterIndex
+	 *            DOCUMENT ME!
+	 * @param x
+	 *            DOCUMENT ME!
+	 * @param length
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 * @throws NotImplemented
+	 *             DOCUMENT ME!
+	 * 
+	 * @see java.sql.PreparedStatement#setUnicodeStream(int,
+	 *      java.io.InputStream, int)
+	 * @deprecated
+	 */
+	public void setUnicodeStream(int parameterIndex, InputStream x, int length)
+			throws SQLException {
+		checkClosed();
+
+		throw new NotImplemented();
+	}
+
+	/**
+	 * @see java.sql.PreparedStatement#setURL(int, java.net.URL)
+	 */
+	public void setURL(int parameterIndex, URL x) throws SQLException {
+		checkClosed();
+
+		setString(parameterIndex, x.toString());
+	}
+
+	/**
+	 * Method storeBinding.
+	 * 
+	 * @param packet
+	 * @param bindValue
+	 * @param mysql
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void storeBinding(Buffer packet, BindValue bindValue, MysqlIO mysql)
+			throws SQLException {
+		try {
+			Object value = bindValue.value;
+
+			//
+			// Handle primitives first
+			//
+			switch (bindValue.bufferType) {
+
+			case MysqlDefs.FIELD_TYPE_TINY:
+				packet.writeByte(bindValue.byteBinding);
+				return;
+			case MysqlDefs.FIELD_TYPE_SHORT:
+				packet.ensureCapacity(2);
+				packet.writeInt(bindValue.shortBinding);
+				return;
+			case MysqlDefs.FIELD_TYPE_LONG:
+				packet.ensureCapacity(4);
+				packet.writeLong(bindValue.intBinding);
+				return;
+			case MysqlDefs.FIELD_TYPE_LONGLONG:
+				packet.ensureCapacity(8);
+				packet.writeLongLong(bindValue.longBinding);
+				return;
+			case MysqlDefs.FIELD_TYPE_FLOAT:
+				packet.ensureCapacity(4);
+				packet.writeFloat(bindValue.floatBinding);
+				return;
+			case MysqlDefs.FIELD_TYPE_DOUBLE:
+				packet.ensureCapacity(8);
+				packet.writeDouble(bindValue.doubleBinding);
+				return;
+			case MysqlDefs.FIELD_TYPE_TIME:
+				storeTime(packet, (Time) value);
+				return;
+			case MysqlDefs.FIELD_TYPE_DATE:
+			case MysqlDefs.FIELD_TYPE_DATETIME:
+			case MysqlDefs.FIELD_TYPE_TIMESTAMP:
+				storeDateTime(packet, (java.util.Date) value, mysql);
+				return;
+			case MysqlDefs.FIELD_TYPE_VAR_STRING:
+			case MysqlDefs.FIELD_TYPE_STRING:
+			case MysqlDefs.FIELD_TYPE_VARCHAR:
+			case MysqlDefs.FIELD_TYPE_DECIMAL:
+			case MysqlDefs.FIELD_TYPE_NEW_DECIMAL:
+				if (value instanceof byte[]) {
+					packet.writeLenBytes((byte[]) value);
+				} else if (!this.isLoadDataQuery) {
+					packet.writeLenString((String) value, this.charEncoding,
+							this.connection.getServerCharacterEncoding(),
+							this.charConverter, this.connection
+									.parserKnowsUnicode(),
+									this.connection);
+				} else {
+					packet.writeLenBytes(((String) value).getBytes());
+				}
+
+				return;
+			}
+
+			
+		} catch (UnsupportedEncodingException uEE) {
+			throw SQLError.createSQLException(Messages
+					.getString("ServerPreparedStatement.22") //$NON-NLS-1$
+					+ this.connection.getEncoding() + "'", //$NON-NLS-1$
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		}
+	}
+
+	private void storeDataTime412AndOlder(Buffer intoBuf, java.util.Date dt)
+			throws SQLException {
+		
+		Calendar sessionCalendar = getCalendarInstanceForSessionOrNew();
+		
+		synchronized (sessionCalendar) {
+			java.util.Date oldTime = sessionCalendar.getTime();
+			
+			try {
+				intoBuf.ensureCapacity(8);
+				intoBuf.writeByte((byte) 7); // length
+	
+				sessionCalendar.setTime(dt);
+				
+				int year = sessionCalendar.get(Calendar.YEAR);
+				int month = sessionCalendar.get(Calendar.MONTH) + 1;
+				int date = sessionCalendar.get(Calendar.DATE);
+
+				intoBuf.writeInt(year);
+				intoBuf.writeByte((byte) month);
+				intoBuf.writeByte((byte) date);
+		
+				if (dt instanceof java.sql.Date) {
+					intoBuf.writeByte((byte) 0);
+					intoBuf.writeByte((byte) 0);
+					intoBuf.writeByte((byte) 0);
+				} else {
+					intoBuf.writeByte((byte) sessionCalendar
+							.get(Calendar.HOUR_OF_DAY));
+					intoBuf.writeByte((byte) sessionCalendar
+							.get(Calendar.MINUTE));
+					intoBuf.writeByte((byte) sessionCalendar
+							.get(Calendar.SECOND));
+				}
+			} finally {
+				sessionCalendar.setTime(oldTime);
+			}
+		}
+	}
+
+	private void storeDateTime(Buffer intoBuf, java.util.Date dt, MysqlIO mysql)
+			throws SQLException {
+		if (this.connection.versionMeetsMinimum(4, 1, 3)) {
+			storeDateTime413AndNewer(intoBuf, dt);
+		} else {
+			storeDataTime412AndOlder(intoBuf, dt);
+		}
+	}
+
+	private void storeDateTime413AndNewer(Buffer intoBuf, java.util.Date dt)
+			throws SQLException {
+		Calendar sessionCalendar = (dt instanceof Timestamp && 
+				this.connection.getUseJDBCCompliantTimezoneShift()) ? 
+				this.connection.getUtcCalendar() : getCalendarInstanceForSessionOrNew();
+		
+		synchronized (sessionCalendar) {
+			java.util.Date oldTime = sessionCalendar.getTime();
+		
+		
+			try {
+				sessionCalendar.setTime(dt);
+				
+				if (dt instanceof java.sql.Date) {
+					sessionCalendar.set(Calendar.HOUR_OF_DAY, 0);
+					sessionCalendar.set(Calendar.MINUTE, 0);
+					sessionCalendar.set(Calendar.SECOND, 0);
+				}
+
+				byte length = (byte) 7;
+
+				intoBuf.ensureCapacity(length);
+
+				if (dt instanceof java.sql.Timestamp) {
+					length = (byte) 11;
+				}
+
+				intoBuf.writeByte(length); // length
+
+				int year = sessionCalendar.get(Calendar.YEAR);
+				int month = sessionCalendar.get(Calendar.MONTH) + 1;
+				int date = sessionCalendar.get(Calendar.DAY_OF_MONTH);
+				
+				intoBuf.writeInt(year);
+				intoBuf.writeByte((byte) month);
+				intoBuf.writeByte((byte) date);
+
+				if (dt instanceof java.sql.Date) {
+					intoBuf.writeByte((byte) 0);
+					intoBuf.writeByte((byte) 0);
+					intoBuf.writeByte((byte) 0);
+				} else {
+					intoBuf.writeByte((byte) sessionCalendar
+							.get(Calendar.HOUR_OF_DAY));
+					intoBuf.writeByte((byte) sessionCalendar
+							.get(Calendar.MINUTE));
+					intoBuf.writeByte((byte) sessionCalendar
+							.get(Calendar.SECOND));
+				}
+
+				if (length == 11) {
+					intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos());
+				}
+			
+			} finally {
+				sessionCalendar.setTime(oldTime);
+			}
+		}
+	}
+
+	//
+	// TO DO: Investigate using NIO to do this faster
+	//
+	private void storeReader(MysqlIO mysql, int parameterIndex, Buffer packet,
+			Reader inStream) throws SQLException {
+		String forcedEncoding = this.connection.getClobCharacterEncoding();
+		
+		String clobEncoding = 
+			(forcedEncoding == null ? this.connection.getEncoding() : forcedEncoding);
+		
+		int maxBytesChar = 2;
+			
+		if (clobEncoding != null) {
+			if (!clobEncoding.equals("UTF-16")) {
+				maxBytesChar = this.connection.getMaxBytesPerChar(clobEncoding);
+				
+				if (maxBytesChar == 1) {
+					maxBytesChar = 2; // for safety
+				}
+			} else {
+				maxBytesChar = 4;
+			}
+		}
+			
+		char[] buf = new char[BLOB_STREAM_READ_BUF_SIZE / maxBytesChar];
+		
+		int numRead = 0;
+
+		int bytesInPacket = 0;
+		int totalBytesRead = 0;
+		int bytesReadAtLastSend = 0;
+		int packetIsFullAt = this.connection.getBlobSendChunkSize();
+		
+		
+		
+		try {
+			packet.clear();
+			packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
+			packet.writeLong(this.serverStatementId);
+			packet.writeInt((parameterIndex));
+
+			boolean readAny = false;
+			
+			while ((numRead = inStream.read(buf)) != -1) {
+				readAny = true;
+			
+				byte[] valueAsBytes = StringUtils.getBytes(buf, null,
+						clobEncoding, this.connection
+								.getServerCharacterEncoding(), 0, numRead,
+						this.connection.parserKnowsUnicode());
+
+				packet.writeBytesNoNull(valueAsBytes, 0, valueAsBytes.length);
+
+				bytesInPacket += valueAsBytes.length;
+				totalBytesRead += valueAsBytes.length;
+
+				if (bytesInPacket >= packetIsFullAt) {
+					bytesReadAtLastSend = totalBytesRead;
+
+					mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet,
+							true, null);
+
+					bytesInPacket = 0;
+					packet.clear();
+					packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
+					packet.writeLong(this.serverStatementId);
+					packet.writeInt((parameterIndex));
+				}
+			}
+
+			if (totalBytesRead != bytesReadAtLastSend) {
+				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
+						null);
+			}
+			
+			if (!readAny) {
+				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
+						null);
+			}
+		} catch (IOException ioEx) {
+			throw SQLError.createSQLException(Messages
+					.getString("ServerPreparedStatement.24") //$NON-NLS-1$
+					+ ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
+		} finally {
+			if (this.connection.getAutoClosePStmtStreams()) {
+				if (inStream != null) {
+					try {
+						inStream.close();
+					} catch (IOException ioEx) {
+						; // ignore
+					}
+				}
+			}
+		}
+	}
+
+	private void storeStream(MysqlIO mysql, int parameterIndex, Buffer packet,
+			InputStream inStream) throws SQLException {
+		byte[] buf = new byte[BLOB_STREAM_READ_BUF_SIZE];
+
+		int numRead = 0;
+		
+		try {
+			int bytesInPacket = 0;
+			int totalBytesRead = 0;
+			int bytesReadAtLastSend = 0;
+			int packetIsFullAt = this.connection.getBlobSendChunkSize();
+
+			packet.clear();
+			packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
+			packet.writeLong(this.serverStatementId);
+			packet.writeInt((parameterIndex));
+
+			boolean readAny = false;
+			
+			while ((numRead = inStream.read(buf)) != -1) {
+
+				readAny = true;
+				
+				packet.writeBytesNoNull(buf, 0, numRead);
+				bytesInPacket += numRead;
+				totalBytesRead += numRead;
+
+				if (bytesInPacket >= packetIsFullAt) {
+					bytesReadAtLastSend = totalBytesRead;
+
+					mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet,
+							true, null);
+
+					bytesInPacket = 0;
+					packet.clear();
+					packet.writeByte((byte) MysqlDefs.COM_LONG_DATA);
+					packet.writeLong(this.serverStatementId);
+					packet.writeInt((parameterIndex));
+				}
+			}
+
+			if (totalBytesRead != bytesReadAtLastSend) {
+				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
+						null);
+			}
+			
+			if (!readAny) {
+				mysql.sendCommand(MysqlDefs.COM_LONG_DATA, null, packet, true,
+						null);
+			}
+		} catch (IOException ioEx) {
+			throw SQLError.createSQLException(Messages
+					.getString("ServerPreparedStatement.25") //$NON-NLS-1$
+					+ ioEx.toString(), SQLError.SQL_STATE_GENERAL_ERROR);
+		} finally {
+			if (this.connection.getAutoClosePStmtStreams()) {
+				if (inStream != null) {
+					try {
+						inStream.close();
+					} catch (IOException ioEx) {
+						; // ignore
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * @see java.lang.Object#toString()
+	 */
+	public String toString() {
+		StringBuffer toStringBuf = new StringBuffer();
+
+		toStringBuf.append("com.mysql.jdbc.ServerPreparedStatement["); //$NON-NLS-1$
+		toStringBuf.append(this.serverStatementId);
+		toStringBuf.append("] - "); //$NON-NLS-1$
+
+		try {
+			toStringBuf.append(asSql());
+		} catch (SQLException sqlEx) {
+			toStringBuf.append(Messages.getString("ServerPreparedStatement.6")); //$NON-NLS-1$
+			toStringBuf.append(sqlEx);
+		}
+
+		return toStringBuf.toString();
+	}
+
+	protected long getServerStatementId() {
+		return serverStatementId;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SingleByteCharsetConverter.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SingleByteCharsetConverter.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SingleByteCharsetConverter.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,288 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.UnsupportedEncodingException;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Converter for char[]->byte[] and byte[]->char[] for single-byte character
+ * sets.
+ * 
+ * Much faster (5-6x) than the built-in solution that ships with the JVM, even
+ * with JDK-1.4.x and NewIo.
+ * 
+ * @author Mark Matthews
+ */
+public class SingleByteCharsetConverter {
+
+	private static final int BYTE_RANGE = (1 + Byte.MAX_VALUE) - Byte.MIN_VALUE;
+	private static byte[] allBytes = new byte[BYTE_RANGE];
+	private static final Map CONVERTER_MAP = new HashMap();
+
+	private final static byte[] EMPTY_BYTE_ARRAY = new byte[0];
+
+	// The initial charToByteMap, with all char mappings mapped
+	// to (byte) '?', so that unknown characters are mapped to '?'
+	// instead of '\0' (which means end-of-string to MySQL).
+	private static byte[] unknownCharsMap = new byte[65536];
+
+	static {
+		for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
+			allBytes[i - Byte.MIN_VALUE] = (byte) i;
+		}
+
+		for (int i = 0; i < unknownCharsMap.length; i++) {
+			unknownCharsMap[i] = (byte) '?'; // use something 'sane' for
+												// unknown chars
+		}
+	}
+
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	/**
+	 * Get a converter for the given encoding name
+	 * 
+	 * @param encodingName
+	 *            the Java character encoding name
+	 * 
+	 * @return a converter for the given encoding name
+	 * @throws UnsupportedEncodingException
+	 *             if the character encoding is not supported
+	 */
+	public static synchronized SingleByteCharsetConverter getInstance(
+			String encodingName, Connection conn)
+			throws UnsupportedEncodingException, SQLException {
+		SingleByteCharsetConverter instance = (SingleByteCharsetConverter) CONVERTER_MAP
+				.get(encodingName);
+
+		if (instance == null) {
+			instance = initCharset(encodingName);
+		}
+
+		return instance;
+	}
+
+	/**
+	 * Initialize the shared instance of a converter for the given character
+	 * encoding.
+	 * 
+	 * @param javaEncodingName
+	 *            the Java name for the character set to initialize
+	 * @return a converter for the given character set
+	 * @throws UnsupportedEncodingException
+	 *             if the character encoding is not supported
+	 */
+	public static SingleByteCharsetConverter initCharset(String javaEncodingName)
+			throws UnsupportedEncodingException, SQLException {
+		if (CharsetMapping.isMultibyteCharset(javaEncodingName)) {
+			return null;
+		}
+
+		SingleByteCharsetConverter converter = new SingleByteCharsetConverter(
+				javaEncodingName);
+
+		CONVERTER_MAP.put(javaEncodingName, converter);
+
+		return converter;
+	}
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Convert the byte buffer from startPos to a length of length to a string
+	 * using the default platform encoding.
+	 * 
+	 * @param buffer
+	 *            the bytes to convert
+	 * @param startPos
+	 *            the index to start at
+	 * @param length
+	 *            the number of bytes to convert
+	 * @return the String representation of the given bytes
+	 */
+	public static String toStringDefaultEncoding(byte[] buffer, int startPos,
+			int length) {
+		return new String(buffer, startPos, length);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	private char[] byteToChars = new char[BYTE_RANGE];
+
+	private byte[] charToByteMap = new byte[65536];
+
+	/**
+	 * Prevent instantiation, called out of static method initCharset().
+	 * 
+	 * @param encodingName
+	 *            a JVM character encoding
+	 * @throws UnsupportedEncodingException
+	 *             if the JVM does not support the encoding
+	 */
+	private SingleByteCharsetConverter(String encodingName)
+			throws UnsupportedEncodingException {
+		String allBytesString = new String(allBytes, 0, BYTE_RANGE,
+				encodingName);
+		int allBytesLen = allBytesString.length();
+
+		System.arraycopy(unknownCharsMap, 0, this.charToByteMap, 0,
+				this.charToByteMap.length);
+
+		for (int i = 0; i < BYTE_RANGE && i < allBytesLen; i++) {
+			char c = allBytesString.charAt(i);
+			this.byteToChars[i] = c;
+			this.charToByteMap[c] = allBytes[i];
+		}
+	}
+
+	public final byte[] toBytes(char[] c) {
+		if (c == null) {
+			return null;
+		}
+
+		int length = c.length;
+		byte[] bytes = new byte[length];
+
+		for (int i = 0; i < length; i++) {
+			bytes[i] = this.charToByteMap[c[i]];
+		}
+
+		return bytes;
+	}
+
+	public final byte[] toBytes(char[] chars, int offset, int length) {
+		if (chars == null) {
+			return null;
+		}
+
+		if (length == 0) {
+			return EMPTY_BYTE_ARRAY;
+		}
+
+		byte[] bytes = new byte[length];
+
+		for (int i = 0; (i < length); i++) {
+			bytes[i] = this.charToByteMap[chars[i + offset]];
+		}
+
+		return bytes;
+	}
+
+	/**
+	 * Convert the given string to an array of bytes.
+	 * 
+	 * @param s
+	 *            the String to convert
+	 * @return the bytes that make up the String
+	 */
+	public final byte[] toBytes(String s) {
+		if (s == null) {
+			return null;
+		}
+
+		int length = s.length();
+		byte[] bytes = new byte[length];
+
+		for (int i = 0; i < length; i++) {
+			bytes[i] = this.charToByteMap[s.charAt(i)];
+		}
+
+		return bytes;
+	}
+
+	/**
+	 * Convert the given string to an array of bytes.
+	 * 
+	 * @param s
+	 *            the String to convert
+	 * @param offset
+	 *            the offset to start at
+	 * @param length
+	 *            length (max) to convert
+	 * 
+	 * @return the bytes that make up the String
+	 */
+	public final byte[] toBytes(String s, int offset, int length) {
+		if (s == null) {
+			return null;
+		}
+
+		if (length == 0) {
+			return EMPTY_BYTE_ARRAY;
+		}
+
+		byte[] bytes = new byte[length];
+
+		for (int i = 0; (i < length); i++) {
+			char c = s.charAt(i + offset);
+			bytes[i] = this.charToByteMap[c];
+		}
+
+		return bytes;
+	}
+
+	/**
+	 * Convert the byte buffer to a string using this instance's character
+	 * encoding.
+	 * 
+	 * @param buffer
+	 *            the bytes to convert to a String
+	 * @return the converted String
+	 */
+	public final String toString(byte[] buffer) {
+		return toString(buffer, 0, buffer.length);
+	}
+
+	/**
+	 * Convert the byte buffer from startPos to a length of length to a string
+	 * using this instance's character encoding.
+	 * 
+	 * @param buffer
+	 *            the bytes to convert
+	 * @param startPos
+	 *            the index to start at
+	 * @param length
+	 *            the number of bytes to convert
+	 * @return the String representation of the given bytes
+	 */
+	public final String toString(byte[] buffer, int startPos, int length) {
+		char[] charArray = new char[length];
+		int readpoint = startPos;
+
+		for (int i = 0; i < length; i++) {
+			charArray[i] = this.byteToChars[buffer[readpoint] - Byte.MIN_VALUE];
+			readpoint++;
+		}
+
+		return new String(charArray);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SocketFactory.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SocketFactory.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/SocketFactory.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,98 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.IOException;
+
+import java.net.Socket;
+import java.net.SocketException;
+
+import java.util.Properties;
+
+/**
+ * Interface to allow pluggable socket creation in the driver
+ * 
+ * @author Mark Matthews
+ */
+public interface SocketFactory {
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Called by the driver after issuing the MySQL protocol handshake and
+	 * reading the results of the handshake.
+	 * 
+	 * @throws SocketException
+	 *             if a socket error occurs
+	 * @throws IOException
+	 *             if an I/O error occurs
+	 * 
+	 * @return the socket to use after the handshake
+	 */
+	Socket afterHandshake() throws SocketException, IOException;
+
+	/**
+	 * Called by the driver before issuing the MySQL protocol handshake. Should
+	 * return the socket instance that should be used during the handshake.
+	 * 
+	 * @throws SocketException
+	 *             if a socket error occurs
+	 * @throws IOException
+	 *             if an I/O error occurs
+	 * 
+	 * @return the socket to use before the handshake
+	 */
+	Socket beforeHandshake() throws SocketException, IOException;
+
+	/**
+	 * Creates a new socket using the given properties. Properties are parsed by
+	 * the driver from the URL. All properties other than sensitive ones (user
+	 * and password) are passed to this method. The driver will instantiate the
+	 * socket factory with the class name given in the property
+	 * &quot;socketFactory&quot;, where the standard is
+	 * <code>com.mysql.jdbc.StandardSocketFactory</code> Implementing classes
+	 * are responsible for handling synchronization of this method (if needed).
+	 * 
+	 * @param host
+	 *            the hostname passed in the JDBC URL. It will be a single
+	 *            hostname, as the driver parses multi-hosts (for failover) and
+	 *            calls this method for each host connection attempt.
+	 * 
+	 * @param portNumber
+	 *            the port number to connect to (if required).
+	 * 
+	 * @param props
+	 *            properties passed to the driver via the URL and/or properties
+	 *            instance.
+	 * 
+	 * @return a socket connected to the given host
+	 * @throws SocketException
+	 *             if a socket error occurs
+	 * @throws IOException
+	 *             if an I/O error occurs
+	 */
+	Socket connect(String host, int portNumber, Properties props)
+			throws SocketException, IOException;
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/StandardSocketFactory.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/StandardSocketFactory.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/StandardSocketFactory.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,224 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.IOException;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import java.net.InetAddress;
+import java.net.Socket;
+import java.net.SocketException;
+
+import java.util.Properties;
+
+/**
+ * Socket factory for vanilla TCP/IP sockets (the standard)
+ * 
+ * @author Mark Matthews
+ */
+public class StandardSocketFactory implements SocketFactory {
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	/** The hostname to connect to */
+	protected String host = null;
+
+	/** The port number to connect to */
+	protected int port = 3306;
+
+	/** 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.
+	 * 
+	 * @throws SocketException
+	 *             if a socket error occurs
+	 * @throws IOException
+	 *             if an I/O error occurs
+	 * 
+	 * @return The socket to use after the handshake
+	 */
+	public Socket afterHandshake() throws SocketException, IOException {
+		return this.rawSocket;
+	}
+
+	/**
+	 * Called by the driver before issuing the MySQL protocol handshake. Should
+	 * return the socket instance that should be used during the handshake.
+	 * 
+	 * @throws SocketException
+	 *             if a socket error occurs
+	 * @throws IOException
+	 *             if an I/O error occurs
+	 * 
+	 * @return the socket to use before the handshake
+	 */
+	public Socket beforeHandshake() throws SocketException, IOException {
+		return this.rawSocket;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.SocketFactory#createSocket(Properties)
+	 */
+	public Socket connect(String hostname, int portNumber, Properties props)
+			throws SocketException, IOException {
+
+		if (props != null) {
+			this.host = hostname;
+
+			this.port = portNumber;
+
+			boolean hasConnectTimeoutMethod = false;
+
+			Method connectWithTimeoutMethod = null;
+
+			try {
+				// Have to do this with reflection, otherwise older JVMs croak
+				Class socketAddressClass = Class
+						.forName("java.net.SocketAddress");
+
+				connectWithTimeoutMethod = Socket.class.getMethod("connect",
+						new Class[] { socketAddressClass, Integer.TYPE });
+
+				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");
+
+			if (connectTimeoutStr != null) {
+				try {
+					connectTimeout = Integer.parseInt(connectTimeoutStr);
+				} catch (NumberFormatException nfe) {
+					throw new SocketException("Illegal value '"
+							+ connectTimeoutStr + "' for connectTimeout");
+				}
+			}
+
+			if (this.host != null) {
+				if (!hasConnectTimeoutMethod || (connectTimeout == 0)) {
+					InetAddress[] possibleAddresses = InetAddress
+							.getAllByName(this.host);
+
+					Exception 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);
+
+							break;
+						} catch (Exception ex) {
+							caughtWhileConnecting = ex;
+						}
+					}
+
+					if (rawSocket == null) {
+						throw new SocketException(caughtWhileConnecting
+								.toString());
+					}
+				} 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);
+
+				Exception 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 {
+						Object sockAddr = addrConstructor
+								.newInstance(new Object[] { possibleAddresses[i],
+										new Integer(port) });
+						
+								rawSocket = new Socket();
+								connectWithTimeoutMethod.invoke(rawSocket,
+										new Object[] { sockAddr,
+												new Integer(connectTimeout) });
+
+								break;
+							} catch (Exception ex) {
+								rawSocket = null;
+
+								caughtWhileConnecting = ex;
+							}
+						}
+
+						if (rawSocket == null) {
+							throw new SocketException(caughtWhileConnecting
+									.toString());
+						}
+
+					} catch (Throwable t) {
+						if (!(t instanceof SocketException)) {
+							throw new SocketException(t.toString());
+						}
+
+						throw (SocketException) t;
+					}
+				}
+
+				try {
+					this.rawSocket.setTcpNoDelay(true);
+				} catch (Exception ex) {
+					/* Ignore */
+					;
+				}
+
+				return this.rawSocket;
+			}
+		}
+
+		throw new SocketException("Unable to create socket");
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Statement.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Statement.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Statement.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,2307 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.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.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
+ */
+public class Statement implements java.sql.Statement {
+	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;
+	}
+
+	/**
+	 * 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().
+	 * 
+	 * @throws SQLException
+	 */
+	public void enableStreamingResults() throws SQLException {
+		setFetchSize(Integer.MIN_VALUE);
+		setResultSetType(ResultSet.TYPE_FORWARD_ONLY);
+	}
+
+	/**
+	 * 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()
+	 * @throws SQLException
+	 */
+	private int[] executeBatchUsingMultiQueries(boolean multiQueriesEnabled,
+			int nbrCommands) 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
+	 */
+	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);
+
+				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
+	 */
+	public int executeUpdate(String sql) throws SQLException {
+		return executeUpdate(sql, false);
+	}
+
+	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;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/StringUtils.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/StringUtils.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/StringUtils.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1448 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.math.BigDecimal;
+
+import java.sql.SQLException;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+/**
+ * Various utility methods for converting to/from byte arrays in the platform
+ * encoding
+ * 
+ * @author Mark Matthews
+ */
+public class StringUtils {
+	
+	private static final int BYTE_RANGE = (1 + Byte.MAX_VALUE) - Byte.MIN_VALUE;
+
+	private static byte[] allBytes = new byte[BYTE_RANGE];
+
+	private static char[] byteToChars = new char[BYTE_RANGE];
+
+	private static Method toPlainStringMethod;
+
+	static final int WILD_COMPARE_MATCH_NO_WILD = 0;
+
+	static final int WILD_COMPARE_MATCH_WITH_WILD = 1;
+
+	static final int WILD_COMPARE_NO_MATCH = -1;
+
+	static {
+		for (int i = Byte.MIN_VALUE; i <= Byte.MAX_VALUE; i++) {
+			allBytes[i - Byte.MIN_VALUE] = (byte) i;
+		}
+
+		String allBytesString = new String(allBytes, 0, Byte.MAX_VALUE
+				- Byte.MIN_VALUE);
+
+		int allBytesStringLen = allBytesString.length();
+
+		for (int i = 0; (i < (Byte.MAX_VALUE - Byte.MIN_VALUE))
+				&& (i < allBytesStringLen); i++) {
+			byteToChars[i] = allBytesString.charAt(i);
+		}
+
+		try {
+			toPlainStringMethod = BigDecimal.class.getMethod("toPlainString",
+					new Class[0]);
+		} catch (NoSuchMethodException nsme) {
+			// that's okay, we fallback to .toString()
+		}
+	}
+
+	/**
+	 * Takes care of the fact that Sun changed the output of
+	 * BigDecimal.toString() between JDK-1.4 and JDK 5
+	 * 
+	 * @param decimal
+	 *            the big decimal to stringify
+	 * 
+	 * @return a string representation of 'decimal'
+	 */
+	public static String consistentToString(BigDecimal decimal) {
+		if (decimal == null) {
+			return null;
+		}
+
+		if (toPlainStringMethod != null) {
+			try {
+				return (String) toPlainStringMethod.invoke(decimal, null);
+			} catch (InvocationTargetException invokeEx) {
+				// that's okay, we fall-through to decimal.toString()
+			} catch (IllegalAccessException accessEx) {
+				// that's okay, we fall-through to decimal.toString()
+			}
+		}
+
+		return decimal.toString();
+	}
+
+	/**
+	 * Dumps the given bytes to STDOUT as a hex dump (up to length bytes).
+	 * 
+	 * @param byteBuffer
+	 *            the data to print as hex
+	 * @param length
+	 *            the number of bytes to print
+	 * 
+	 * @return ...
+	 */
+	public static final String dumpAsHex(byte[] byteBuffer, int length) {
+		StringBuffer outputBuf = new StringBuffer(length * 4);
+
+		int p = 0;
+		int rows = length / 8;
+
+		for (int i = 0; (i < rows) && (p < length); i++) {
+			int ptemp = p;
+
+			for (int j = 0; j < 8; j++) {
+				String hexVal = Integer.toHexString(byteBuffer[ptemp] & 0xff);
+
+				if (hexVal.length() == 1) {
+					hexVal = "0" + hexVal; //$NON-NLS-1$
+				}
+
+				outputBuf.append(hexVal + " "); //$NON-NLS-1$
+				ptemp++;
+			}
+
+			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$
+				} else {
+					outputBuf.append(". "); //$NON-NLS-1$
+				}
+
+				p++;
+			}
+
+			outputBuf.append("\n"); //$NON-NLS-1$
+		}
+
+		int n = 0;
+
+		for (int i = p; i < length; i++) {
+			String hexVal = Integer.toHexString(byteBuffer[i] & 0xff);
+
+			if (hexVal.length() == 1) {
+				hexVal = "0" + hexVal; //$NON-NLS-1$
+			}
+
+			outputBuf.append(hexVal + " "); //$NON-NLS-1$
+			n++;
+		}
+
+		for (int i = n; i < 8; i++) {
+			outputBuf.append("   "); //$NON-NLS-1$
+		}
+
+		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$
+			} else {
+				outputBuf.append(". "); //$NON-NLS-1$
+			}
+		}
+
+		outputBuf.append("\n"); //$NON-NLS-1$
+
+		return outputBuf.toString();
+	}
+
+	private static boolean endsWith(byte[] dataFrom, String suffix) {
+		for (int i = 1; i <= suffix.length(); i++) {
+			int dfOffset = dataFrom.length - i;
+			int suffixOffset = suffix.length() - i;
+			if (dataFrom[dfOffset] != suffix.charAt(suffixOffset)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Unfortunately, SJIS has 0x5c as a high byte in some of its double-byte
+	 * characters, so we need to escape it.
+	 * 
+	 * @param origBytes
+	 *            the original bytes in SJIS format
+	 * @param origString
+	 *            the string that had .getBytes() called on it
+	 * @param offset
+	 *            where to start converting from
+	 * @param length
+	 *            how many characters to convert.
+	 * 
+	 * @return byte[] with 0x5c escaped
+	 */
+	public static byte[] escapeEasternUnicodeByteStream(byte[] origBytes,
+			String origString, int offset, int length) {
+		if ((origBytes == null) || (origBytes.length == 0)) {
+			return origBytes;
+		}
+
+		int bytesLen = origBytes.length;
+		int bufIndex = 0;
+		int strIndex = 0;
+
+		ByteArrayOutputStream bytesOut = new ByteArrayOutputStream(bytesLen);
+
+		while (true) {
+			if (origString.charAt(strIndex) == '\\') {
+				// write it out as-is
+				bytesOut.write(origBytes[bufIndex++]);
+
+				// bytesOut.write(origBytes[bufIndex++]);
+			} else {
+				// Grab the first byte
+				int loByte = origBytes[bufIndex];
+
+				if (loByte < 0) {
+					loByte += 256; // adjust for signedness/wrap-around
+				}
+
+				// We always write the first byte
+				bytesOut.write(loByte);
+
+				//
+				// The codepage characters in question exist between
+				// 0x81-0x9F and 0xE0-0xFC...
+				//
+				// See:
+				//
+				// http://www.microsoft.com/GLOBALDEV/Reference/dbcs/932.htm
+				//
+				// Problematic characters in GBK
+				//
+				// U+905C : CJK UNIFIED IDEOGRAPH
+				//
+				// Problematic characters in Big5
+				//
+				// B9F0 = U+5C62 : CJK UNIFIED IDEOGRAPH
+				//
+				if (loByte >= 0x80) {
+					if (bufIndex < (bytesLen - 1)) {
+						int hiByte = origBytes[bufIndex + 1];
+
+						if (hiByte < 0) {
+							hiByte += 256; // adjust for signedness/wrap-around
+						}
+
+						// write the high byte here, and increment the index
+						// for the high byte
+						bytesOut.write(hiByte);
+						bufIndex++;
+
+						// escape 0x5c if necessary
+						if (hiByte == 0x5C) {
+							bytesOut.write(hiByte);
+						}
+					}
+				} else if (loByte == 0x5c) {
+					if (bufIndex < (bytesLen - 1)) {
+						int hiByte = origBytes[bufIndex + 1];
+
+						if (hiByte < 0) {
+							hiByte += 256; // adjust for signedness/wrap-around
+						}
+
+						if (hiByte == 0x62) {
+							// we need to escape the 0x5c
+							bytesOut.write(0x5c);
+							bytesOut.write(0x62);
+							bufIndex++;
+						}
+					}
+				}
+
+				bufIndex++;
+			}
+
+			if (bufIndex >= bytesLen) {
+				// we're done
+				break;
+			}
+
+			strIndex++;
+		}
+
+		return bytesOut.toByteArray();
+	}
+
+	/**
+	 * Returns the first non whitespace char, converted to upper case
+	 * 
+	 * @param searchIn
+	 *            the string to search in
+	 * 
+	 * @return the first non-whitespace character, upper cased.
+	 */
+	public static char firstNonWsCharUc(String searchIn) {
+		if (searchIn == null) {
+			return 0;
+		}
+
+		int length = searchIn.length();
+
+		for (int i = 0; i < length; i++) {
+			char c = searchIn.charAt(i);
+
+			if (!Character.isWhitespace(c)) {
+				return Character.toUpperCase(c);
+			}
+		}
+
+		return 0;
+	}
+
+	/**
+	 * Adds '+' to decimal numbers that are positive (MySQL doesn't understand
+	 * them otherwise
+	 * 
+	 * @param dString
+	 *            The value as a string
+	 * 
+	 * @return String the string with a '+' added (if needed)
+	 */
+	public static final String fixDecimalExponent(String dString) {
+		int ePos = dString.indexOf("E"); //$NON-NLS-1$
+
+		if (ePos == -1) {
+			ePos = dString.indexOf("e"); //$NON-NLS-1$
+		}
+
+		if (ePos != -1) {
+			if (dString.length() > (ePos + 1)) {
+				char maybeMinusChar = dString.charAt(ePos + 1);
+
+				if (maybeMinusChar != '-' && maybeMinusChar != '+') {
+					StringBuffer buf = new StringBuffer(dString.length() + 1);
+					buf.append(dString.substring(0, ePos + 1));
+					buf.append('+');
+					buf.append(dString.substring(ePos + 1, dString.length()));
+					dString = buf.toString();
+				}
+			}
+		}
+
+		return dString;
+	}
+
+	public static final byte[] getBytes(char[] c,
+			SingleByteCharsetConverter converter, String encoding,
+			String serverEncoding, boolean parserKnowsUnicode)
+			throws SQLException {
+		try {
+			byte[] b = null;
+
+			if (converter != null) {
+				b = converter.toBytes(c);
+			} else if (encoding == null) {
+				b = new String(c).getBytes();
+			} else {
+				String s = new String(c);
+
+				b = s.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$
+		}
+	}
+
+	public static final byte[] getBytes(char[] c,
+			SingleByteCharsetConverter converter, String encoding,
+			String serverEncoding, int offset, int length,
+			boolean parserKnowsUnicode) throws SQLException {
+		try {
+			byte[] b = null;
+
+			if (converter != null) {
+				b = converter.toBytes(c, offset, length);
+			} else if (encoding == null) {
+				byte[] temp = new String(c, offset, length).getBytes();
+
+				length = temp.length;
+				
+				b = new byte[length];
+				System.arraycopy(temp, 0, b, 0, length);
+			} else {
+				String s = new String(c, offset, length);
+
+				byte[] temp = s.getBytes(encoding);
+
+				length = temp.length;
+				
+				b = new byte[length];
+				System.arraycopy(temp, 0, b, 0, length);
+
+				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, offset, length);
+					}
+				}
+			}
+
+			return b;
+		} catch (UnsupportedEncodingException uee) {
+			throw SQLError.createSQLException(Messages.getString("StringUtils.10") //$NON-NLS-1$
+					+ encoding + Messages.getString("StringUtils.11"),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	public static final byte[] getBytes(char[] c, String encoding,
+			String serverEncoding, boolean parserKnowsUnicode, Connection conn)
+			throws SQLException {
+		try {
+			
+			SingleByteCharsetConverter converter = null;
+			
+			if (conn != null) {
+				converter = conn.getCharsetConverter(encoding);
+			} else {
+				converter = SingleByteCharsetConverter.getInstance(encoding, null);
+			}
+
+			return getBytes(c, converter, encoding, serverEncoding,
+					parserKnowsUnicode);
+		} catch (UnsupportedEncodingException uee) {
+			throw SQLError.createSQLException(Messages.getString("StringUtils.0") //$NON-NLS-1$
+					+ encoding + Messages.getString("StringUtils.1"),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Returns the byte[] representation of the given string (re)using the given
+	 * charset converter, and the given encoding.
+	 * 
+	 * @param s
+	 *            the string to convert
+	 * @param converter
+	 *            the converter to reuse
+	 * @param encoding
+	 *            the character encoding to use
+	 * @param serverEncoding
+	 *            DOCUMENT ME!
+	 * @param parserKnowsUnicode
+	 *            DOCUMENT ME!
+	 * 
+	 * @return byte[] representation of the string
+	 * 
+	 * @throws SQLException
+	 *             if an encoding unsupported by the JVM is supplied.
+	 */
+	public static final byte[] getBytes(String s,
+			SingleByteCharsetConverter converter, String encoding,
+			String serverEncoding, boolean parserKnowsUnicode)
+			throws SQLException {
+		try {
+			byte[] b = null;
+
+			if (converter != null) {
+				b = converter.toBytes(s);
+			} else if (encoding == null) {
+				b = s.getBytes();
+			} else {
+				b = s.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!
+	 * 
+	 * @param s
+	 *            DOCUMENT ME!
+	 * @param converter
+	 *            DOCUMENT ME!
+	 * @param encoding
+	 *            DOCUMENT ME!
+	 * @param serverEncoding
+	 *            DOCUMENT ME!
+	 * @param offset
+	 *            DOCUMENT ME!
+	 * @param length
+	 *            DOCUMENT ME!
+	 * @param parserKnowsUnicode
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public static final byte[] getBytes(String s,
+			SingleByteCharsetConverter converter, String encoding,
+			String serverEncoding, int offset, int length,
+			boolean parserKnowsUnicode) throws SQLException {
+		try {
+			byte[] b = null;
+
+			if (converter != null) {
+				b = converter.toBytes(s, offset, length);
+			} else if (encoding == null) {
+				byte[] temp = s.substring(offset, offset + length).getBytes();
+
+				length = temp.length;
+				
+				b = new byte[length];
+				System.arraycopy(temp, 0, b, 0, length);
+			} else {
+
+				byte[] temp = s.substring(offset, offset + length)
+					.getBytes(encoding);
+
+				length = temp.length;
+				
+				b = new byte[length];
+				System.arraycopy(temp, 0, b, 0, length);
+
+				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, offset, length);
+					}
+				}
+			}
+
+			return b;
+		} catch (UnsupportedEncodingException uee) {
+			throw SQLError.createSQLException(Messages.getString("StringUtils.10") //$NON-NLS-1$
+					+ encoding + Messages.getString("StringUtils.11"),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	/**
+	 * Returns the byte[] representation of the given string using given
+	 * encoding.
+	 * 
+	 * @param s
+	 *            the string to convert
+	 * @param encoding
+	 *            the character encoding to use
+	 * @param parserKnowsUnicode
+	 *            DOCUMENT ME!
+	 * 
+	 * @return byte[] representation of the string
+	 * 
+	 * @throws SQLException
+	 *             if an encoding unsupported by the JVM is supplied.
+	 */
+	public static final byte[] getBytes(String s, String encoding,
+			String serverEncoding, boolean parserKnowsUnicode, Connection conn)
+			throws SQLException {
+		try {
+			SingleByteCharsetConverter converter = null;
+			
+			if (conn != null) {
+				converter = conn.getCharsetConverter(encoding);
+			} else {
+				converter = SingleByteCharsetConverter.getInstance(encoding, null);
+			}
+
+			return getBytes(s, converter, encoding, serverEncoding,
+					parserKnowsUnicode);
+		} catch (UnsupportedEncodingException uee) {
+			throw SQLError.createSQLException(Messages.getString("StringUtils.0") //$NON-NLS-1$
+					+ encoding + Messages.getString("StringUtils.1"),
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$
+		}
+	}
+
+	public static int getInt(byte[] buf) throws NumberFormatException {
+		int base = 10;
+
+		int s = 0;
+
+		/* Skip white space. */
+		while (Character.isWhitespace((char) buf[s]) && (s < buf.length)) {
+			++s;
+		}
+
+		if (s == buf.length) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		/* Check for a sign. */
+		boolean negative = false;
+
+		if ((char) buf[s] == '-') {
+			negative = true;
+			++s;
+		} else if ((char) buf[s] == '+') {
+			++s;
+		}
+
+		/* Save the pointer so we can check later if anything happened. */
+		int save = s;
+
+		int cutoff = Integer.MAX_VALUE / base;
+		int cutlim = (Integer.MAX_VALUE % base);
+
+		if (negative) {
+			cutlim++;
+		}
+
+		boolean overflow = false;
+
+		int i = 0;
+
+		for (; s < buf.length; s++) {
+			char c = (char) buf[s];
+
+			if (Character.isDigit(c)) {
+				c -= '0';
+			} else if (Character.isLetter(c)) {
+				c = (char) (Character.toUpperCase(c) - 'A' + 10);
+			} else {
+				break;
+			}
+
+			if (c >= base) {
+				break;
+			}
+
+			/* Check for overflow. */
+			if ((i > cutoff) || ((i == cutoff) && (c > cutlim))) {
+				overflow = true;
+			} else {
+				i *= base;
+				i += c;
+			}
+		}
+
+		if (s == save) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		if (overflow) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		/* Return the result of the appropriate sign. */
+		return (negative ? (-i) : i);
+	}
+
+	public static long getLong(byte[] buf) throws NumberFormatException {
+		int base = 10;
+
+		int s = 0;
+
+		/* Skip white space. */
+		while (Character.isWhitespace((char) buf[s]) && (s < buf.length)) {
+			++s;
+		}
+
+		if (s == buf.length) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		/* Check for a sign. */
+		boolean negative = false;
+
+		if ((char) buf[s] == '-') {
+			negative = true;
+			++s;
+		} else if ((char) buf[s] == '+') {
+			++s;
+		}
+
+		/* Save the pointer so we can check later if anything happened. */
+		int save = s;
+
+		long cutoff = Long.MAX_VALUE / base;
+		long cutlim = (int) (Long.MAX_VALUE % base);
+
+		if (negative) {
+			cutlim++;
+		}
+
+		boolean overflow = false;
+		long i = 0;
+
+		for (; s < buf.length; s++) {
+			char c = (char) buf[s];
+
+			if (Character.isDigit(c)) {
+				c -= '0';
+			} else if (Character.isLetter(c)) {
+				c = (char) (Character.toUpperCase(c) - 'A' + 10);
+			} else {
+				break;
+			}
+
+			if (c >= base) {
+				break;
+			}
+
+			/* Check for overflow. */
+			if ((i > cutoff) || ((i == cutoff) && (c > cutlim))) {
+				overflow = true;
+			} else {
+				i *= base;
+				i += c;
+			}
+		}
+
+		if (s == save) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		if (overflow) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		/* Return the result of the appropriate sign. */
+		return (negative ? (-i) : i);
+	}
+
+	public static short getShort(byte[] buf) throws NumberFormatException {
+		short base = 10;
+
+		int s = 0;
+
+		/* Skip white space. */
+		while (Character.isWhitespace((char) buf[s]) && (s < buf.length)) {
+			++s;
+		}
+
+		if (s == buf.length) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		/* Check for a sign. */
+		boolean negative = false;
+
+		if ((char) buf[s] == '-') {
+			negative = true;
+			++s;
+		} else if ((char) buf[s] == '+') {
+			++s;
+		}
+
+		/* Save the pointer so we can check later if anything happened. */
+		int save = s;
+
+		short cutoff = (short) (Short.MAX_VALUE / base);
+		short cutlim = (short) (Short.MAX_VALUE % base);
+
+		if (negative) {
+			cutlim++;
+		}
+
+		boolean overflow = false;
+		short i = 0;
+
+		for (; s < buf.length; s++) {
+			char c = (char) buf[s];
+
+			if (Character.isDigit(c)) {
+				c -= '0';
+			} else if (Character.isLetter(c)) {
+				c = (char) (Character.toUpperCase(c) - 'A' + 10);
+			} else {
+				break;
+			}
+
+			if (c >= base) {
+				break;
+			}
+
+			/* Check for overflow. */
+			if ((i > cutoff) || ((i == cutoff) && (c > cutlim))) {
+				overflow = true;
+			} else {
+				i *= base;
+				i += c;
+			}
+		}
+
+		if (s == save) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		if (overflow) {
+			throw new NumberFormatException(new String(buf));
+		}
+
+		/* Return the result of the appropriate sign. */
+		return (negative ? (short) -i : (short) i);
+	}
+
+	public final static int indexOfIgnoreCase(int startingPosition,
+			String searchIn, String searchFor) {
+		if ((searchIn == null) || (searchFor == null)
+				|| startingPosition > searchIn.length()) {
+			return -1;
+		}
+
+		int patternLength = searchFor.length();
+		int stringLength = searchIn.length();
+		int stopSearchingAt = stringLength - patternLength;
+
+		int i = startingPosition;
+
+		if (patternLength == 0) {
+			return -1;
+		}
+
+		// Brute force string pattern matching
+		// Some locales don't follow upper-case rule, so need to check both
+		char firstCharOfPatternUc = Character.toUpperCase(searchFor.charAt(0));
+		char firstCharOfPatternLc = Character.toLowerCase(searchFor.charAt(0));
+
+		lookForFirstChar: while (true) {
+			while ((i < stopSearchingAt)
+					&& (Character.toUpperCase(searchIn.charAt(i)) != firstCharOfPatternUc)
+					&& Character.toLowerCase(searchIn.charAt(i)) != firstCharOfPatternLc) {
+				i++;
+			}
+
+			if (i > stopSearchingAt) {
+				return -1;
+			}
+
+			int j = i + 1;
+			int end = (j + patternLength) - 1;
+
+			int k = 1; // start at second char of pattern
+
+			while (j < end) {
+				int searchInPos = j++;
+				int searchForPos = k++;
+
+				if (Character.toUpperCase(searchIn.charAt(searchInPos)) != Character
+						.toUpperCase(searchFor.charAt(searchForPos))) {
+					i++;
+
+					// start over
+					continue lookForFirstChar;
+				}
+
+				// Georgian and Turkish locales don't have same convention, so
+				// need to check lowercase
+				// too!
+				if (Character.toLowerCase(searchIn.charAt(searchInPos)) != Character
+						.toLowerCase(searchFor.charAt(searchForPos))) {
+					i++;
+
+					// start over
+					continue lookForFirstChar;
+				}
+			}
+
+			return i; // found entire pattern
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param searchIn
+	 *            DOCUMENT ME!
+	 * @param searchFor
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public final static int indexOfIgnoreCase(String searchIn, String searchFor) {
+		return indexOfIgnoreCase(0, searchIn, searchFor);
+	}
+
+	public static int indexOfIgnoreCaseRespectMarker(int startAt, String src,
+			String target, String marker, String markerCloses,
+			boolean allowBackslashEscapes) {
+		char contextMarker = Character.MIN_VALUE;
+		boolean escaped = false;
+		int markerTypeFound = 0;
+		int srcLength = src.length();
+		int ind = 0;
+
+		for (int i = startAt; i < srcLength; i++) {
+			char c = src.charAt(i);
+
+			if (allowBackslashEscapes && c == '\\') {
+				escaped = !escaped;
+			} else if (c == markerCloses.charAt(markerTypeFound) && !escaped) {
+				contextMarker = Character.MIN_VALUE;
+			} else if ((ind = marker.indexOf(c)) != -1 && !escaped
+					&& contextMarker == Character.MIN_VALUE) {
+				markerTypeFound = ind;
+				contextMarker = c;
+			} else if (c == target.charAt(0) && !escaped
+					&& contextMarker == Character.MIN_VALUE) {
+				if (indexOfIgnoreCase(i, src, target) != -1)
+					return i;
+			}
+		}
+
+		return -1;
+
+	}
+
+	public static int indexOfIgnoreCaseRespectQuotes(int startAt, String src,
+			String target, char quoteChar, boolean allowBackslashEscapes) {
+		char contextMarker = Character.MIN_VALUE;
+		boolean escaped = false;
+
+		int srcLength = src.length();
+
+		for (int i = startAt; i < srcLength; i++) {
+			char c = src.charAt(i);
+
+			if (allowBackslashEscapes && c == '\\') {
+				escaped = !escaped;
+			} else if (c == contextMarker && !escaped) {
+				contextMarker = Character.MIN_VALUE;
+			} else if (c == quoteChar && !escaped
+					&& contextMarker == Character.MIN_VALUE) {
+				contextMarker = c;
+			} else if (c == target.charAt(0) && !escaped
+					&& contextMarker == Character.MIN_VALUE) {
+				if (startsWithIgnoreCase(src, i, target))
+					return i;
+			}
+		}
+
+		return -1;
+
+	}
+
+	/**
+	 * Splits stringToSplit into a list, using the given delimitter
+	 * 
+	 * @param stringToSplit
+	 *            the string to split
+	 * @param delimitter
+	 *            the string to split on
+	 * @param trim
+	 *            should the split strings be whitespace trimmed?
+	 * 
+	 * @return the list of strings, split by delimitter
+	 * 
+	 * @throws IllegalArgumentException
+	 *             DOCUMENT ME!
+	 */
+	public static final List split(String stringToSplit, String delimitter,
+			boolean trim) {
+		if (stringToSplit == null) {
+			return new ArrayList();
+		}
+
+		if (delimitter == null) {
+			throw new IllegalArgumentException();
+		}
+
+		StringTokenizer tokenizer = new StringTokenizer(stringToSplit,
+				delimitter, false);
+
+		List splitTokens = new ArrayList(tokenizer.countTokens());
+
+		while (tokenizer.hasMoreTokens()) {
+			String token = tokenizer.nextToken();
+
+			if (trim) {
+				token = token.trim();
+			}
+
+			splitTokens.add(token);
+		}
+
+		return splitTokens;
+	}
+
+	/**
+	 * Splits stringToSplit into a list, using the given delimitter
+	 * 
+	 * @param stringToSplit
+	 *            the string to split
+	 * @param delimitter
+	 *            the string to split on
+	 * @param trim
+	 *            should the split strings be whitespace trimmed?
+	 * 
+	 * @return the list of strings, split by delimiter
+	 * 
+	 * @throws IllegalArgumentException
+	 *             DOCUMENT ME!
+	 */
+	public static final List split(String stringToSplit, String delimiter,
+			String markers, String markerCloses, boolean trim) {
+		if (stringToSplit == null) {
+			return new ArrayList();
+		}
+
+		if (delimiter == null) {
+			throw new IllegalArgumentException();
+		}
+
+		int delimPos = 0;
+		int currentPos = 0;
+
+		List splitTokens = new ArrayList();
+
+		while ((delimPos = indexOfIgnoreCaseRespectMarker(currentPos,
+				stringToSplit, delimiter, markers, markerCloses, false)) != -1) {
+			String token = stringToSplit.substring(currentPos, delimPos);
+
+			if (trim) {
+				token = token.trim();
+			}
+
+			splitTokens.add(token);
+			currentPos = delimPos + 1;
+		}
+
+		if (currentPos < stringToSplit.length()) {
+			String token = stringToSplit.substring(currentPos);
+
+			if (trim) {
+				token = token.trim();
+			}
+
+			splitTokens.add(token);
+		}
+
+		return splitTokens;
+	}
+
+	private static boolean startsWith(byte[] dataFrom, String chars) {
+		for (int i = 0; i < chars.length(); i++) {
+			if (dataFrom[i] != chars.charAt(i)) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	/**
+	 * Determines whether or not the string 'searchIn' contains the string
+	 * 'searchFor', dis-regarding case starting at 'startAt' Shorthand for a
+	 * String.regionMatch(...)
+	 * 
+	 * @param searchIn
+	 *            the string to search in
+	 * @param startAt
+	 *            the position to start at
+	 * @param searchFor
+	 *            the string to search for
+	 * 
+	 * @return whether searchIn starts with searchFor, ignoring case
+	 */
+	public static boolean startsWithIgnoreCase(String searchIn, int startAt,
+			String searchFor) {
+		return searchIn.regionMatches(true, startAt, searchFor, 0, searchFor
+				.length());
+	}
+
+	/**
+	 * Determines whether or not the string 'searchIn' contains the string
+	 * 'searchFor', dis-regarding case. Shorthand for a String.regionMatch(...)
+	 * 
+	 * @param searchIn
+	 *            the string to search in
+	 * @param searchFor
+	 *            the string to search for
+	 * 
+	 * @return whether searchIn starts with searchFor, ignoring case
+	 */
+	public static boolean startsWithIgnoreCase(String searchIn, String searchFor) {
+		return startsWithIgnoreCase(searchIn, 0, searchFor);
+	}
+
+	/**
+	 * Determines whether or not the sting 'searchIn' contains the string
+	 * 'searchFor', disregarding case,leading whitespace and non-alphanumeric
+	 * characters.
+	 * 
+	 * @param searchIn
+	 *            the string to search in
+	 * @param searchFor
+	 *            the string to search for
+	 * 
+	 * @return true if the string starts with 'searchFor' ignoring whitespace
+	 */
+	public static boolean startsWithIgnoreCaseAndNonAlphaNumeric(
+			String searchIn, String searchFor) {
+		if (searchIn == null) {
+			return searchFor == null;
+		}
+
+		int beginPos = 0;
+
+		int inLength = searchIn.length();
+
+		for (beginPos = 0; beginPos < inLength; beginPos++) {
+			char c = searchIn.charAt(beginPos);
+
+			if (Character.isLetterOrDigit(c)) {
+				break;
+			}
+		}
+
+		return startsWithIgnoreCase(searchIn, beginPos, searchFor);
+	}
+
+	/**
+	 * Determines whether or not the sting 'searchIn' contains the string
+	 * 'searchFor', disregarding case and leading whitespace
+	 * 
+	 * @param searchIn
+	 *            the string to search in
+	 * @param searchFor
+	 *            the string to search for
+	 * 
+	 * @return true if the string starts with 'searchFor' ignoring whitespace
+	 */
+	public static boolean startsWithIgnoreCaseAndWs(String searchIn,
+			String searchFor) {
+		return startsWithIgnoreCaseAndWs(searchIn, searchFor, 0);
+	}
+	
+	/**
+	 * Determines whether or not the sting 'searchIn' contains the string
+	 * 'searchFor', disregarding case and leading whitespace
+	 * 
+	 * @param searchIn
+	 *            the string to search in
+	 * @param searchFor
+	 *            the string to search for
+	 * @param beginPos
+	 *            where to start searching
+	 * 
+	 * @return true if the string starts with 'searchFor' ignoring whitespace
+	 */
+	
+	public static boolean startsWithIgnoreCaseAndWs(String searchIn,
+			String searchFor, int beginPos) {
+		if (searchIn == null) {
+			return searchFor == null;
+		}
+
+		int inLength = searchIn.length();
+
+		for (; beginPos < inLength; beginPos++) {
+			if (!Character.isWhitespace(searchIn.charAt(beginPos))) {
+				break;
+			}
+		}
+
+		return startsWithIgnoreCase(searchIn, beginPos, searchFor);
+	}
+
+	/**
+	 * @param bytesToStrip
+	 * @param prefix
+	 * @param suffix
+	 * @return
+	 */
+	public static byte[] stripEnclosure(byte[] source, String prefix,
+			String suffix) {
+		if (source.length >= prefix.length() + suffix.length()
+				&& startsWith(source, prefix) && endsWith(source, suffix)) {
+
+			int totalToStrip = prefix.length() + suffix.length();
+			int enclosedLength = source.length - totalToStrip;
+			byte[] enclosed = new byte[enclosedLength];
+
+			int startPos = prefix.length();
+			int numToCopy = enclosed.length;
+			System.arraycopy(source, startPos, enclosed, 0, numToCopy);
+
+			return enclosed;
+		}
+		return source;
+	}
+
+	/**
+	 * Returns the bytes as an ASCII String.
+	 * 
+	 * @param buffer
+	 *            the bytes representing the string
+	 * 
+	 * @return The ASCII String.
+	 */
+	public static final String toAsciiString(byte[] buffer) {
+		return toAsciiString(buffer, 0, buffer.length);
+	}
+
+	/**
+	 * Returns the bytes as an ASCII String.
+	 * 
+	 * @param buffer
+	 *            the bytes to convert
+	 * @param startPos
+	 *            the position to start converting
+	 * @param length
+	 *            the length of the string to convert
+	 * 
+	 * @return the ASCII string
+	 */
+	public static final String toAsciiString(byte[] buffer, int startPos,
+			int length) {
+		char[] charArray = new char[length];
+		int readpoint = startPos;
+
+		for (int i = 0; i < length; i++) {
+			charArray[i] = (char) buffer[readpoint];
+			readpoint++;
+		}
+
+		return new String(charArray);
+	}
+
+	/**
+	 * Compares searchIn against searchForWildcard with wildcards (heavily
+	 * borrowed from strings/ctype-simple.c in the server sources)
+	 * 
+	 * @param searchIn
+	 *            the string to search in
+	 * @param searchForWildcard
+	 *            the string to search for, using the 'standard' SQL wildcard
+	 *            chars of '%' and '_'
+	 * 
+	 * @return WILD_COMPARE_MATCH_NO_WILD if matched, WILD_COMPARE_NO_MATCH if
+	 *         not matched with wildcard, WILD_COMPARE_MATCH_WITH_WILD if
+	 *         matched with wildcard
+	 */
+	public static int wildCompare(String searchIn, String searchForWildcard) {
+		if ((searchIn == null) || (searchForWildcard == null)) {
+			return WILD_COMPARE_NO_MATCH;
+		}
+
+		if (searchForWildcard.equals("%")) { //$NON-NLS-1$
+
+			return WILD_COMPARE_MATCH_WITH_WILD;
+		}
+
+		int result = WILD_COMPARE_NO_MATCH; /* Not found, using wildcards */
+
+		char wildcardMany = '%';
+		char wildcardOne = '_';
+		char wildcardEscape = '\\';
+
+		int searchForPos = 0;
+		int searchForEnd = searchForWildcard.length();
+
+		int searchInPos = 0;
+		int searchInEnd = searchIn.length();
+
+		while (searchForPos != searchForEnd) {
+			char wildstrChar = searchForWildcard.charAt(searchForPos);
+
+			while ((searchForWildcard.charAt(searchForPos) != wildcardMany)
+					&& (wildstrChar != wildcardOne)) {
+				if ((searchForWildcard.charAt(searchForPos) == wildcardEscape)
+						&& ((searchForPos + 1) != searchForEnd)) {
+					searchForPos++;
+				}
+
+				if ((searchInPos == searchInEnd)
+						|| (Character.toUpperCase(searchForWildcard
+								.charAt(searchForPos++)) != Character
+								.toUpperCase(searchIn.charAt(searchInPos++)))) {
+					return WILD_COMPARE_MATCH_WITH_WILD; /* No match */
+				}
+
+				if (searchForPos == searchForEnd) {
+					return ((searchInPos != searchInEnd) ? WILD_COMPARE_MATCH_WITH_WILD
+							: WILD_COMPARE_MATCH_NO_WILD); /*
+															 * Match if both are
+															 * at end
+															 */
+				}
+
+				result = WILD_COMPARE_MATCH_WITH_WILD; /* Found an anchor char */
+			}
+
+			if (searchForWildcard.charAt(searchForPos) == wildcardOne) {
+				do {
+					if (searchInPos == searchInEnd) { /*
+														 * Skip one char if
+														 * possible
+														 */
+
+						return (result);
+					}
+
+					searchInPos++;
+				} while ((++searchForPos < searchForEnd)
+						&& (searchForWildcard.charAt(searchForPos) == wildcardOne));
+
+				if (searchForPos == searchForEnd) {
+					break;
+				}
+			}
+
+			if (searchForWildcard.charAt(searchForPos) == wildcardMany) { /*
+																			 * Found
+																			 * w_many
+																			 */
+
+				char cmp;
+
+				searchForPos++;
+
+				/* Remove any '%' and '_' from the wild search string */
+				for (; searchForPos != searchForEnd; searchForPos++) {
+					if (searchForWildcard.charAt(searchForPos) == wildcardMany) {
+						continue;
+					}
+
+					if (searchForWildcard.charAt(searchForPos) == wildcardOne) {
+						if (searchInPos == searchInEnd) {
+							return (WILD_COMPARE_NO_MATCH);
+						}
+
+						searchInPos++;
+
+						continue;
+					}
+
+					break; /* Not a wild character */
+				}
+
+				if (searchForPos == searchForEnd) {
+					return WILD_COMPARE_MATCH_NO_WILD; /* Ok if w_many is last */
+				}
+
+				if (searchInPos == searchInEnd) {
+					return WILD_COMPARE_NO_MATCH;
+				}
+
+				if (((cmp = searchForWildcard.charAt(searchForPos)) == wildcardEscape)
+						&& ((searchForPos + 1) != searchForEnd)) {
+					cmp = searchForWildcard.charAt(++searchForPos);
+				}
+
+				searchForPos++;
+
+				do {
+					while ((searchInPos != searchInEnd)
+							&& (Character.toUpperCase(searchIn
+									.charAt(searchInPos)) != Character
+									.toUpperCase(cmp)))
+						searchInPos++;
+
+					if (searchInPos++ == searchInEnd) {
+						return WILD_COMPARE_NO_MATCH;
+					}
+
+					{
+						int tmp = wildCompare(searchIn, searchForWildcard);
+
+						if (tmp <= 0) {
+							return (tmp);
+						}
+					}
+				} while ((searchInPos != searchInEnd)
+						&& (searchForWildcard.charAt(0) != wildcardMany));
+
+				return WILD_COMPARE_NO_MATCH;
+			}
+		}
+
+		return ((searchInPos != searchInEnd) ? WILD_COMPARE_MATCH_WITH_WILD
+				: WILD_COMPARE_MATCH_NO_WILD);
+	}
+	
+	static byte[] s2b(String s, Connection conn) throws SQLException {
+		if (s == null) {
+			return null;
+		}
+		
+		if ((conn != null) && conn.getUseUnicode()) {
+			try {
+				String encoding = conn.getEncoding();
+
+				if (encoding == null) {
+					return s.getBytes();
+				}
+
+				SingleByteCharsetConverter converter = conn
+						.getCharsetConverter(encoding);
+
+				if (converter != null) {
+					return converter.toBytes(s);
+				}
+
+				return s.getBytes(encoding);
+			} catch (java.io.UnsupportedEncodingException E) {
+				return s.getBytes();
+			}
+		}
+
+		return s.getBytes();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/TimeUtil.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/TimeUtil.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/TimeUtil.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1179 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.Date;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.GregorianCalendar;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+/**
+ * Timezone conversion routines
+ * 
+ * @author Mark Matthews
+ */
+public class TimeUtil {
+	static final Map ABBREVIATED_TIMEZONES;
+
+	static final TimeZone GMT_TIMEZONE = TimeZone.getTimeZone("GMT");
+
+	static final Map TIMEZONE_MAPPINGS;
+
+	static {
+		HashMap tempMap = new HashMap();
+
+		//
+		// Windows Mappings
+		//
+		tempMap.put("Romance", "Europe/Paris");
+		tempMap.put("Romance Standard Time", "Europe/Paris");
+		tempMap.put("Warsaw", "Europe/Warsaw");
+		tempMap.put("Central Europe", "Europe/Prague");
+		tempMap.put("Central Europe Standard Time", "Europe/Prague");
+		tempMap.put("Prague Bratislava", "Europe/Prague");
+		tempMap.put("W. Central Africa Standard Time", "Africa/Luanda");
+		tempMap.put("FLE", "Europe/Helsinki");
+		tempMap.put("FLE Standard Time", "Europe/Helsinki");
+		tempMap.put("GFT", "Europe/Athens");
+		tempMap.put("GFT Standard Time", "Europe/Athens");
+		tempMap.put("GTB", "Europe/Athens");
+		tempMap.put("GTB Standard Time", "Europe/Athens");
+		tempMap.put("Israel", "Asia/Jerusalem");
+		tempMap.put("Israel Standard Time", "Asia/Jerusalem");
+		tempMap.put("Arab", "Asia/Riyadh");
+		tempMap.put("Arab Standard Time", "Asia/Riyadh");
+		tempMap.put("Arabic Standard Time", "Asia/Baghdad");
+		tempMap.put("E. Africa", "Africa/Nairobi");
+		tempMap.put("E. Africa Standard Time", "Africa/Nairobi");
+		tempMap.put("Saudi Arabia", "Asia/Riyadh");
+		tempMap.put("Saudi Arabia Standard Time", "Asia/Riyadh");
+		tempMap.put("Iran", "Asia/Tehran");
+		tempMap.put("Iran Standard Time", "Asia/Tehran");
+		tempMap.put("Afghanistan", "Asia/Kabul");
+		tempMap.put("Afghanistan Standard Time", "Asia/Kabul");
+		tempMap.put("India", "Asia/Calcutta");
+		tempMap.put("India Standard Time", "Asia/Calcutta");
+		tempMap.put("Myanmar Standard Time", "Asia/Rangoon");
+		tempMap.put("Nepal Standard Time", "Asia/Katmandu");
+		tempMap.put("Sri Lanka", "Asia/Colombo");
+		tempMap.put("Sri Lanka Standard Time", "Asia/Colombo");
+		tempMap.put("Beijing", "Asia/Shanghai");
+		tempMap.put("China", "Asia/Shanghai");
+		tempMap.put("China Standard Time", "Asia/Shanghai");
+		tempMap.put("AUS Central", "Australia/Darwin");
+		tempMap.put("AUS Central Standard Time", "Australia/Darwin");
+		tempMap.put("Cen. Australia", "Australia/Adelaide");
+		tempMap.put("Cen. Australia Standard Time", "Australia/Adelaide");
+		tempMap.put("Vladivostok", "Asia/Vladivostok");
+		tempMap.put("Vladivostok Standard Time", "Asia/Vladivostok");
+		tempMap.put("West Pacific", "Pacific/Guam");
+		tempMap.put("West Pacific Standard Time", "Pacific/Guam");
+		tempMap.put("E. South America", "America/Sao_Paulo");
+		tempMap.put("E. South America Standard Time", "America/Sao_Paulo");
+		tempMap.put("Greenland Standard Time", "America/Godthab");
+		tempMap.put("Newfoundland", "America/St_Johns");
+		tempMap.put("Newfoundland Standard Time", "America/St_Johns");
+		tempMap.put("Pacific SA", "America/Caracas");
+		tempMap.put("Pacific SA Standard Time", "America/Caracas");
+		tempMap.put("SA Western", "America/Caracas");
+		tempMap.put("SA Western Standard Time", "America/Caracas");
+		tempMap.put("SA Pacific", "America/Bogota");
+		tempMap.put("SA Pacific Standard Time", "America/Bogota");
+		tempMap.put("US Eastern", "America/Indianapolis");
+		tempMap.put("US Eastern Standard Time", "America/Indianapolis");
+		tempMap.put("Central America Standard Time", "America/Regina");
+		tempMap.put("Mexico", "America/Mexico_City");
+		tempMap.put("Mexico Standard Time", "America/Mexico_City");
+		tempMap.put("Canada Central", "America/Regina");
+		tempMap.put("Canada Central Standard Time", "America/Regina");
+		tempMap.put("US Mountain", "America/Phoenix");
+		tempMap.put("US Mountain Standard Time", "America/Phoenix");
+		tempMap.put("GMT", "Europe/London");
+		tempMap.put("GMT Standard Time", "Europe/London");
+		tempMap.put("Ekaterinburg", "Asia/Yekaterinburg");
+		tempMap.put("Ekaterinburg Standard Time", "Asia/Yekaterinburg");
+		tempMap.put("West Asia", "Asia/Karachi");
+		tempMap.put("West Asia Standard Time", "Asia/Karachi");
+		tempMap.put("Central Asia", "Asia/Dhaka");
+		tempMap.put("Central Asia Standard Time", "Asia/Dhaka");
+		tempMap.put("N. Central Asia Standard Time", "Asia/Novosibirsk");
+		tempMap.put("Bangkok", "Asia/Bangkok");
+		tempMap.put("Bangkok Standard Time", "Asia/Bangkok");
+		tempMap.put("North Asia Standard Time", "Asia/Krasnoyarsk");
+		tempMap.put("SE Asia", "Asia/Bangkok");
+		tempMap.put("SE Asia Standard Time", "Asia/Bangkok");
+		tempMap.put("North Asia East Standard Time", "Asia/Ulaanbaatar");
+		tempMap.put("Singapore", "Asia/Singapore");
+		tempMap.put("Singapore Standard Time", "Asia/Singapore");
+		tempMap.put("Taipei", "Asia/Taipei");
+		tempMap.put("Taipei Standard Time", "Asia/Taipei");
+		tempMap.put("W. Australia", "Australia/Perth");
+		tempMap.put("W. Australia Standard Time", "Australia/Perth");
+		tempMap.put("Korea", "Asia/Seoul");
+		tempMap.put("Korea Standard Time", "Asia/Seoul");
+		tempMap.put("Tokyo", "Asia/Tokyo");
+		tempMap.put("Tokyo Standard Time", "Asia/Tokyo");
+		tempMap.put("Yakutsk", "Asia/Yakutsk");
+		tempMap.put("Yakutsk Standard Time", "Asia/Yakutsk");
+		tempMap.put("Central European", "Europe/Belgrade");
+		tempMap.put("Central European Standard Time", "Europe/Belgrade");
+		tempMap.put("W. Europe", "Europe/Berlin");
+		tempMap.put("W. Europe Standard Time", "Europe/Berlin");
+		tempMap.put("Tasmania", "Australia/Hobart");
+		tempMap.put("Tasmania Standard Time", "Australia/Hobart");
+		tempMap.put("AUS Eastern", "Australia/Sydney");
+		tempMap.put("AUS Eastern Standard Time", "Australia/Sydney");
+		tempMap.put("E. Australia", "Australia/Brisbane");
+		tempMap.put("E. Australia Standard Time", "Australia/Brisbane");
+		tempMap.put("Sydney Standard Time", "Australia/Sydney");
+		tempMap.put("Central Pacific", "Pacific/Guadalcanal");
+		tempMap.put("Central Pacific Standard Time", "Pacific/Guadalcanal");
+		tempMap.put("Dateline", "Pacific/Majuro");
+		tempMap.put("Dateline Standard Time", "Pacific/Majuro");
+		tempMap.put("Fiji", "Pacific/Fiji");
+		tempMap.put("Fiji Standard Time", "Pacific/Fiji");
+		tempMap.put("Samoa", "Pacific/Apia");
+		tempMap.put("Samoa Standard Time", "Pacific/Apia");
+		tempMap.put("Hawaiian", "Pacific/Honolulu");
+		tempMap.put("Hawaiian Standard Time", "Pacific/Honolulu");
+		tempMap.put("Alaskan", "America/Anchorage");
+		tempMap.put("Alaskan Standard Time", "America/Anchorage");
+		tempMap.put("Pacific", "America/Los_Angeles");
+		tempMap.put("Pacific Standard Time", "America/Los_Angeles");
+		tempMap.put("Mexico Standard Time 2", "America/Chihuahua");
+		tempMap.put("Mountain", "America/Denver");
+		tempMap.put("Mountain Standard Time", "America/Denver");
+		tempMap.put("Central", "America/Chicago");
+		tempMap.put("Central Standard Time", "America/Chicago");
+		tempMap.put("Eastern", "America/New_York");
+		tempMap.put("Eastern Standard Time", "America/New_York");
+		tempMap.put("E. Europe", "Europe/Bucharest");
+		tempMap.put("E. Europe Standard Time", "Europe/Bucharest");
+		tempMap.put("Egypt", "Africa/Cairo");
+		tempMap.put("Egypt Standard Time", "Africa/Cairo");
+		tempMap.put("South Africa", "Africa/Harare");
+		tempMap.put("South Africa Standard Time", "Africa/Harare");
+		tempMap.put("Atlantic", "America/Halifax");
+		tempMap.put("Atlantic Standard Time", "America/Halifax");
+		tempMap.put("SA Eastern", "America/Buenos_Aires");
+		tempMap.put("SA Eastern Standard Time", "America/Buenos_Aires");
+		tempMap.put("Mid-Atlantic", "Atlantic/South_Georgia");
+		tempMap.put("Mid-Atlantic Standard Time", "Atlantic/South_Georgia");
+		tempMap.put("Azores", "Atlantic/Azores");
+		tempMap.put("Azores Standard Time", "Atlantic/Azores");
+		tempMap.put("Cape Verde Standard Time", "Atlantic/Cape_Verde");
+		tempMap.put("Russian", "Europe/Moscow");
+		tempMap.put("Russian Standard Time", "Europe/Moscow");
+		tempMap.put("New Zealand", "Pacific/Auckland");
+		tempMap.put("New Zealand Standard Time", "Pacific/Auckland");
+		tempMap.put("Tonga Standard Time", "Pacific/Tongatapu");
+		tempMap.put("Arabian", "Asia/Muscat");
+		tempMap.put("Arabian Standard Time", "Asia/Muscat");
+		tempMap.put("Caucasus", "Asia/Tbilisi");
+		tempMap.put("Caucasus Standard Time", "Asia/Tbilisi");
+		tempMap.put("GMT Standard Time", "GMT");
+		tempMap.put("Greenwich", "GMT");
+		tempMap.put("Greenwich Standard Time", "GMT");
+		tempMap.put("UTC", "GMT");
+
+		TIMEZONE_MAPPINGS = Collections.unmodifiableMap(tempMap);
+
+		//
+		// Handle abbreviated mappings
+		//
+		tempMap = new HashMap();
+
+		tempMap.put("ACST", new String[] { "America/Porto_Acre" });
+		tempMap.put("ACT", new String[] { "America/Porto_Acre" });
+		tempMap.put("ADDT", new String[] { "America/Pangnirtung" });
+		tempMap.put("ADMT", new String[] { "Africa/Asmera",
+				"Africa/Addis_Ababa" });
+		tempMap.put("ADT", new String[] { "Atlantic/Bermuda", "Asia/Baghdad",
+				"America/Thule", "America/Goose_Bay", "America/Halifax",
+				"America/Glace_Bay", "America/Pangnirtung", "America/Barbados",
+				"America/Martinique" });
+		tempMap.put("AFT", new String[] { "Asia/Kabul" });
+		tempMap.put("AHDT", new String[] { "America/Anchorage" });
+		tempMap.put("AHST", new String[] { "America/Anchorage" });
+		tempMap.put("AHWT", new String[] { "America/Anchorage" });
+		tempMap.put("AKDT", new String[] { "America/Juneau", "America/Yakutat",
+				"America/Anchorage", "America/Nome" });
+		tempMap.put("AKST", new String[] { "Asia/Aqtobe", "America/Juneau",
+				"America/Yakutat", "America/Anchorage", "America/Nome" });
+		tempMap.put("AKT", new String[] { "Asia/Aqtobe" });
+		tempMap.put("AKTST", new String[] { "Asia/Aqtobe" });
+		tempMap.put("AKWT", new String[] { "America/Juneau", "America/Yakutat",
+				"America/Anchorage", "America/Nome" });
+		tempMap.put("ALMST", new String[] { "Asia/Almaty" });
+		tempMap.put("ALMT", new String[] { "Asia/Almaty" });
+		tempMap.put("AMST", new String[] { "Asia/Yerevan", "America/Cuiaba",
+				"America/Porto_Velho", "America/Boa_Vista", "America/Manaus" });
+		tempMap.put("AMT", new String[] { "Europe/Athens", "Europe/Amsterdam",
+				"Asia/Yerevan", "Africa/Asmera", "America/Cuiaba",
+				"America/Porto_Velho", "America/Boa_Vista", "America/Manaus",
+				"America/Asuncion" });
+		tempMap.put("ANAMT", new String[] { "Asia/Anadyr" });
+		tempMap.put("ANAST", new String[] { "Asia/Anadyr" });
+		tempMap.put("ANAT", new String[] { "Asia/Anadyr" });
+		tempMap.put("ANT", new String[] { "America/Aruba", "America/Curacao" });
+		tempMap.put("AQTST", new String[] { "Asia/Aqtobe", "Asia/Aqtau" });
+		tempMap.put("AQTT", new String[] { "Asia/Aqtobe", "Asia/Aqtau" });
+		tempMap.put("ARST", new String[] { "Antarctica/Palmer",
+				"America/Buenos_Aires", "America/Rosario", "America/Cordoba",
+				"America/Jujuy", "America/Catamarca", "America/Mendoza" });
+		tempMap.put("ART", new String[] { "Antarctica/Palmer",
+				"America/Buenos_Aires", "America/Rosario", "America/Cordoba",
+				"America/Jujuy", "America/Catamarca", "America/Mendoza" });
+		tempMap.put("ASHST", new String[] { "Asia/Ashkhabad" });
+		tempMap.put("ASHT", new String[] { "Asia/Ashkhabad" });
+		tempMap.put("AST", new String[] { "Atlantic/Bermuda", "Asia/Bahrain",
+				"Asia/Baghdad", "Asia/Kuwait", "Asia/Qatar", "Asia/Riyadh",
+				"Asia/Aden", "America/Thule", "America/Goose_Bay",
+				"America/Halifax", "America/Glace_Bay", "America/Pangnirtung",
+				"America/Anguilla", "America/Antigua", "America/Barbados",
+				"America/Dominica", "America/Santo_Domingo", "America/Grenada",
+				"America/Guadeloupe", "America/Martinique",
+				"America/Montserrat", "America/Puerto_Rico",
+				"America/St_Kitts", "America/St_Lucia", "America/Miquelon",
+				"America/St_Vincent", "America/Tortola", "America/St_Thomas",
+				"America/Aruba", "America/Curacao", "America/Port_of_Spain" });
+		tempMap.put("AWT", new String[] { "America/Puerto_Rico" });
+		tempMap.put("AZOST", new String[] { "Atlantic/Azores" });
+		tempMap.put("AZOT", new String[] { "Atlantic/Azores" });
+		tempMap.put("AZST", new String[] { "Asia/Baku" });
+		tempMap.put("AZT", new String[] { "Asia/Baku" });
+		tempMap.put("BAKST", new String[] { "Asia/Baku" });
+		tempMap.put("BAKT", new String[] { "Asia/Baku" });
+		tempMap.put("BDT", new String[] { "Asia/Dacca", "America/Nome",
+				"America/Adak" });
+		tempMap.put("BEAT", new String[] { "Africa/Nairobi",
+				"Africa/Mogadishu", "Africa/Kampala" });
+		tempMap.put("BEAUT", new String[] { "Africa/Nairobi",
+				"Africa/Dar_es_Salaam", "Africa/Kampala" });
+		tempMap.put("BMT", new String[] { "Europe/Brussels", "Europe/Chisinau",
+				"Europe/Tiraspol", "Europe/Bucharest", "Europe/Zurich",
+				"Asia/Baghdad", "Asia/Bangkok", "Africa/Banjul",
+				"America/Barbados", "America/Bogota" });
+		tempMap.put("BNT", new String[] { "Asia/Brunei" });
+		tempMap.put("BORT",
+				new String[] { "Asia/Ujung_Pandang", "Asia/Kuching" });
+		tempMap.put("BOST", new String[] { "America/La_Paz" });
+		tempMap.put("BOT", new String[] { "America/La_Paz" });
+		tempMap.put("BRST", new String[] { "America/Belem",
+				"America/Fortaleza", "America/Araguaina", "America/Maceio",
+				"America/Sao_Paulo" });
+		tempMap.put("BRT", new String[] { "America/Belem", "America/Fortaleza",
+				"America/Araguaina", "America/Maceio", "America/Sao_Paulo" });
+		tempMap.put("BST", new String[] { "Europe/London", "Europe/Belfast",
+				"Europe/Dublin", "Europe/Gibraltar", "Pacific/Pago_Pago",
+				"Pacific/Midway", "America/Nome", "America/Adak" });
+		tempMap.put("BTT", new String[] { "Asia/Thimbu" });
+		tempMap.put("BURT", new String[] { "Asia/Dacca", "Asia/Rangoon",
+				"Asia/Calcutta" });
+		tempMap.put("BWT", new String[] { "America/Nome", "America/Adak" });
+		tempMap.put("CANT", new String[] { "Atlantic/Canary" });
+		tempMap.put("CAST",
+				new String[] { "Africa/Gaborone", "Africa/Khartoum" });
+		tempMap.put("CAT", new String[] { "Africa/Gaborone",
+				"Africa/Bujumbura", "Africa/Lubumbashi", "Africa/Blantyre",
+				"Africa/Maputo", "Africa/Windhoek", "Africa/Kigali",
+				"Africa/Khartoum", "Africa/Lusaka", "Africa/Harare",
+				"America/Anchorage" });
+		tempMap.put("CCT", new String[] { "Indian/Cocos" });
+		tempMap.put("CDDT", new String[] { "America/Rankin_Inlet" });
+		tempMap.put("CDT", new String[] { "Asia/Harbin", "Asia/Shanghai",
+				"Asia/Chungking", "Asia/Urumqi", "Asia/Kashgar", "Asia/Taipei",
+				"Asia/Macao", "America/Chicago", "America/Indianapolis",
+				"America/Indiana/Marengo", "America/Indiana/Knox",
+				"America/Indiana/Vevay", "America/Louisville",
+				"America/Menominee", "America/Rainy_River", "America/Winnipeg",
+				"America/Pangnirtung", "America/Iqaluit",
+				"America/Rankin_Inlet", "America/Cambridge_Bay",
+				"America/Cancun", "America/Mexico_City", "America/Chihuahua",
+				"America/Belize", "America/Costa_Rica", "America/Havana",
+				"America/El_Salvador", "America/Guatemala",
+				"America/Tegucigalpa", "America/Managua" });
+		tempMap.put("CEST", new String[] { "Europe/Tirane", "Europe/Andorra",
+				"Europe/Vienna", "Europe/Minsk", "Europe/Brussels",
+				"Europe/Sofia", "Europe/Prague", "Europe/Copenhagen",
+				"Europe/Tallinn", "Europe/Berlin", "Europe/Gibraltar",
+				"Europe/Athens", "Europe/Budapest", "Europe/Rome",
+				"Europe/Riga", "Europe/Vaduz", "Europe/Vilnius",
+				"Europe/Luxembourg", "Europe/Malta", "Europe/Chisinau",
+				"Europe/Tiraspol", "Europe/Monaco", "Europe/Amsterdam",
+				"Europe/Oslo", "Europe/Warsaw", "Europe/Lisbon",
+				"Europe/Kaliningrad", "Europe/Madrid", "Europe/Stockholm",
+				"Europe/Zurich", "Europe/Kiev", "Europe/Uzhgorod",
+				"Europe/Zaporozhye", "Europe/Simferopol", "Europe/Belgrade",
+				"Africa/Algiers", "Africa/Tripoli", "Africa/Tunis",
+				"Africa/Ceuta" });
+		tempMap.put("CET", new String[] { "Europe/Tirane", "Europe/Andorra",
+				"Europe/Vienna", "Europe/Minsk", "Europe/Brussels",
+				"Europe/Sofia", "Europe/Prague", "Europe/Copenhagen",
+				"Europe/Tallinn", "Europe/Berlin", "Europe/Gibraltar",
+				"Europe/Athens", "Europe/Budapest", "Europe/Rome",
+				"Europe/Riga", "Europe/Vaduz", "Europe/Vilnius",
+				"Europe/Luxembourg", "Europe/Malta", "Europe/Chisinau",
+				"Europe/Tiraspol", "Europe/Monaco", "Europe/Amsterdam",
+				"Europe/Oslo", "Europe/Warsaw", "Europe/Lisbon",
+				"Europe/Kaliningrad", "Europe/Madrid", "Europe/Stockholm",
+				"Europe/Zurich", "Europe/Kiev", "Europe/Uzhgorod",
+				"Europe/Zaporozhye", "Europe/Simferopol", "Europe/Belgrade",
+				"Africa/Algiers", "Africa/Tripoli", "Africa/Casablanca",
+				"Africa/Tunis", "Africa/Ceuta" });
+		tempMap.put("CGST", new String[] { "America/Scoresbysund" });
+		tempMap.put("CGT", new String[] { "America/Scoresbysund" });
+		tempMap.put("CHDT", new String[] { "America/Belize" });
+		tempMap.put("CHUT", new String[] { "Asia/Chungking" });
+		tempMap.put("CJT", new String[] { "Asia/Tokyo" });
+		tempMap.put("CKHST", new String[] { "Pacific/Rarotonga" });
+		tempMap.put("CKT", new String[] { "Pacific/Rarotonga" });
+		tempMap.put("CLST", new String[] { "Antarctica/Palmer",
+				"America/Santiago" });
+		tempMap.put("CLT", new String[] { "Antarctica/Palmer",
+				"America/Santiago" });
+		tempMap.put("CMT", new String[] { "Europe/Copenhagen",
+				"Europe/Chisinau", "Europe/Tiraspol", "America/St_Lucia",
+				"America/Buenos_Aires", "America/Rosario", "America/Cordoba",
+				"America/Jujuy", "America/Catamarca", "America/Mendoza",
+				"America/Caracas" });
+		tempMap.put("COST", new String[] { "America/Bogota" });
+		tempMap.put("COT", new String[] { "America/Bogota" });
+		tempMap
+				.put("CST", new String[] { "Asia/Harbin", "Asia/Shanghai",
+						"Asia/Chungking", "Asia/Urumqi", "Asia/Kashgar",
+						"Asia/Taipei", "Asia/Macao", "Asia/Jayapura",
+						"Australia/Darwin", "Australia/Adelaide",
+						"Australia/Broken_Hill", "America/Chicago",
+						"America/Indianapolis", "America/Indiana/Marengo",
+						"America/Indiana/Knox", "America/Indiana/Vevay",
+						"America/Louisville", "America/Detroit",
+						"America/Menominee", "America/Rainy_River",
+						"America/Winnipeg", "America/Regina",
+						"America/Swift_Current", "America/Pangnirtung",
+						"America/Iqaluit", "America/Rankin_Inlet",
+						"America/Cambridge_Bay", "America/Cancun",
+						"America/Mexico_City", "America/Chihuahua",
+						"America/Hermosillo", "America/Mazatlan",
+						"America/Belize", "America/Costa_Rica",
+						"America/Havana", "America/El_Salvador",
+						"America/Guatemala", "America/Tegucigalpa",
+						"America/Managua" });
+		tempMap.put("CUT", new String[] { "Europe/Zaporozhye" });
+		tempMap.put("CVST", new String[] { "Atlantic/Cape_Verde" });
+		tempMap.put("CVT", new String[] { "Atlantic/Cape_Verde" });
+		tempMap.put("CWT", new String[] { "America/Chicago",
+				"America/Indianapolis", "America/Indiana/Marengo",
+				"America/Indiana/Knox", "America/Indiana/Vevay",
+				"America/Louisville", "America/Menominee" });
+		tempMap.put("CXT", new String[] { "Indian/Christmas" });
+		tempMap.put("DACT", new String[] { "Asia/Dacca" });
+		tempMap.put("DAVT", new String[] { "Antarctica/Davis" });
+		tempMap.put("DDUT", new String[] { "Antarctica/DumontDUrville" });
+		tempMap.put("DFT", new String[] { "Europe/Oslo", "Europe/Paris" });
+		tempMap.put("DMT", new String[] { "Europe/Belfast", "Europe/Dublin" });
+		tempMap.put("DUSST", new String[] { "Asia/Dushanbe" });
+		tempMap.put("DUST", new String[] { "Asia/Dushanbe" });
+		tempMap.put("EASST", new String[] { "Pacific/Easter" });
+		tempMap.put("EAST", new String[] { "Indian/Antananarivo",
+				"Pacific/Easter" });
+		tempMap.put("EAT", new String[] { "Indian/Comoro",
+				"Indian/Antananarivo", "Indian/Mayotte", "Africa/Djibouti",
+				"Africa/Asmera", "Africa/Addis_Ababa", "Africa/Nairobi",
+				"Africa/Mogadishu", "Africa/Khartoum", "Africa/Dar_es_Salaam",
+				"Africa/Kampala" });
+		tempMap.put("ECT", new String[] { "Pacific/Galapagos",
+				"America/Guayaquil" });
+		tempMap.put("EDDT", new String[] { "America/Iqaluit" });
+		tempMap.put("EDT", new String[] { "America/New_York",
+				"America/Indianapolis", "America/Indiana/Marengo",
+				"America/Indiana/Vevay", "America/Louisville",
+				"America/Detroit", "America/Montreal", "America/Thunder_Bay",
+				"America/Nipigon", "America/Pangnirtung", "America/Iqaluit",
+				"America/Cancun", "America/Nassau", "America/Santo_Domingo",
+				"America/Port-au-Prince", "America/Jamaica",
+				"America/Grand_Turk" });
+		tempMap.put("EEMT", new String[] { "Europe/Minsk", "Europe/Chisinau",
+				"Europe/Tiraspol", "Europe/Kaliningrad", "Europe/Moscow" });
+		tempMap.put("EEST", new String[] { "Europe/Minsk", "Europe/Sofia",
+				"Europe/Tallinn", "Europe/Helsinki", "Europe/Athens",
+				"Europe/Riga", "Europe/Vilnius", "Europe/Chisinau",
+				"Europe/Tiraspol", "Europe/Warsaw", "Europe/Bucharest",
+				"Europe/Kaliningrad", "Europe/Moscow", "Europe/Istanbul",
+				"Europe/Kiev", "Europe/Uzhgorod", "Europe/Zaporozhye",
+				"Asia/Nicosia", "Asia/Amman", "Asia/Beirut", "Asia/Gaza",
+				"Asia/Damascus", "Africa/Cairo" });
+		tempMap.put("EET", new String[] { "Europe/Minsk", "Europe/Sofia",
+				"Europe/Tallinn", "Europe/Helsinki", "Europe/Athens",
+				"Europe/Riga", "Europe/Vilnius", "Europe/Chisinau",
+				"Europe/Tiraspol", "Europe/Warsaw", "Europe/Bucharest",
+				"Europe/Kaliningrad", "Europe/Moscow", "Europe/Istanbul",
+				"Europe/Kiev", "Europe/Uzhgorod", "Europe/Zaporozhye",
+				"Europe/Simferopol", "Asia/Nicosia", "Asia/Amman",
+				"Asia/Beirut", "Asia/Gaza", "Asia/Damascus", "Africa/Cairo",
+				"Africa/Tripoli" });
+		tempMap.put("EGST", new String[] { "America/Scoresbysund" });
+		tempMap.put("EGT", new String[] { "Atlantic/Jan_Mayen",
+				"America/Scoresbysund" });
+		tempMap.put("EHDT", new String[] { "America/Santo_Domingo" });
+		tempMap.put("EST", new String[] { "Australia/Brisbane",
+				"Australia/Lindeman", "Australia/Hobart",
+				"Australia/Melbourne", "Australia/Sydney",
+				"Australia/Broken_Hill", "Australia/Lord_Howe",
+				"America/New_York", "America/Chicago", "America/Indianapolis",
+				"America/Indiana/Marengo", "America/Indiana/Knox",
+				"America/Indiana/Vevay", "America/Louisville",
+				"America/Detroit", "America/Menominee", "America/Montreal",
+				"America/Thunder_Bay", "America/Nipigon",
+				"America/Pangnirtung", "America/Iqaluit", "America/Cancun",
+				"America/Antigua", "America/Nassau", "America/Cayman",
+				"America/Santo_Domingo", "America/Port-au-Prince",
+				"America/Jamaica", "America/Managua", "America/Panama",
+				"America/Grand_Turk" });
+		tempMap.put("EWT", new String[] { "America/New_York",
+				"America/Indianapolis", "America/Indiana/Marengo",
+				"America/Indiana/Vevay", "America/Louisville",
+				"America/Detroit", "America/Jamaica" });
+		tempMap.put("FFMT", new String[] { "America/Martinique" });
+		tempMap.put("FJST", new String[] { "Pacific/Fiji" });
+		tempMap.put("FJT", new String[] { "Pacific/Fiji" });
+		tempMap.put("FKST", new String[] { "Atlantic/Stanley" });
+		tempMap.put("FKT", new String[] { "Atlantic/Stanley" });
+		tempMap.put("FMT",
+				new String[] { "Atlantic/Madeira", "Africa/Freetown" });
+		tempMap.put("FNST", new String[] { "America/Noronha" });
+		tempMap.put("FNT", new String[] { "America/Noronha" });
+		tempMap.put("FRUST", new String[] { "Asia/Bishkek" });
+		tempMap.put("FRUT", new String[] { "Asia/Bishkek" });
+		tempMap.put("GALT", new String[] { "Pacific/Galapagos" });
+		tempMap.put("GAMT", new String[] { "Pacific/Gambier" });
+		tempMap.put("GBGT", new String[] { "America/Guyana" });
+		tempMap.put("GEST", new String[] { "Asia/Tbilisi" });
+		tempMap.put("GET", new String[] { "Asia/Tbilisi" });
+		tempMap.put("GFT", new String[] { "America/Cayenne" });
+		tempMap.put("GHST", new String[] { "Africa/Accra" });
+		tempMap.put("GILT", new String[] { "Pacific/Tarawa" });
+		tempMap.put("GMT", new String[] { "Atlantic/St_Helena",
+				"Atlantic/Reykjavik", "Europe/London", "Europe/Belfast",
+				"Europe/Dublin", "Europe/Gibraltar", "Africa/Porto-Novo",
+				"Africa/Ouagadougou", "Africa/Abidjan", "Africa/Malabo",
+				"Africa/Banjul", "Africa/Accra", "Africa/Conakry",
+				"Africa/Bissau", "Africa/Monrovia", "Africa/Bamako",
+				"Africa/Timbuktu", "Africa/Nouakchott", "Africa/Niamey",
+				"Africa/Sao_Tome", "Africa/Dakar", "Africa/Freetown",
+				"Africa/Lome" });
+		tempMap.put("GST", new String[] { "Atlantic/South_Georgia",
+				"Asia/Bahrain", "Asia/Muscat", "Asia/Qatar", "Asia/Dubai",
+				"Pacific/Guam" });
+		tempMap.put("GYT", new String[] { "America/Guyana" });
+		tempMap.put("HADT", new String[] { "America/Adak" });
+		tempMap.put("HART", new String[] { "Asia/Harbin" });
+		tempMap.put("HAST", new String[] { "America/Adak" });
+		tempMap.put("HAWT", new String[] { "America/Adak" });
+		tempMap.put("HDT", new String[] { "Pacific/Honolulu" });
+		tempMap.put("HKST", new String[] { "Asia/Hong_Kong" });
+		tempMap.put("HKT", new String[] { "Asia/Hong_Kong" });
+		tempMap.put("HMT", new String[] { "Atlantic/Azores", "Europe/Helsinki",
+				"Asia/Dacca", "Asia/Calcutta", "America/Havana" });
+		tempMap.put("HOVST", new String[] { "Asia/Hovd" });
+		tempMap.put("HOVT", new String[] { "Asia/Hovd" });
+		tempMap.put("HST", new String[] { "Pacific/Johnston",
+				"Pacific/Honolulu" });
+		tempMap.put("HWT", new String[] { "Pacific/Honolulu" });
+		tempMap.put("ICT", new String[] { "Asia/Phnom_Penh", "Asia/Vientiane",
+				"Asia/Bangkok", "Asia/Saigon" });
+		tempMap.put("IDDT", new String[] { "Asia/Jerusalem", "Asia/Gaza" });
+		tempMap.put("IDT", new String[] { "Asia/Jerusalem", "Asia/Gaza" });
+		tempMap.put("IHST", new String[] { "Asia/Colombo" });
+		tempMap.put("IMT", new String[] { "Europe/Sofia", "Europe/Istanbul",
+				"Asia/Irkutsk" });
+		tempMap.put("IOT", new String[] { "Indian/Chagos" });
+		tempMap.put("IRKMT", new String[] { "Asia/Irkutsk" });
+		tempMap.put("IRKST", new String[] { "Asia/Irkutsk" });
+		tempMap.put("IRKT", new String[] { "Asia/Irkutsk" });
+		tempMap.put("IRST", new String[] { "Asia/Tehran" });
+		tempMap.put("IRT", new String[] { "Asia/Tehran" });
+		tempMap.put("ISST", new String[] { "Atlantic/Reykjavik" });
+		tempMap.put("IST", new String[] { "Atlantic/Reykjavik",
+				"Europe/Belfast", "Europe/Dublin", "Asia/Dacca", "Asia/Thimbu",
+				"Asia/Calcutta", "Asia/Jerusalem", "Asia/Katmandu",
+				"Asia/Karachi", "Asia/Gaza", "Asia/Colombo" });
+		tempMap.put("JAYT", new String[] { "Asia/Jayapura" });
+		tempMap.put("JMT", new String[] { "Atlantic/St_Helena",
+				"Asia/Jerusalem" });
+		tempMap.put("JST", new String[] { "Asia/Rangoon", "Asia/Dili",
+				"Asia/Ujung_Pandang", "Asia/Tokyo", "Asia/Kuala_Lumpur",
+				"Asia/Kuching", "Asia/Manila", "Asia/Singapore",
+				"Pacific/Nauru" });
+		tempMap.put("KART", new String[] { "Asia/Karachi" });
+		tempMap.put("KAST", new String[] { "Asia/Kashgar" });
+		tempMap.put("KDT", new String[] { "Asia/Seoul" });
+		tempMap.put("KGST", new String[] { "Asia/Bishkek" });
+		tempMap.put("KGT", new String[] { "Asia/Bishkek" });
+		tempMap.put("KMT", new String[] { "Europe/Vilnius", "Europe/Kiev",
+				"America/Cayman", "America/Jamaica", "America/St_Vincent",
+				"America/Grand_Turk" });
+		tempMap.put("KOST", new String[] { "Pacific/Kosrae" });
+		tempMap.put("KRAMT", new String[] { "Asia/Krasnoyarsk" });
+		tempMap.put("KRAST", new String[] { "Asia/Krasnoyarsk" });
+		tempMap.put("KRAT", new String[] { "Asia/Krasnoyarsk" });
+		tempMap.put("KST", new String[] { "Asia/Seoul", "Asia/Pyongyang" });
+		tempMap.put("KUYMT", new String[] { "Europe/Samara" });
+		tempMap.put("KUYST", new String[] { "Europe/Samara" });
+		tempMap.put("KUYT", new String[] { "Europe/Samara" });
+		tempMap.put("KWAT", new String[] { "Pacific/Kwajalein" });
+		tempMap.put("LHST", new String[] { "Australia/Lord_Howe" });
+		tempMap.put("LINT", new String[] { "Pacific/Kiritimati" });
+		tempMap.put("LKT", new String[] { "Asia/Colombo" });
+		tempMap.put("LPMT", new String[] { "America/La_Paz" });
+		tempMap.put("LRT", new String[] { "Africa/Monrovia" });
+		tempMap.put("LST", new String[] { "Europe/Riga" });
+		tempMap.put("M", new String[] { "Europe/Moscow" });
+		tempMap.put("MADST", new String[] { "Atlantic/Madeira" });
+		tempMap.put("MAGMT", new String[] { "Asia/Magadan" });
+		tempMap.put("MAGST", new String[] { "Asia/Magadan" });
+		tempMap.put("MAGT", new String[] { "Asia/Magadan" });
+		tempMap.put("MALT", new String[] { "Asia/Kuala_Lumpur",
+				"Asia/Singapore" });
+		tempMap.put("MART", new String[] { "Pacific/Marquesas" });
+		tempMap.put("MAWT", new String[] { "Antarctica/Mawson" });
+		tempMap.put("MDDT", new String[] { "America/Cambridge_Bay",
+				"America/Yellowknife", "America/Inuvik" });
+		tempMap.put("MDST", new String[] { "Europe/Moscow" });
+		tempMap.put("MDT", new String[] { "America/Denver", "America/Phoenix",
+				"America/Boise", "America/Regina", "America/Swift_Current",
+				"America/Edmonton", "America/Cambridge_Bay",
+				"America/Yellowknife", "America/Inuvik", "America/Chihuahua",
+				"America/Hermosillo", "America/Mazatlan" });
+		tempMap.put("MET", new String[] { "Europe/Tirane", "Europe/Andorra",
+				"Europe/Vienna", "Europe/Minsk", "Europe/Brussels",
+				"Europe/Sofia", "Europe/Prague", "Europe/Copenhagen",
+				"Europe/Tallinn", "Europe/Berlin", "Europe/Gibraltar",
+				"Europe/Athens", "Europe/Budapest", "Europe/Rome",
+				"Europe/Riga", "Europe/Vaduz", "Europe/Vilnius",
+				"Europe/Luxembourg", "Europe/Malta", "Europe/Chisinau",
+				"Europe/Tiraspol", "Europe/Monaco", "Europe/Amsterdam",
+				"Europe/Oslo", "Europe/Warsaw", "Europe/Lisbon",
+				"Europe/Kaliningrad", "Europe/Madrid", "Europe/Stockholm",
+				"Europe/Zurich", "Europe/Kiev", "Europe/Uzhgorod",
+				"Europe/Zaporozhye", "Europe/Simferopol", "Europe/Belgrade",
+				"Africa/Algiers", "Africa/Tripoli", "Africa/Casablanca",
+				"Africa/Tunis", "Africa/Ceuta" });
+		tempMap.put("MHT",
+				new String[] { "Pacific/Majuro", "Pacific/Kwajalein" });
+		tempMap.put("MMT", new String[] { "Indian/Maldives", "Europe/Minsk",
+				"Europe/Moscow", "Asia/Rangoon", "Asia/Ujung_Pandang",
+				"Asia/Colombo", "Pacific/Easter", "Africa/Monrovia",
+				"America/Managua", "America/Montevideo" });
+		tempMap.put("MOST", new String[] { "Asia/Macao" });
+		tempMap.put("MOT", new String[] { "Asia/Macao" });
+		tempMap.put("MPT", new String[] { "Pacific/Saipan" });
+		tempMap.put("MSK", new String[] { "Europe/Minsk", "Europe/Tallinn",
+				"Europe/Riga", "Europe/Vilnius", "Europe/Chisinau",
+				"Europe/Kiev", "Europe/Uzhgorod", "Europe/Zaporozhye",
+				"Europe/Simferopol" });
+		tempMap.put("MST", new String[] { "Europe/Moscow", "America/Denver",
+				"America/Phoenix", "America/Boise", "America/Regina",
+				"America/Swift_Current", "America/Edmonton",
+				"America/Dawson_Creek", "America/Cambridge_Bay",
+				"America/Yellowknife", "America/Inuvik", "America/Mexico_City",
+				"America/Chihuahua", "America/Hermosillo", "America/Mazatlan",
+				"America/Tijuana" });
+		tempMap.put("MUT", new String[] { "Indian/Mauritius" });
+		tempMap.put("MVT", new String[] { "Indian/Maldives" });
+		tempMap.put("MWT", new String[] { "America/Denver", "America/Phoenix",
+				"America/Boise" });
+		tempMap
+				.put("MYT",
+						new String[] { "Asia/Kuala_Lumpur", "Asia/Kuching" });
+		tempMap.put("NCST", new String[] { "Pacific/Noumea" });
+		tempMap.put("NCT", new String[] { "Pacific/Noumea" });
+		tempMap.put("NDT", new String[] { "America/Nome", "America/Adak",
+				"America/St_Johns", "America/Goose_Bay" });
+		tempMap.put("NEGT", new String[] { "America/Paramaribo" });
+		tempMap.put("NFT", new String[] { "Europe/Paris", "Europe/Oslo",
+				"Pacific/Norfolk" });
+		tempMap.put("NMT", new String[] { "Pacific/Norfolk" });
+		tempMap.put("NOVMT", new String[] { "Asia/Novosibirsk" });
+		tempMap.put("NOVST", new String[] { "Asia/Novosibirsk" });
+		tempMap.put("NOVT", new String[] { "Asia/Novosibirsk" });
+		tempMap.put("NPT", new String[] { "Asia/Katmandu" });
+		tempMap.put("NRT", new String[] { "Pacific/Nauru" });
+		tempMap.put("NST", new String[] { "Europe/Amsterdam",
+				"Pacific/Pago_Pago", "Pacific/Midway", "America/Nome",
+				"America/Adak", "America/St_Johns", "America/Goose_Bay" });
+		tempMap.put("NUT", new String[] { "Pacific/Niue" });
+		tempMap.put("NWT", new String[] { "America/Nome", "America/Adak" });
+		tempMap.put("NZDT", new String[] { "Antarctica/McMurdo" });
+		tempMap.put("NZHDT", new String[] { "Pacific/Auckland" });
+		tempMap.put("NZST", new String[] { "Antarctica/McMurdo",
+				"Pacific/Auckland" });
+		tempMap.put("OMSMT", new String[] { "Asia/Omsk" });
+		tempMap.put("OMSST", new String[] { "Asia/Omsk" });
+		tempMap.put("OMST", new String[] { "Asia/Omsk" });
+		tempMap.put("PDDT", new String[] { "America/Inuvik",
+				"America/Whitehorse", "America/Dawson" });
+		tempMap.put("PDT", new String[] { "America/Los_Angeles",
+				"America/Juneau", "America/Boise", "America/Vancouver",
+				"America/Dawson_Creek", "America/Inuvik", "America/Whitehorse",
+				"America/Dawson", "America/Tijuana" });
+		tempMap.put("PEST", new String[] { "America/Lima" });
+		tempMap.put("PET", new String[] { "America/Lima" });
+		tempMap.put("PETMT", new String[] { "Asia/Kamchatka" });
+		tempMap.put("PETST", new String[] { "Asia/Kamchatka" });
+		tempMap.put("PETT", new String[] { "Asia/Kamchatka" });
+		tempMap.put("PGT", new String[] { "Pacific/Port_Moresby" });
+		tempMap.put("PHOT", new String[] { "Pacific/Enderbury" });
+		tempMap.put("PHST", new String[] { "Asia/Manila" });
+		tempMap.put("PHT", new String[] { "Asia/Manila" });
+		tempMap.put("PKT", new String[] { "Asia/Karachi" });
+		tempMap.put("PMDT", new String[] { "America/Miquelon" });
+		tempMap.put("PMMT", new String[] { "Pacific/Port_Moresby" });
+		tempMap.put("PMST", new String[] { "America/Miquelon" });
+		tempMap.put("PMT", new String[] { "Antarctica/DumontDUrville",
+				"Europe/Prague", "Europe/Paris", "Europe/Monaco",
+				"Africa/Algiers", "Africa/Tunis", "America/Panama",
+				"America/Paramaribo" });
+		tempMap.put("PNT", new String[] { "Pacific/Pitcairn" });
+		tempMap.put("PONT", new String[] { "Pacific/Ponape" });
+		tempMap.put("PPMT", new String[] { "America/Port-au-Prince" });
+		tempMap.put("PST", new String[] { "Pacific/Pitcairn",
+				"America/Los_Angeles", "America/Juneau", "America/Boise",
+				"America/Vancouver", "America/Dawson_Creek", "America/Inuvik",
+				"America/Whitehorse", "America/Dawson", "America/Hermosillo",
+				"America/Mazatlan", "America/Tijuana" });
+		tempMap.put("PWT", new String[] { "Pacific/Palau",
+				"America/Los_Angeles", "America/Juneau", "America/Boise",
+				"America/Tijuana" });
+		tempMap.put("PYST", new String[] { "America/Asuncion" });
+		tempMap.put("PYT", new String[] { "America/Asuncion" });
+		tempMap.put("QMT", new String[] { "America/Guayaquil" });
+		tempMap.put("RET", new String[] { "Indian/Reunion" });
+		tempMap.put("RMT", new String[] { "Atlantic/Reykjavik", "Europe/Rome",
+				"Europe/Riga", "Asia/Rangoon" });
+		tempMap.put("S", new String[] { "Europe/Moscow" });
+		tempMap.put("SAMMT", new String[] { "Europe/Samara" });
+		tempMap
+				.put("SAMST",
+						new String[] { "Europe/Samara", "Asia/Samarkand" });
+		tempMap.put("SAMT", new String[] { "Europe/Samara", "Asia/Samarkand",
+				"Pacific/Pago_Pago", "Pacific/Apia" });
+		tempMap.put("SAST", new String[] { "Africa/Maseru", "Africa/Windhoek",
+				"Africa/Johannesburg", "Africa/Mbabane" });
+		tempMap.put("SBT", new String[] { "Pacific/Guadalcanal" });
+		tempMap.put("SCT", new String[] { "Indian/Mahe" });
+		tempMap.put("SDMT", new String[] { "America/Santo_Domingo" });
+		tempMap.put("SGT", new String[] { "Asia/Singapore" });
+		tempMap.put("SHEST", new String[] { "Asia/Aqtau" });
+		tempMap.put("SHET", new String[] { "Asia/Aqtau" });
+		tempMap.put("SJMT", new String[] { "America/Costa_Rica" });
+		tempMap.put("SLST", new String[] { "Africa/Freetown" });
+		tempMap.put("SMT", new String[] { "Atlantic/Stanley",
+				"Europe/Stockholm", "Europe/Simferopol", "Asia/Phnom_Penh",
+				"Asia/Vientiane", "Asia/Kuala_Lumpur", "Asia/Singapore",
+				"Asia/Saigon", "America/Santiago" });
+		tempMap.put("SRT", new String[] { "America/Paramaribo" });
+		tempMap.put("SST",
+				new String[] { "Pacific/Pago_Pago", "Pacific/Midway" });
+		tempMap.put("SVEMT", new String[] { "Asia/Yekaterinburg" });
+		tempMap.put("SVEST", new String[] { "Asia/Yekaterinburg" });
+		tempMap.put("SVET", new String[] { "Asia/Yekaterinburg" });
+		tempMap.put("SWAT", new String[] { "Africa/Windhoek" });
+		tempMap.put("SYOT", new String[] { "Antarctica/Syowa" });
+		tempMap.put("TAHT", new String[] { "Pacific/Tahiti" });
+		tempMap
+				.put("TASST",
+						new String[] { "Asia/Samarkand", "Asia/Tashkent" });
+		tempMap.put("TAST", new String[] { "Asia/Samarkand", "Asia/Tashkent" });
+		tempMap.put("TBIST", new String[] { "Asia/Tbilisi" });
+		tempMap.put("TBIT", new String[] { "Asia/Tbilisi" });
+		tempMap.put("TBMT", new String[] { "Asia/Tbilisi" });
+		tempMap.put("TFT", new String[] { "Indian/Kerguelen" });
+		tempMap.put("TJT", new String[] { "Asia/Dushanbe" });
+		tempMap.put("TKT", new String[] { "Pacific/Fakaofo" });
+		tempMap.put("TMST", new String[] { "Asia/Ashkhabad" });
+		tempMap.put("TMT", new String[] { "Europe/Tallinn", "Asia/Tehran",
+				"Asia/Ashkhabad" });
+		tempMap.put("TOST", new String[] { "Pacific/Tongatapu" });
+		tempMap.put("TOT", new String[] { "Pacific/Tongatapu" });
+		tempMap.put("TPT", new String[] { "Asia/Dili" });
+		tempMap.put("TRST", new String[] { "Europe/Istanbul" });
+		tempMap.put("TRT", new String[] { "Europe/Istanbul" });
+		tempMap.put("TRUT", new String[] { "Pacific/Truk" });
+		tempMap.put("TVT", new String[] { "Pacific/Funafuti" });
+		tempMap.put("ULAST", new String[] { "Asia/Ulaanbaatar" });
+		tempMap.put("ULAT", new String[] { "Asia/Ulaanbaatar" });
+		tempMap.put("URUT", new String[] { "Asia/Urumqi" });
+		tempMap.put("UYHST", new String[] { "America/Montevideo" });
+		tempMap.put("UYT", new String[] { "America/Montevideo" });
+		tempMap.put("UZST", new String[] { "Asia/Samarkand", "Asia/Tashkent" });
+		tempMap.put("UZT", new String[] { "Asia/Samarkand", "Asia/Tashkent" });
+		tempMap.put("VET", new String[] { "America/Caracas" });
+		tempMap.put("VLAMT", new String[] { "Asia/Vladivostok" });
+		tempMap.put("VLAST", new String[] { "Asia/Vladivostok" });
+		tempMap.put("VLAT", new String[] { "Asia/Vladivostok" });
+		tempMap.put("VUST", new String[] { "Pacific/Efate" });
+		tempMap.put("VUT", new String[] { "Pacific/Efate" });
+		tempMap.put("WAKT", new String[] { "Pacific/Wake" });
+		tempMap.put("WARST",
+				new String[] { "America/Jujuy", "America/Mendoza" });
+		tempMap
+				.put("WART",
+						new String[] { "America/Jujuy", "America/Mendoza" });
+		tempMap.put("WAST",
+				new String[] { "Africa/Ndjamena", "Africa/Windhoek" });
+		tempMap.put("WAT", new String[] { "Africa/Luanda", "Africa/Porto-Novo",
+				"Africa/Douala", "Africa/Bangui", "Africa/Ndjamena",
+				"Africa/Kinshasa", "Africa/Brazzaville", "Africa/Malabo",
+				"Africa/Libreville", "Africa/Banjul", "Africa/Conakry",
+				"Africa/Bissau", "Africa/Bamako", "Africa/Nouakchott",
+				"Africa/El_Aaiun", "Africa/Windhoek", "Africa/Niamey",
+				"Africa/Lagos", "Africa/Dakar", "Africa/Freetown" });
+		tempMap.put("WEST", new String[] { "Atlantic/Faeroe",
+				"Atlantic/Azores", "Atlantic/Madeira", "Atlantic/Canary",
+				"Europe/Brussels", "Europe/Luxembourg", "Europe/Monaco",
+				"Europe/Lisbon", "Europe/Madrid", "Africa/Algiers",
+				"Africa/Casablanca", "Africa/Ceuta" });
+		tempMap.put("WET", new String[] { "Atlantic/Faeroe", "Atlantic/Azores",
+				"Atlantic/Madeira", "Atlantic/Canary", "Europe/Andorra",
+				"Europe/Brussels", "Europe/Luxembourg", "Europe/Monaco",
+				"Europe/Lisbon", "Europe/Madrid", "Africa/Algiers",
+				"Africa/Casablanca", "Africa/El_Aaiun", "Africa/Ceuta" });
+		tempMap.put("WFT", new String[] { "Pacific/Wallis" });
+		tempMap.put("WGST", new String[] { "America/Godthab" });
+		tempMap.put("WGT", new String[] { "America/Godthab" });
+		tempMap.put("WMT", new String[] { "Europe/Vilnius", "Europe/Warsaw" });
+		tempMap.put("WST", new String[] { "Antarctica/Casey", "Pacific/Apia",
+				"Australia/Perth" });
+		tempMap.put("YAKMT", new String[] { "Asia/Yakutsk" });
+		tempMap.put("YAKST", new String[] { "Asia/Yakutsk" });
+		tempMap.put("YAKT", new String[] { "Asia/Yakutsk" });
+		tempMap.put("YAPT", new String[] { "Pacific/Yap" });
+		tempMap.put("YDDT", new String[] { "America/Whitehorse",
+				"America/Dawson" });
+		tempMap.put("YDT", new String[] { "America/Yakutat",
+				"America/Whitehorse", "America/Dawson" });
+		tempMap.put("YEKMT", new String[] { "Asia/Yekaterinburg" });
+		tempMap.put("YEKST", new String[] { "Asia/Yekaterinburg" });
+		tempMap.put("YEKT", new String[] { "Asia/Yekaterinburg" });
+		tempMap.put("YERST", new String[] { "Asia/Yerevan" });
+		tempMap.put("YERT", new String[] { "Asia/Yerevan" });
+		tempMap.put("YST", new String[] { "America/Yakutat",
+				"America/Whitehorse", "America/Dawson" });
+		tempMap.put("YWT", new String[] { "America/Yakutat" });
+
+		ABBREVIATED_TIMEZONES = Collections.unmodifiableMap(tempMap);
+	}
+
+	/**
+	 * Change the given times from one timezone to another
+	 * 
+	 * @param conn
+	 *            the current connection to the MySQL server
+	 * @param t
+	 *            the times to change
+	 * @param fromTz
+	 *            the timezone to change from
+	 * @param toTz
+	 *            the timezone to change to
+	 * 
+	 * @return the times changed to the timezone 'toTz'
+	 */
+	public static Time changeTimezone(Connection conn,
+			Calendar sessionCalendar, 
+			Calendar targetCalendar, 
+			Time t, 
+			TimeZone fromTz,
+			TimeZone toTz, 
+			boolean rollForward) {
+		if ((conn != null)) {
+			if (conn.getUseTimezone() &&
+				!conn.getNoTimezoneConversionForTimeType()) {
+				// Convert the timestamp from GMT to the server's timezone
+				Calendar fromCal = Calendar.getInstance(fromTz);
+				fromCal.setTime(t);
+	
+				int fromOffset = fromCal.get(Calendar.ZONE_OFFSET)
+						+ fromCal.get(Calendar.DST_OFFSET);
+				Calendar toCal = Calendar.getInstance(toTz);
+				toCal.setTime(t);
+	
+				int toOffset = toCal.get(Calendar.ZONE_OFFSET)
+						+ toCal.get(Calendar.DST_OFFSET);
+				int offsetDiff = fromOffset - toOffset;
+				long toTime = toCal.getTime().getTime();
+	
+				if (rollForward || (conn.isServerTzUTC() && !conn.isClientTzUTC())) {
+					toTime += offsetDiff;
+				} else {
+					toTime -= offsetDiff;
+				}
+	
+				Time changedTime = new Time(toTime);
+	
+				return changedTime;
+			}  else if (conn.getUseJDBCCompliantTimezoneShift()) {
+				if (targetCalendar != null) {
+
+					Time adjustedTime = new Time( 
+							jdbcCompliantZoneShift(sessionCalendar, 
+									targetCalendar, t));
+					
+					return adjustedTime;
+				}
+			}
+		}
+		
+		return t;
+	}
+
+	/**
+	 * Change the given timestamp from one timezone to another
+	 * 
+	 * @param conn
+	 *            the current connection to the MySQL server
+	 * @param tstamp
+	 *            the timestamp to change
+	 * @param fromTz
+	 *            the timezone to change from
+	 * @param toTz
+	 *            the timezone to change to
+	 * 
+	 * @return the timestamp changed to the timezone 'toTz'
+	 */
+	public static Timestamp changeTimezone(Connection conn, 
+			Calendar sessionCalendar, 
+			Calendar targetCalendar, 
+			Timestamp tstamp,
+			TimeZone fromTz, 
+			TimeZone toTz, 
+			boolean rollForward) {
+		if ((conn != null)) {
+			if (conn.getUseTimezone()) {
+				// Convert the timestamp from GMT to the server's timezone
+				Calendar fromCal = Calendar.getInstance(fromTz);
+				fromCal.setTime(tstamp);
+	
+				int fromOffset = fromCal.get(Calendar.ZONE_OFFSET)
+						+ fromCal.get(Calendar.DST_OFFSET);
+				Calendar toCal = Calendar.getInstance(toTz);
+				toCal.setTime(tstamp);
+	
+				int toOffset = toCal.get(Calendar.ZONE_OFFSET)
+						+ toCal.get(Calendar.DST_OFFSET);
+				int offsetDiff = fromOffset - toOffset;
+				long toTime = toCal.getTime().getTime();
+	
+				if (rollForward || (conn.isServerTzUTC() && !conn.isClientTzUTC())) {
+					toTime += offsetDiff;
+				} else {
+					toTime -= offsetDiff;
+				}
+	
+				Timestamp changedTimestamp = new Timestamp(toTime);
+	
+				return changedTimestamp;
+			} else if (conn.getUseJDBCCompliantTimezoneShift()) {
+				if (targetCalendar != null) {
+
+					Timestamp adjustedTimestamp = new Timestamp( 
+							jdbcCompliantZoneShift(sessionCalendar, 
+									targetCalendar, tstamp));
+					
+					adjustedTimestamp.setNanos(tstamp.getNanos());
+					
+					return adjustedTimestamp;
+				}
+			}
+		}
+		
+		return tstamp;
+	}
+
+	private static long jdbcCompliantZoneShift(Calendar sessionCalendar, 
+			Calendar targetCalendar, 
+			java.util.Date dt) {
+		if (sessionCalendar == null) {
+			sessionCalendar = new GregorianCalendar();
+		}
+		
+		// JDBC spec is not clear whether or not this 
+		// calendar should be immutable, so let's treat
+		// it like it is, for safety
+		
+		java.util.Date origCalDate = targetCalendar.getTime();
+		java.util.Date origSessionDate = sessionCalendar.getTime();
+		
+		try {
+			sessionCalendar.setTime(dt);
+			
+			targetCalendar.set(Calendar.YEAR, sessionCalendar.get(Calendar.YEAR));
+			targetCalendar.set(Calendar.MONTH, sessionCalendar.get(Calendar.MONTH));
+			targetCalendar.set(Calendar.DAY_OF_MONTH, sessionCalendar.get(Calendar.DAY_OF_MONTH));
+
+			targetCalendar.set(Calendar.HOUR_OF_DAY, sessionCalendar.get(Calendar.HOUR_OF_DAY));                
+			targetCalendar.set(Calendar.MINUTE, sessionCalendar.get(Calendar.MINUTE));
+			targetCalendar.set(Calendar.SECOND, sessionCalendar.get(Calendar.SECOND));
+			targetCalendar.set(Calendar.MILLISECOND, sessionCalendar.get(Calendar.MILLISECOND));                
+			
+			return targetCalendar.getTime().getTime();
+			
+		} finally {
+			sessionCalendar.setTime(origSessionDate);
+			targetCalendar.setTime(origCalDate);
+		}
+	}
+
+	//
+	// WARN! You must externally synchronize these calendar instances
+	// See ResultSet.fastDateCreate() for an example
+	//
+	final static Date fastDateCreate(boolean useGmtConversion,
+			Calendar gmtCalIfNeeded,
+			Calendar cal, int year, int month, int day) {
+		
+		Calendar dateCal = cal;
+		
+		if (useGmtConversion) {
+			
+			if (gmtCalIfNeeded == null) {
+				gmtCalIfNeeded = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+			}
+			gmtCalIfNeeded.clear();
+			
+			dateCal = gmtCalIfNeeded;
+		}
+		
+		dateCal.clear();
+
+		// why-oh-why is this different than java.util.date,
+		// in the year part, but it still keeps the silly '0'
+		// for the start month????
+		dateCal.set(year, month - 1, day, 0, 0, 0);
+		
+		long dateAsMillis = 0;
+
+		try {
+			dateAsMillis = dateCal.getTimeInMillis();
+		} catch (IllegalAccessError iae) {
+			// Must be on JDK-1.3.1 or older....
+			dateAsMillis = dateCal.getTime().getTime();
+		}
+
+		return new Date(dateAsMillis);
+	}
+
+	final static Time fastTimeCreate(Calendar cal, int hour, int minute,
+			int second) throws SQLException {
+		if (hour < 0 || hour > 23) {
+			throw SQLError.createSQLException("Illegal hour value '" + hour + "' for java.sql.Time type in value '"
+					+ timeFormattedString(hour, minute, second) + ".", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		if (minute < 0 || minute > 59) {
+			throw SQLError.createSQLException("Illegal minute value '" + minute + "'" + "' for java.sql.Time type in value '"
+					+ timeFormattedString(hour, minute, second) + ".", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		if (second < 0 || second > 59) {
+			throw SQLError.createSQLException("Illegal minute value '" + second + "'" + "' for java.sql.Time type in value '"
+					+ timeFormattedString(hour, minute, second) + ".", 
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+		
+		cal.clear();
+
+		// Set 'date' to epoch of Jan 1, 1970
+		cal.set(1970, 0, 1, hour, minute, second);
+
+		long timeAsMillis = 0;
+
+		try {
+			timeAsMillis = cal.getTimeInMillis();
+		} catch (IllegalAccessError iae) {
+			// Must be on JDK-1.3.1 or older....
+			timeAsMillis = cal.getTime().getTime();
+		}
+
+		return new Time(timeAsMillis);
+	}
+
+	final static Timestamp fastTimestampCreate(boolean useGmtConversion,
+			Calendar gmtCalIfNeeded,
+			Calendar cal, int year,
+			int month, int day, int hour, int minute, int seconds,
+			int secondsPart) {
+		cal.clear();
+
+		// why-oh-why is this different than java.util.date,
+		// in the year part, but it still keeps the silly '0'
+		// for the start month????
+		cal.set(year, month - 1, day, hour, minute, seconds);
+
+		int offsetDiff = 0;
+		
+		if (useGmtConversion) {
+			int fromOffset = cal.get(Calendar.ZONE_OFFSET)
+			+ cal.get(Calendar.DST_OFFSET);
+			
+			if (gmtCalIfNeeded == null) {
+				gmtCalIfNeeded = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+			}
+			gmtCalIfNeeded.clear();
+			
+			gmtCalIfNeeded.setTimeInMillis(cal.getTimeInMillis());
+	
+			int toOffset = gmtCalIfNeeded.get(Calendar.ZONE_OFFSET)
+				+ gmtCalIfNeeded.get(Calendar.DST_OFFSET);
+			offsetDiff = fromOffset - toOffset;
+		}
+
+		long tsAsMillis = 0;
+
+		try {
+			tsAsMillis = cal.getTimeInMillis();
+		} catch (IllegalAccessError iae) {
+			// Must be on JDK-1.3.1 or older....
+			tsAsMillis = cal.getTime().getTime();
+		}
+
+		Timestamp ts = new Timestamp(tsAsMillis + offsetDiff);
+		ts.setNanos(secondsPart);
+
+		return ts;
+	}
+
+	/**
+	 * Returns the 'official' Java timezone name for the given timezone
+	 * 
+	 * @param timezoneStr
+	 *            the 'common' timezone name
+	 * 
+	 * @return the Java timezone name for the given timezone
+	 * 
+	 * @throws IllegalArgumentException
+	 *             DOCUMENT ME!
+	 */
+	public static String getCanoncialTimezone(String timezoneStr) {
+		if (timezoneStr == null) {
+			return null;
+		}
+
+		timezoneStr = timezoneStr.trim();
+
+		// Fix windows Daylight/Standard shift JDK doesn't map these (doh)
+
+		int daylightIndex = StringUtils.indexOfIgnoreCase(timezoneStr,
+				"DAYLIGHT");
+
+		if (daylightIndex != -1) {
+			StringBuffer timezoneBuf = new StringBuffer();
+			timezoneBuf.append(timezoneStr.substring(0, daylightIndex));
+			timezoneBuf.append("Standard");
+			timezoneBuf.append(timezoneStr.substring(daylightIndex
+					+ "DAYLIGHT".length(), timezoneStr.length()));
+			timezoneStr = timezoneBuf.toString();
+		}
+
+		String canonicalTz = (String) TIMEZONE_MAPPINGS.get(timezoneStr);
+
+		// if we didn't find it, try abbreviated timezones
+		if (canonicalTz == null) {
+			String[] abbreviatedTimezone = (String[]) ABBREVIATED_TIMEZONES
+					.get(timezoneStr);
+
+			if (abbreviatedTimezone != null) {
+				// If there's only one mapping use that
+				if (abbreviatedTimezone.length == 1) {
+					canonicalTz = abbreviatedTimezone[0];
+				} else {
+					StringBuffer errorMsg = new StringBuffer(
+							"The server timezone value '");
+					errorMsg.append(timezoneStr);
+					errorMsg
+							.append("' represents more than one timezone. You must ");
+					errorMsg
+							.append("configure either the server or client to use a ");
+					errorMsg
+							.append("more specifc timezone value if you want to enable ");
+					errorMsg.append("timezone support. The timezones that '");
+					errorMsg.append(timezoneStr);
+					errorMsg.append("' maps to are: ");
+					errorMsg.append(abbreviatedTimezone[0]);
+
+					for (int i = 1; i < abbreviatedTimezone.length; i++) {
+						errorMsg.append(", ");
+						errorMsg.append(abbreviatedTimezone[i]);
+					}
+
+					throw new IllegalArgumentException(errorMsg.toString());
+				}
+			}
+		}
+
+		return canonicalTz;
+	}
+	
+	// we could use SimpleDateFormat, but it won't work when the time values
+	// are out-of-bounds, and we're using this for error messages for exactly 
+	// that case
+	//
+	
+	private static String timeFormattedString(int hours, int minutes, int seconds) {
+		StringBuffer buf = new StringBuffer(8);
+		
+		if (hours < 10) {
+			buf.append("0");
+		}
+		
+		buf.append(hours);
+		buf.append(":");
+		
+		if (minutes < 10) {
+			buf.append("0");
+		}
+		
+		buf.append(minutes);
+		buf.append(":");
+		
+		if (seconds < 10) {
+			buf.append("0");
+		}
+		
+		buf.append(seconds);
+		
+		return buf.toString();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/UpdatableResultSet.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/UpdatableResultSet.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/UpdatableResultSet.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,2441 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.profiler.ProfileEventSink;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+import java.math.BigDecimal;
+
+import java.sql.SQLException;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+/**
+ * A result set that is updatable.
+ * 
+ * @author Mark Matthews
+ */
+public class UpdatableResultSet extends ResultSet {
+	/** Marker for 'stream' data when doing INSERT rows */
+	private final static byte[] STREAM_DATA_MARKER = "** STREAM DATA **" //$NON-NLS-1$
+	.getBytes();
+
+	private SingleByteCharsetConverter charConverter;
+
+	private String charEncoding;
+
+	/** What is the default value for the column? */
+	private byte[][] defaultColumnValue;
+
+	/** PreparedStatement used to delete data */
+	private com.mysql.jdbc.PreparedStatement deleter = null;
+
+	private String deleteSQL = null;
+
+	private boolean initializedCharConverter = false;
+
+	/** PreparedStatement used to insert data */
+	private com.mysql.jdbc.PreparedStatement inserter = null;
+
+	private String insertSQL = null;
+
+	/** Is this result set updateable? */
+	private boolean isUpdatable = false;
+
+	/** List of primary keys */
+	private List primaryKeyIndicies = null;
+
+	private String qualifiedAndQuotedTableName;
+
+	private String quotedIdChar = null;
+
+	/** PreparedStatement used to refresh data */
+	private com.mysql.jdbc.PreparedStatement refresher;
+
+	private String refreshSQL = null;
+
+	/** The binary data for the 'current' row */
+	private Object[] savedCurrentRow;
+
+	private String tableOnlyName;
+
+	/** PreparedStatement used to delete data */
+	private 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();
+	}
+
+	/**
+	 * 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
+	 *             DOCUMENT ME!
+	 */
+	public UpdatableResultSet(String catalog, Field[] fields, RowData tuples,
+			Connection conn, Statement creatorStmt) throws SQLException {
+		super(catalog, fields, tuples, conn, creatorStmt);
+		checkUpdatability();
+	}
+
+	/**
+	 * 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.
+	 */
+	public synchronized boolean absolute(int row) throws SQLException {
+		return super.absolute(row);
+	}
+
+	/**
+	 * 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 synchronized void afterLast() throws SQLException {
+		super.afterLast();
+	}
+
+	/**
+	 * 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 synchronized void beforeFirst() throws SQLException {
+		super.beforeFirst();
+	}
+
+	/**
+	 * 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.
+	 */
+	public synchronized void cancelRowUpdates() throws SQLException {
+		checkClosed();
+
+		if (this.doingUpdates) {
+			this.doingUpdates = false;
+			this.updater.clearParameters();
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.ResultSet#checkRowPos()
+	 */
+	protected void checkRowPos() throws SQLException {
+		checkClosed();
+
+		if (!this.onInsertRow) {
+			super.checkRowPos();
+		}
+	}
+
+	/**
+	 * Is this ResultSet updateable?
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	private void checkUpdatability() throws SQLException {
+		String singleTableName = null;
+		String catalogName = null;
+
+		int primaryKeyCount = 0;
+
+		if (this.fields.length > 0) {
+			singleTableName = this.fields[0].getOriginalTableName();
+			catalogName = this.fields[0].getDatabaseName();
+
+			if (singleTableName == null) {
+				singleTableName = this.fields[0].getTableName();
+				catalogName = this.catalog;
+			}
+
+			if (this.fields[0].isPrimaryKey()) {
+				primaryKeyCount++;
+			}
+
+			//
+			// References only one table?
+			//
+			for (int i = 1; i < this.fields.length; i++) {
+				String otherTableName = this.fields[i].getOriginalTableName();
+				String otherCatalogName = this.fields[i].getDatabaseName();
+
+				if (otherTableName == null) {
+					otherTableName = this.fields[i].getTableName();
+					otherCatalogName = this.catalog;
+				}
+
+				if ((singleTableName == null)
+						|| !otherTableName.equals(singleTableName)) {
+					this.isUpdatable = false;
+
+					return;
+				}
+
+				// Can't reference more than one database
+				if ((catalogName == null)
+						|| !otherCatalogName.equals(catalogName)) {
+					this.isUpdatable = false;
+
+					return;
+				}
+
+				if (this.fields[i].isPrimaryKey()) {
+					primaryKeyCount++;
+				}
+			}
+
+			if ((singleTableName == null) || (singleTableName.length() == 0)) {
+				this.isUpdatable = false;
+
+				return;
+			}
+		} else {
+			this.isUpdatable = false;
+
+			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();
+
+			java.sql.ResultSet rs = null;
+			HashMap primaryKeyNames = new HashMap();
+
+			try {
+				rs = dbmd.getPrimaryKeys(catalogName, null, singleTableName);
+
+				while (rs.next()) {
+					String keyName = rs.getString(4);
+					keyName = keyName.toUpperCase();
+					primaryKeyNames.put(keyName, keyName);
+				}
+			} finally {
+				if (rs != null) {
+					try {
+						rs.close();
+					} catch (Exception ex) {
+						AssertionFailedException.shouldNotHappen(ex);
+					}
+
+					rs = null;
+				}
+			}
+
+			if (primaryKeyNames.size() == 0) {
+				this.isUpdatable = false;
+
+				return; // we can't update tables w/o keys
+			}
+
+			//
+			// Contains all primary keys?
+			//
+			for (int i = 0; i < this.fields.length; i++) {
+				if (this.fields[i].isPrimaryKey()) {
+					String columnNameUC = this.fields[i].getName()
+							.toUpperCase();
+
+					if (primaryKeyNames.remove(columnNameUC) == null) {
+						// try original name
+						String originalName = this.fields[i].getOriginalName();
+
+						if (originalName != null) {
+							if (primaryKeyNames.remove(originalName
+									.toUpperCase()) == null) {
+								// we don't know about this key, so give up :(
+								this.isUpdatable = false;
+
+								return;
+							}
+						}
+					}
+				}
+			}
+
+			this.isUpdatable = primaryKeyNames.isEmpty();
+
+			return;
+		}
+
+		this.isUpdatable = true;
+
+		return;
+	}
+
+	/**
+	 * 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 SQLException
+	 *             if the ResultSet is not updatable or some other error occurs
+	 */
+	public synchronized void deleteRow() throws SQLException {
+		checkClosed();
+
+		if (!this.isUpdatable) {
+			throw new NotUpdatable();
+		}
+
+		if (this.onInsertRow) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.1")); //$NON-NLS-1$
+		} else if (this.rowData.size() == 0) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.2")); //$NON-NLS-1$
+		} else if (isBeforeFirst()) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.3")); //$NON-NLS-1$
+		} else if (isAfterLast()) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.4")); //$NON-NLS-1$
+		}
+
+		if (this.deleter == null) {
+			if (this.deleteSQL == null) {
+				generateStatements();
+			}
+
+			this.deleter = this.connection
+					.clientPrepareStatement(this.deleteSQL);
+		}
+
+		this.deleter.clearParameters();
+
+		String characterEncoding = null;
+
+		if (this.connection.getUseUnicode()) {
+			characterEncoding = this.connection.getEncoding();
+		}
+
+		//
+		// FIXME: Use internal routines where possible for character
+		// conversion!
+		try {
+			int numKeys = this.primaryKeyIndicies.size();
+
+			if (numKeys == 1) {
+				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));
+				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],
+									characterEncoding));
+					this.deleter.setString(i + 1, currentVal);
+				}
+			}
+
+			this.deleter.executeUpdate();
+			this.rowData.removeRow(this.rowData.getCurrentRowNumber());
+		} catch (java.io.UnsupportedEncodingException encodingEx) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.39", //$NON-NLS-1$
+					new Object[] { this.charEncoding }) //$NON-NLS-1$
+					, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$
+		}
+	}
+
+	private synchronized void extractDefaultValues() throws SQLException {
+		java.sql.DatabaseMetaData dbmd = this.connection.getMetaData();
+
+		java.sql.ResultSet columnsResultSet = null;
+
+		try {
+			columnsResultSet = dbmd.getColumns(this.catalog, null,
+					this.tableOnlyName, "%"); //$NON-NLS-1$
+
+			HashMap columnNameToDefaultValueMap = new HashMap(
+					this.fields.length /* at least this big... */);
+
+			while (columnsResultSet.next()) {
+				String columnName = columnsResultSet.getString("COLUMN_NAME"); //$NON-NLS-1$
+				byte[] defaultValue = columnsResultSet.getBytes("COLUMN_DEF"); //$NON-NLS-1$
+
+				columnNameToDefaultValueMap.put(columnName, defaultValue);
+			}
+
+			int numFields = this.fields.length;
+
+			this.defaultColumnValue = new byte[numFields][];
+
+			for (int i = 0; i < numFields; i++) {
+				String defValTableName = this.fields[i].getOriginalName();
+
+				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;
+			}
+		}
+	}
+
+	/**
+	 * 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 synchronized boolean first() throws SQLException {
+		return super.first();
+	}
+
+	/**
+	 * Figure out whether or not this ResultSet is updateable, and if so,
+	 * generate the PreparedStatements to support updates.
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 * @throws NotUpdatable
+	 *             DOCUMENT ME!
+	 */
+	protected synchronized void generateStatements() throws SQLException {
+		if (!this.isUpdatable) {
+			this.doingUpdates = false;
+			this.onInsertRow = false;
+
+			throw new NotUpdatable();
+		}
+
+		String quotedId = getQuotedIdChar();
+
+		if (this.fields[0].getOriginalTableName() != null) {
+			StringBuffer tableNameBuffer = new StringBuffer();
+
+			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();
+		} 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();
+		}
+
+		this.primaryKeyIndicies = new ArrayList();
+
+		StringBuffer fieldValues = new StringBuffer();
+		StringBuffer keyValues = new StringBuffer();
+		StringBuffer columnNames = new StringBuffer();
+		StringBuffer insertPlaceHolders = new StringBuffer();
+		boolean firstTime = true;
+		boolean keysFirstTime = true;
+
+		String equalsStr = this.connection.versionMeetsMinimum(3, 23, 0) ? "<=>"
+				: "=";
+
+		for (int i = 0; i < this.fields.length; i++) {
+			String originalColumnName = this.fields[i].getOriginalName();
+			String columnName = null;
+
+			if (this.connection.getIO().hasLongColumnInfo()
+					&& (originalColumnName != null)
+					&& (originalColumnName.length() > 0)) {
+				columnName = originalColumnName;
+			} else {
+				columnName = this.fields[i].getName();
+			}
+
+			if (this.fields[i].isPrimaryKey()) {
+				this.primaryKeyIndicies.add(new Integer(i));
+
+				if (!keysFirstTime) {
+					keyValues.append(" AND "); //$NON-NLS-1$
+				} else {
+					keysFirstTime = false;
+				}
+
+				keyValues.append(quotedId);
+				keyValues.append(columnName);
+				keyValues.append(quotedId);
+				keyValues.append(equalsStr);
+				keyValues.append("?"); //$NON-NLS-1$
+			}
+
+			if (firstTime) {
+				firstTime = false;
+				fieldValues.append("SET "); //$NON-NLS-1$
+			} else {
+				fieldValues.append(","); //$NON-NLS-1$
+				columnNames.append(","); //$NON-NLS-1$
+				insertPlaceHolders.append(","); //$NON-NLS-1$
+			}
+
+			insertPlaceHolders.append("?"); //$NON-NLS-1$
+
+			columnNames.append(quotedId);
+			columnNames.append(columnName);
+			columnNames.append(quotedId);
+
+			fieldValues.append(quotedId);
+			fieldValues.append(columnName);
+			fieldValues.append(quotedId);
+			fieldValues.append("=?"); //$NON-NLS-1$
+		}
+
+		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$
+		this.insertSQL = "INSERT INTO " + this.qualifiedAndQuotedTableName //$NON-NLS-1$
+				+ " (" + columnNames.toString() //$NON-NLS-1$ //$NON-NLS-2$
+				+ ") VALUES (" + insertPlaceHolders.toString() + ")"; //$NON-NLS-1$ //$NON-NLS-2$
+		this.refreshSQL = "SELECT " + columnNames.toString() + " FROM " //$NON-NLS-1$ //$NON-NLS-2$
+				+ this.qualifiedAndQuotedTableName //$NON-NLS-1$ //$NON-NLS-2$
+				+ " WHERE " + keyValues.toString(); //$NON-NLS-1$
+		this.deleteSQL = "DELETE FROM " + this.qualifiedAndQuotedTableName //$NON-NLS-1$
+				+ " WHERE " //$NON-NLS-1$ //$NON-NLS-2$
+				+ keyValues.toString();
+	}
+
+	private synchronized SingleByteCharsetConverter getCharConverter()
+			throws SQLException {
+		if (!this.initializedCharConverter) {
+			this.initializedCharConverter = true;
+
+			if (this.connection.getUseUnicode()) {
+				this.charEncoding = connection.getEncoding();
+				this.charConverter = this.connection
+						.getCharsetConverter(this.charEncoding);
+			}
+		}
+
+		return this.charConverter;
+	}
+
+	/**
+	 * 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
+	 */
+	public int getConcurrency() throws SQLException {
+		return (this.isUpdatable ? CONCUR_UPDATABLE : CONCUR_READ_ONLY);
+	}
+
+	private synchronized String getQuotedIdChar() throws SQLException {
+		if (this.quotedIdChar == null) {
+			boolean useQuotedIdentifiers = this.connection
+					.supportsQuotedIdentifiers();
+
+			if (useQuotedIdentifiers) {
+				java.sql.DatabaseMetaData dbmd = this.connection.getMetaData();
+				this.quotedIdChar = dbmd.getIdentifierQuoteString();
+			} else {
+				this.quotedIdChar = ""; //$NON-NLS-1$
+			}
+		}
+
+		return this.quotedIdChar;
+	}
+
+	/**
+	 * 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
+	 */
+	public synchronized void insertRow() throws SQLException {
+		checkClosed();
+
+		if (!this.onInsertRow) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.7")); //$NON-NLS-1$
+		}
+
+		this.inserter.executeUpdate();
+
+		long autoIncrementId = this.inserter.getLastInsertID();
+		int numFields = this.fields.length;
+		byte[][] newRow = new byte[numFields][];
+
+		for (int i = 0; i < numFields; i++) {
+			if (this.inserter.isNull(i)) {
+				newRow[i] = null;
+			} else {
+				newRow[i] = this.inserter.getBytesRepresentation(i);
+			}
+
+			//
+			// WARN: This non-variant only holds if MySQL never allows more
+			// than one auto-increment key (which is the way it is _today_)
+			//
+			if (this.fields[i].isAutoIncrement() && autoIncrementId > 0) {
+				newRow[i] = String.valueOf(autoIncrementId).getBytes();
+			}
+		}
+
+		this.rowData.addRow(newRow);
+		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.
+	 */
+	public synchronized boolean isAfterLast() throws SQLException {
+		return super.isAfterLast();
+	}
+
+	/**
+	 * 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 synchronized boolean isBeforeFirst() throws SQLException {
+		return super.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 synchronized boolean isFirst() throws SQLException {
+		return super.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 synchronized boolean isLast() throws SQLException {
+		return super.isLast();
+	}
+
+	boolean isUpdatable() {
+		return this.isUpdatable;
+	}
+
+	/**
+	 * 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 synchronized boolean last() throws SQLException {
+		return super.last();
+	}
+
+	/**
+	 * 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 SQLException
+	 *             if the ResultSet is not updatable or some other error occurs
+	 */
+	public synchronized void moveToCurrentRow() throws SQLException {
+		checkClosed();
+
+		if (!this.isUpdatable) {
+			throw new NotUpdatable();
+		}
+
+		if (this.onInsertRow) {
+			this.onInsertRow = false;
+			this.thisRow = this.savedCurrentRow;
+		}
+	}
+
+	/**
+	 * 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 synchronized void moveToInsertRow() throws SQLException {
+		checkClosed();
+
+		if (!this.isUpdatable) {
+			throw new NotUpdatable();
+		}
+
+		if (this.inserter == null) {
+			if (this.insertSQL == null) {
+				generateStatements();
+			}
+
+			this.inserter = this.connection
+					.clientPrepareStatement(this.insertSQL);
+			extractDefaultValues();
+			resetInserter();
+		} else {
+			resetInserter();
+		}
+
+		int numFields = this.fields.length;
+
+		this.onInsertRow = true;
+		this.doingUpdates = false;
+		this.savedCurrentRow = this.thisRow;
+		this.thisRow = new byte[numFields][];
+
+		for (int i = 0; i < numFields; i++) {
+			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:
+
+					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;
+					}
+				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);
+				this.thisRow[i] = defaultValueCopy;
+			} else {
+				this.inserter.setNull(i + 1, java.sql.Types.NULL);
+				this.thisRow[i] = null;
+			}
+		}
+	}
+
+	// ---------------------------------------------------------------------
+	// Updates
+	// ---------------------------------------------------------------------
+
+	/**
+	 * 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 synchronized boolean next() throws SQLException {
+		return super.next();
+	}
+
+	/**
+	 * 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
+	 */
+	public synchronized boolean prev() throws SQLException {
+		return super.prev();
+	}
+
+	/**
+	 * 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 synchronized boolean previous() throws SQLException {
+		return super.previous();
+	}
+
+	/**
+	 * 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 {
+		if (this.isClosed) {
+			return;
+		}
+		
+		SQLException sqlEx = null;
+
+		if (this.useUsageAdvisor) {
+			if ((this.deleter == null) && (this.inserter == null)
+					&& (this.refresher == null) && (this.updater == null)) {
+				this.eventSink = ProfileEventSink.getInstance(this.connection);
+
+				String message = Messages.getString("UpdatableResultSet.34"); //$NON-NLS-1$
+
+				this.eventSink.consumeEvent(new ProfilerEvent(
+						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));
+			}
+		}
+
+		try {
+			if (this.deleter != null) {
+				this.deleter.close();
+			}
+		} catch (SQLException ex) {
+			sqlEx = ex;
+		}
+
+		try {
+			if (this.inserter != null) {
+				this.inserter.close();
+			}
+		} catch (SQLException ex) {
+			sqlEx = ex;
+		}
+
+		try {
+			if (this.refresher != null) {
+				this.refresher.close();
+			}
+		} catch (SQLException ex) {
+			sqlEx = ex;
+		}
+
+		try {
+			if (this.updater != null) {
+				this.updater.close();
+			}
+		} catch (SQLException ex) {
+			sqlEx = ex;
+		}
+
+		super.realClose(calledExplicitly);
+
+		if (sqlEx != null) {
+			throw sqlEx;
+		}
+	}
+
+	/**
+	 * 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 synchronized void refreshRow() throws SQLException {
+		checkClosed();
+
+		if (!this.isUpdatable) {
+			throw new NotUpdatable();
+		}
+
+		if (this.onInsertRow) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.8")); //$NON-NLS-1$
+		} else if (this.rowData.size() == 0) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.9")); //$NON-NLS-1$
+		} else if (isBeforeFirst()) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.10")); //$NON-NLS-1$
+		} else if (isAfterLast()) {
+			throw SQLError.createSQLException(Messages.getString("UpdatableResultSet.11")); //$NON-NLS-1$
+		}
+
+		if (this.refresher == null) {
+			if (this.refreshSQL == null) {
+				generateStatements();
+			}
+
+			this.refresher = this.connection
+					.clientPrepareStatement(this.refreshSQL);
+		}
+
+		this.refresher.clearParameters();
+
+		int numKeys = this.primaryKeyIndicies.size();
+
+		if (numKeys == 1) {
+			byte[] dataFrom = null;
+			int index = ((Integer) this.primaryKeyIndicies.get(0)).intValue();
+
+			if (!this.doingUpdates) {
+				dataFrom = (byte[]) this.thisRow[index];
+			} else {
+				dataFrom = this.updater.getBytesRepresentation(index);
+
+				// Primary keys not set?
+				if (this.updater.isNull(index) || (dataFrom.length == 0)) {
+					dataFrom = (byte[]) this.thisRow[index];
+				} else {
+					dataFrom = stripBinaryPrefix(dataFrom);
+				}
+			}
+
+			this.refresher.setBytesNoEscape(1, dataFrom);
+		} else {
+			for (int i = 0; i < numKeys; i++) {
+				byte[] dataFrom = null;
+				int index = ((Integer) this.primaryKeyIndicies.get(i))
+						.intValue();
+
+				if (!this.doingUpdates) {
+					dataFrom = (byte[]) this.thisRow[index];
+				} else {
+					dataFrom = this.updater.getBytesRepresentation(index);
+
+					// Primary keys not set?
+					if (this.updater.isNull(index) || (dataFrom.length == 0)) {
+						dataFrom = (byte[]) this.thisRow[index];
+					} else {
+						dataFrom = stripBinaryPrefix(dataFrom);
+					}
+				}
+
+				this.refresher.setBytesNoEscape(i + 1, dataFrom);
+			}
+		}
+
+		java.sql.ResultSet rs = null;
+
+		try {
+			rs = this.refresher.executeQuery();
+
+			int numCols = rs.getMetaData().getColumnCount();
+
+			if (rs.next()) {
+				for (int i = 0; i < numCols; i++) {
+					byte[] val = rs.getBytes(i + 1);
+
+					if ((val == null) || rs.wasNull()) {
+						this.thisRow[i] = null;
+					} else {
+						this.thisRow[i] = rs.getBytes(i + 1);
+					}
+				}
+			} else {
+				throw SQLError.createSQLException(Messages
+						.getString("UpdatableResultSet.12"), //$NON-NLS-1$
+						SQLError.SQL_STATE_GENERAL_ERROR); //$NON-NLS-1$
+			}
+		} finally {
+			if (rs != null) {
+				try {
+					rs.close();
+				} catch (SQLException ex) {
+					; // ignore
+				}
+			}
+		}
+	}
+
+	/**
+	 * 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.
+	 */
+	public synchronized boolean relative(int rows) throws SQLException {
+		return super.relative(rows);
+	}
+
+	private void resetInserter() throws SQLException {
+		this.inserter.clearParameters();
+
+		for (int i = 0; i < this.fields.length; i++) {
+			this.inserter.setNull(i + 1, 0);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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 synchronized 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 synchronized boolean rowUpdated() throws SQLException {
+		throw new NotImplemented();
+	}
+
+	/**
+	 * Sets the concurrency type of this result set
+	 * 
+	 * @param concurrencyFlag
+	 *            the type of concurrency that this ResultSet should support.
+	 */
+	protected void setResultSetConcurrency(int concurrencyFlag) {
+		super.setResultSetConcurrency(concurrencyFlag);
+
+		//
+		// FIXME: Issue warning when asked for updateable result set, but result
+		// set is not
+		// updatable
+		//
+		// if ((concurrencyFlag == CONCUR_UPDATABLE) && !isUpdatable()) {
+		// java.sql.SQLWarning warning = new java.sql.SQLWarning(
+		// NotUpdatable.NOT_UPDATEABLE_MESSAGE);
+		// }
+	}
+
+	private byte[] stripBinaryPrefix(byte[] dataFrom) {
+		return StringUtils.stripEnclosure(dataFrom, "_binary'", "'");
+	}
+
+	/**
+	 * 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 {
+		if (this.updater == null) {
+			if (this.updateSQL == null) {
+				generateStatements();
+			}
+
+			this.updater = this.connection
+					.clientPrepareStatement(this.updateSQL);
+		}
+
+		int numFields = this.fields.length;
+		this.updater.clearParameters();
+
+		for (int i = 0; i < numFields; i++) {
+			if (this.thisRow[i] != null) {
+				this.updater.setBytes(i + 1, (byte[]) this.thisRow[i],
+						this.fields[i].isBinary(), false);
+			} else {
+				this.updater.setNull(i + 1, 0);
+			}
+		}
+
+		int numKeys = this.primaryKeyIndicies.size();
+
+		if (numKeys == 1) {
+			int index = ((Integer) this.primaryKeyIndicies.get(0)).intValue();
+			byte[] keyData = (byte[]) this.thisRow[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()];
+
+				if (currentVal != null) {
+					this.updater.setBytes(numFields + i + 1, currentVal, false,
+							false);
+				} else {
+					this.updater.setNull(numFields + i + 1, 0);
+				}
+			}
+		}
+	}
+
+	/**
+	 * 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
+	 */
+	public synchronized void updateAsciiStream(int columnIndex,
+			java.io.InputStream x, int length) throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setAsciiStream(columnIndex, x, length);
+		} else {
+			this.inserter.setAsciiStream(columnIndex, x, length);
+			this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateBigDecimal(int columnIndex, BigDecimal x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setBigDecimal(columnIndex, x);
+		} else {
+			this.inserter.setBigDecimal(columnIndex, x);
+
+			if (x == null) {
+				this.thisRow[columnIndex - 1] = null;
+			} else {
+				this.thisRow[columnIndex - 1] = x.toString().getBytes();
+			}
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateBinaryStream(int columnIndex,
+			java.io.InputStream x, int length) throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setBinaryStream(columnIndex, x, length);
+		} else {
+			this.inserter.setBinaryStream(columnIndex, x, length);
+
+			if (x == null) {
+				this.thisRow[columnIndex - 1] = null;
+			} else {
+				this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
+			}
+		}
+	}
+
+	/**
+	 * 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 synchronized void updateBinaryStream(String columnName,
+			java.io.InputStream x, int length) throws SQLException {
+		updateBinaryStream(findColumn(columnName), x, length);
+	}
+
+	/**
+	 * @see ResultSet#updateBlob(int, Blob)
+	 */
+	public synchronized void updateBlob(int columnIndex, java.sql.Blob blob)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setBlob(columnIndex, blob);
+		} else {
+			this.inserter.setBlob(columnIndex, blob);
+
+			if (blob == null) {
+				this.thisRow[columnIndex - 1] = null;
+			} else {
+				this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
+			}
+		}
+	}
+
+	/**
+	 * @see ResultSet#updateBlob(String, Blob)
+	 */
+	public synchronized void updateBlob(String columnName, java.sql.Blob blob)
+			throws SQLException {
+		updateBlob(findColumn(columnName), blob);
+	}
+
+	/**
+	 * 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
+	 */
+	public synchronized void updateBoolean(int columnIndex, boolean x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setBoolean(columnIndex, x);
+		} else {
+			this.inserter.setBoolean(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateByte(int columnIndex, byte x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setByte(columnIndex, x);
+		} else {
+			this.inserter.setByte(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateBytes(int columnIndex, byte[] x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setBytes(columnIndex, x);
+		} else {
+			this.inserter.setBytes(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = 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 columnName
+	 *            the name of the column
+	 * @param x
+	 *            the new column value
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public synchronized 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
+	 */
+	public synchronized void updateCharacterStream(int columnIndex,
+			java.io.Reader x, int length) throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setCharacterStream(columnIndex, x, length);
+		} else {
+			this.inserter.setCharacterStream(columnIndex, x, length);
+
+			if (x == null) {
+				this.thisRow[columnIndex - 1] = null;
+			} else {
+				this.thisRow[columnIndex - 1] = STREAM_DATA_MARKER;
+			}
+		}
+	}
+
+	/**
+	 * 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 new column value
+	 * @param length
+	 *            of the stream
+	 * 
+	 * @exception SQLException
+	 *                if a database-access error occurs
+	 */
+	public synchronized void updateCharacterStream(String columnName,
+			java.io.Reader reader, int length) throws SQLException {
+		updateCharacterStream(findColumn(columnName), reader, length);
+	}
+
+	/**
+	 * @see ResultSet#updateClob(int, Clob)
+	 */
+	public void updateClob(int columnIndex, java.sql.Clob clob)
+			throws SQLException {
+		if (clob == null) {
+			updateNull(columnIndex);
+		} else {
+			updateCharacterStream(columnIndex, clob.getCharacterStream(),
+					(int) clob.length());
+		}
+	}
+
+	/**
+	 * 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
+	 */
+	public synchronized void updateDate(int columnIndex, java.sql.Date x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setDate(columnIndex, x);
+		} else {
+			this.inserter.setDate(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateDouble(int columnIndex, double x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setDouble(columnIndex, x);
+		} else {
+			this.inserter.setDouble(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateFloat(int columnIndex, float x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setFloat(columnIndex, x);
+		} else {
+			this.inserter.setFloat(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateInt(int columnIndex, int x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setInt(columnIndex, x);
+		} else {
+			this.inserter.setInt(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateLong(int columnIndex, long x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setLong(columnIndex, x);
+		} else {
+			this.inserter.setLong(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateNull(int columnIndex) throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setNull(columnIndex, 0);
+		} else {
+			this.inserter.setNull(columnIndex, 0);
+
+			this.thisRow[columnIndex - 1] = null;
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateObject(int columnIndex, Object x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setObject(columnIndex, x);
+		} else {
+			this.inserter.setObject(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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
+	 */
+	public synchronized void updateObject(int columnIndex, Object x, int scale)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setObject(columnIndex, x);
+		} else {
+			this.inserter.setObject(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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 synchronized void updateObject(String columnName, Object x, int scale)
+			throws SQLException {
+		updateObject(findColumn(columnName), x);
+	}
+
+	/**
+	 * 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 synchronized void updateRow() throws SQLException {
+		if (!this.isUpdatable) {
+			throw new NotUpdatable();
+		}
+
+		if (this.doingUpdates) {
+			this.updater.executeUpdate();
+			refreshRow();
+			this.doingUpdates = false;
+		}
+
+		//
+		// fixes calling updateRow() and then doing more
+		// updates on same row...
+		syncUpdate();
+	}
+
+	/**
+	 * 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
+	 */
+	public synchronized void updateShort(int columnIndex, short x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setShort(columnIndex, x);
+		} else {
+			this.inserter.setShort(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateString(int columnIndex, String x)
+			throws SQLException {
+		checkClosed();
+		
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setString(columnIndex, x);
+		} else {
+			this.inserter.setString(columnIndex, x);
+
+			if (x == null) {
+				this.thisRow[columnIndex - 1] = null;
+			} else {
+				if (getCharConverter() != null) {
+					this.thisRow[columnIndex - 1] = StringUtils.getBytes(x,
+							this.charConverter, this.charEncoding,
+							this.connection.getServerCharacterEncoding(),
+							this.connection.parserKnowsUnicode());
+				} else {
+					this.thisRow[columnIndex - 1] = x.getBytes();
+				}
+			}
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateTime(int columnIndex, java.sql.Time x)
+			throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setTime(columnIndex, x);
+		} else {
+			this.inserter.setTime(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized 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
+	 */
+	public synchronized void updateTimestamp(int columnIndex,
+			java.sql.Timestamp x) throws SQLException {
+		if (!this.onInsertRow) {
+			if (!this.doingUpdates) {
+				this.doingUpdates = true;
+				syncUpdate();
+			}
+
+			this.updater.setTimestamp(columnIndex, x);
+		} else {
+			this.inserter.setTimestamp(columnIndex, x);
+
+			this.thisRow[columnIndex - 1] = this.inserter
+					.getBytesRepresentation(columnIndex - 1);
+		}
+	}
+
+	/**
+	 * 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 synchronized void updateTimestamp(String columnName,
+			java.sql.Timestamp x) throws SQLException {
+		updateTimestamp(findColumn(columnName), x);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Util.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Util.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/Util.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,296 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.ObjectInputStream;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * Various utility methods for the driver.
+ * 
+ * @author Mark Matthews
+ */
+public class Util {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	class RandStructcture {
+		long maxValue;
+
+		double maxValueDbl;
+
+		long seed1;
+
+		long seed2;
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	private static Util enclosingInstance = new Util();
+
+	// Right from Monty's code
+	static String newCrypt(String password, String seed) {
+		byte b;
+		double d;
+
+		if ((password == null) || (password.length() == 0)) {
+			return password;
+		}
+
+		long[] pw = newHash(seed);
+		long[] msg = newHash(password);
+		long max = 0x3fffffffL;
+		long seed1 = (pw[0] ^ msg[0]) % max;
+		long seed2 = (pw[1] ^ msg[1]) % max;
+		char[] chars = new char[seed.length()];
+
+		for (int i = 0; i < seed.length(); i++) {
+			seed1 = ((seed1 * 3) + seed2) % max;
+			seed2 = (seed1 + seed2 + 33) % max;
+			d = (double) seed1 / (double) max;
+			b = (byte) java.lang.Math.floor((d * 31) + 64);
+			chars[i] = (char) b;
+		}
+
+		seed1 = ((seed1 * 3) + seed2) % max;
+		seed2 = (seed1 + seed2 + 33) % max;
+		d = (double) seed1 / (double) max;
+		b = (byte) java.lang.Math.floor(d * 31);
+
+		for (int i = 0; i < seed.length(); i++) {
+			chars[i] ^= (char) b;
+		}
+
+		return new String(chars);
+	}
+
+	static long[] newHash(String password) {
+		long nr = 1345345333L;
+		long add = 7;
+		long nr2 = 0x12345671L;
+		long tmp;
+
+		for (int i = 0; i < password.length(); ++i) {
+			if ((password.charAt(i) == ' ') || (password.charAt(i) == '\t')) {
+				continue; // skip spaces
+			}
+
+			tmp = (0xff & password.charAt(i));
+			nr ^= ((((nr & 63) + add) * tmp) + (nr << 8));
+			nr2 += ((nr2 << 8) ^ nr);
+			add += tmp;
+		}
+
+		long[] result = new long[2];
+		result[0] = nr & 0x7fffffffL;
+		result[1] = nr2 & 0x7fffffffL;
+
+		return result;
+	}
+
+	static String oldCrypt(String password, String seed) {
+		long hp;
+		long hm;
+		long s1;
+		long s2;
+		long max = 0x01FFFFFF;
+		double d;
+		byte b;
+
+		if ((password == null) || (password.length() == 0)) {
+			return password;
+		}
+
+		hp = oldHash(seed);
+		hm = oldHash(password);
+
+		long nr = hp ^ hm;
+		nr %= max;
+		s1 = nr;
+		s2 = nr / 2;
+
+		char[] chars = new char[seed.length()];
+
+		for (int i = 0; i < seed.length(); i++) {
+			s1 = ((s1 * 3) + s2) % max;
+			s2 = (s1 + s2 + 33) % max;
+			d = (double) s1 / max;
+			b = (byte) java.lang.Math.floor((d * 31) + 64);
+			chars[i] = (char) b;
+		}
+
+		return new String(chars);
+	}
+
+	static long oldHash(String password) {
+		long nr = 1345345333;
+		long nr2 = 7;
+		long tmp;
+
+		for (int i = 0; i < password.length(); i++) {
+			if ((password.charAt(i) == ' ') || (password.charAt(i) == '\t')) {
+				continue;
+			}
+
+			tmp = password.charAt(i);
+			nr ^= ((((nr & 63) + nr2) * tmp) + (nr << 8));
+			nr2 += tmp;
+		}
+
+		return nr & ((1L << 31) - 1L);
+	}
+
+	private static RandStructcture randomInit(long seed1, long seed2) {
+		RandStructcture randStruct = enclosingInstance.new RandStructcture();
+
+		randStruct.maxValue = 0x3FFFFFFFL;
+		randStruct.maxValueDbl = randStruct.maxValue;
+		randStruct.seed1 = seed1 % randStruct.maxValue;
+		randStruct.seed2 = seed2 % randStruct.maxValue;
+
+		return randStruct;
+	}
+
+	/**
+	 * Given a ResultSet and an index into the columns of that ResultSet, read
+	 * binary data from the column which represents a serialized object, and
+	 * re-create the object.
+	 * 
+	 * @param resultSet
+	 *            the ResultSet to use.
+	 * @param index
+	 *            an index into the ResultSet.
+	 * @return the object if it can be de-serialized
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public static Object readObject(java.sql.ResultSet resultSet, int index)
+			throws Exception {
+		ObjectInputStream objIn = new ObjectInputStream(resultSet
+				.getBinaryStream(index));
+		Object obj = objIn.readObject();
+		objIn.close();
+
+		return obj;
+	}
+
+	private static double rnd(RandStructcture randStruct) {
+		randStruct.seed1 = ((randStruct.seed1 * 3) + randStruct.seed2)
+				% randStruct.maxValue;
+		randStruct.seed2 = (randStruct.seed1 + randStruct.seed2 + 33)
+				% randStruct.maxValue;
+
+		return ((randStruct.seed1) / randStruct.maxValueDbl);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param message
+	 *            DOCUMENT ME!
+	 * @param password
+	 *            DOCUMENT ME!
+	 * 
+	 * @return DOCUMENT ME!
+	 */
+	public static String scramble(String message, String password) {
+		long[] hashPass;
+		long[] hashMessage;
+		byte[] to = new byte[8];
+		String val = ""; //$NON-NLS-1$
+
+		message = message.substring(0, 8);
+
+		if ((password != null) && (password.length() > 0)) {
+			hashPass = newHash(password);
+			hashMessage = newHash(message);
+
+			RandStructcture randStruct = randomInit(hashPass[0]
+					^ hashMessage[0], hashPass[1] ^ hashMessage[1]);
+
+			int msgPos = 0;
+			int msgLength = message.length();
+			int toPos = 0;
+
+			while (msgPos++ < msgLength) {
+				to[toPos++] = (byte) (Math.floor(rnd(randStruct) * 31) + 64);
+			}
+
+			/* Make it harder to break */
+			byte extra = (byte) (Math.floor(rnd(randStruct) * 31));
+
+			for (int i = 0; i < to.length; i++) {
+				to[i] ^= extra;
+			}
+
+			val = new String(to);
+		}
+
+		return val;
+	}
+
+	// ~ Inner Classes
+	// ----------------------------------------------------------
+
+	/**
+	 * Converts a nested exception into a nicer message
+	 * 
+	 * @param ex
+	 *            the exception to expand into a message.
+	 * 
+	 * @return a message containing the exception, the message (if any), and a
+	 *         stacktrace.
+	 */
+	public static String stackTraceToString(Throwable ex) {
+		StringBuffer traceBuf = new StringBuffer();
+		traceBuf.append(Messages.getString("Util.1")); //$NON-NLS-1$
+
+		if (ex != null) {
+			traceBuf.append(ex.getClass().getName());
+
+			String message = ex.getMessage();
+
+			if (message != null) {
+				traceBuf.append(Messages.getString("Util.2")); //$NON-NLS-1$
+				traceBuf.append(message);
+			}
+
+			StringWriter out = new StringWriter();
+
+			PrintWriter printOut = new PrintWriter(out);
+
+			ex.printStackTrace(printOut);
+
+			traceBuf.append(Messages.getString("Util.3")); //$NON-NLS-1$
+			traceBuf.append(out.toString());
+		}
+
+		traceBuf.append(Messages.getString("Util.4")); //$NON-NLS-1$
+
+		return traceBuf.toString();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WatchableOutputStream.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WatchableOutputStream.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WatchableOutputStream.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,64 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+
+/**
+ * A java.io.OutputStream used to write ASCII data into Blobs and Clobs
+ * 
+ * @author Mark Matthews
+ */
+class WatchableOutputStream extends ByteArrayOutputStream {
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	private OutputStreamWatcher watcher;
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * @see java.io.OutputStream#close()
+	 */
+	public void close() throws IOException {
+		super.close();
+
+		if (this.watcher != null) {
+			this.watcher.streamClosed(this);
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param watcher
+	 *            DOCUMENT ME!
+	 */
+	public void setWatcher(OutputStreamWatcher watcher) {
+		this.watcher = watcher;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WatchableWriter.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WatchableWriter.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WatchableWriter.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,64 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.CharArrayWriter;
+
+/**
+ * A java.io.Writer used to write unicode data into Blobs and Clobs
+ * 
+ * @author Mark Matthews
+ */
+class WatchableWriter extends CharArrayWriter {
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	private WriterWatcher watcher;
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * @see java.io.Writer#close()
+	 */
+	public void close() {
+		super.close();
+
+		// Send data to watcher
+		if (this.watcher != null) {
+			this.watcher.writerClosed(this);
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param watcher
+	 *            DOCUMENT ME!
+	 */
+	public void setWatcher(WriterWatcher watcher) {
+		this.watcher = watcher;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WriterWatcher.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WriterWatcher.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/WriterWatcher.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,42 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+/**
+ * Objects that want to be notified of lifecycle events on a WatchableWriter
+ * should implement this interface, and register themselves with setWatcher() on
+ * the WatchableWriter instance.
+ * 
+ * @author Mark Matthews
+ */
+interface WriterWatcher {
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Called when the Writer being watched has .close() called
+	 */
+	void writerClosed(WatchableWriter out);
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/3-0-Compat.properties
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/3-0-Compat.properties	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/3-0-Compat.properties	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,16 @@
+#
+# Settings to maintain Connector/J 3.0.x compatibility
+# (as much as it can be)
+#
+
+emptyStringsConvertToZero=true
+jdbcCompliantTruncation=false
+noDatetimeStringSync=true
+nullCatalogMeansCurrent=true
+nullNamePatternMatchesAll=true
+transformedBitIsBoolean=false
+dontTrackOpenResources=true
+zeroDateTimeBehavior=convertToNull
+useServerPrepStmts=false
+autoClosePStmtStreams=true
+processEscapeCodesForPrepStmts=false
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/clusterBase.properties
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/clusterBase.properties	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/clusterBase.properties	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,4 @@
+# Basic properties for clusters
+autoReconnect=true
+failOverReadOnly=false
+roundRobinLoadBalance=true
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/fullDebug.properties
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/fullDebug.properties	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/fullDebug.properties	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,6 @@
+# Settings for 'max-debug' style situations
+profileSQL=true
+gatherPerMetrics=true
+useUsageAdvisor=true
+logSlowQueries=true
+explainSlowQueries=true
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/maxPerformance.properties
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/maxPerformance.properties	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/maxPerformance.properties	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,25 @@
+#
+# A configuration that maximizes performance, while
+# still staying JDBC-compliant and not doing anything that
+# would be "dangerous" to run-of-the-mill J2EE applications
+#
+# Note that because we're caching things like callable statements
+# and the server configuration, this bundle isn't appropriate
+# for use with servers that get config'd dynamically without
+# restarting the application using this configuration bundle.
+
+cachePrepStmts=true
+cacheCallableStatements=true
+
+cacheServerConfiguration=true
+
+#
+# Reduces amount of calls to database to set
+# session state. "Safe" as long as application uses
+# Connection methods to set current database, autocommit
+# and transaction isolation
+# 
+
+useLocalSessionState=true
+elideSetAutoCommits=true
+alwaysSendSetIsolation=false
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,13 @@
+#
+# Solaris has pretty high syscall overhead, so these configs
+# remove as many syscalls as possible.
+#
+
+# Reduce recv() syscalls
+
+useUnbufferedInput=false
+useReadAheadInput=false
+
+# Reduce number of calls to getTimeOfDay()
+
+maintainTimeStats=false
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLDataException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLDataException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLDataException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,43 @@
+/*
+ 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 MySQLDataException extends MySQLNonTransientException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,44 @@
+/*
+ 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 MySQLIntegrityConstraintViolationException extends
+		MySQLNonTransientException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,44 @@
+/*
+ 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 MySQLInvalidAuthorizationSpecException extends
+		MySQLNonTransientException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,44 @@
+/*
+ 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 MySQLNonTransientConnectionException extends
+		MySQLNonTransientException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -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;
+
+import java.sql.SQLException;
+
+public class MySQLNonTransientException extends SQLException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,43 @@
+/*
+ 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 MySQLSyntaxErrorException extends MySQLNonTransientException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,48 @@
+/*
+ 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 MySQLTimeoutException extends MySQLTransientException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,44 @@
+/*
+ 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 MySQLTransactionRollbackException extends MySQLTransientException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,44 @@
+/*
+ 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 MySQLTransientConnectionException extends MySQLTransientException {
+
+	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: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransientException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransientException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/exceptions/MySQLTransientException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -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;
+
+import java.sql.SQLException;
+
+public class MySQLTransientException extends SQLException {
+
+	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();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,128 @@
+/*
+ Copyright (C) 2002-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.integration.c3p0;
+
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import com.mchange.v2.c3p0.C3P0ProxyConnection;
+import com.mchange.v2.c3p0.QueryConnectionTester;
+import com.mysql.jdbc.CommunicationsException;
+
+/**
+ * ConnectionTester for C3P0 connection pool that uses the more efficient
+ * COM_PING method of testing connection 'liveness' for MySQL, and 'sorts'
+ * exceptions based on SQLState or class of 'CommunicationsException' for
+ * handling exceptions.
+ * 
+ * @version $Id: MysqlConnectionTester.java,v 1.1.2.1 2005/05/13 18:58:39
+ *          mmatthews Exp $
+ */
+public final class MysqlConnectionTester implements QueryConnectionTester {
+
+	private static final long serialVersionUID = 3256444690067896368L;
+
+	private static final Object[] NO_ARGS_ARRAY = new Object[0];
+
+	private Method pingMethod;
+
+	public MysqlConnectionTester() {
+		try {
+			pingMethod = com.mysql.jdbc.Connection.class
+					.getMethod("ping", null);
+		} catch (Exception ex) {
+			// punt, we have no way to recover, other than we now use 'SELECT 1'
+			// for
+			// handling the connection testing.
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @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);
+			} else {
+				Statement pingStatement = null;
+
+				try {
+					pingStatement = con.createStatement();
+					pingStatement.executeQuery("SELECT 1").close();
+				} finally {
+					if (pingStatement != null) {
+						pingStatement.close();
+					}
+				}
+			}
+
+			return CONNECTION_IS_OKAY;
+		} catch (Exception ex) {
+			return CONNECTION_IS_INVALID;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mchange.v2.c3p0.ConnectionTester#statusOnException(java.sql.Connection,
+	 *      java.lang.Throwable)
+	 */
+	public int statusOnException(Connection arg0, Throwable throwable) {
+		if (throwable instanceof CommunicationsException) {
+			return CONNECTION_IS_INVALID;
+		}
+
+		if (throwable instanceof SQLException) {
+			String sqlState = ((SQLException) throwable).getSQLState();
+
+			if (sqlState != null && sqlState.startsWith("08")) {
+				return CONNECTION_IS_INVALID;
+			}
+
+			return CONNECTION_IS_OKAY;
+		}
+
+		// Runtime/Unchecked?
+
+		return CONNECTION_IS_INVALID;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mchange.v2.c3p0.QueryConnectionTester#activeCheckConnection(java.sql.Connection,
+	 *      java.lang.String)
+	 */
+	public int activeCheckConnection(Connection arg0, String arg1) {
+		return CONNECTION_IS_OKAY;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,54 @@
+/*
+ Copyright (C) 2002-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.integration.jboss;
+
+import java.sql.SQLException;
+
+import org.jboss.resource.adapter.jdbc.ExceptionSorter;
+import org.jboss.resource.adapter.jdbc.vendor.MySQLExceptionSorter;
+
+/**
+ * Exception sorter used for JBoss to make recovery of downed/stale connections
+ * work more consistently.
+ * 
+ * @version $Id: ExtendedMysqlExceptionSorter.java,v 1.1.2.1 2005/05/13 18:58:42
+ *          mmatthews Exp $
+ */
+public final class ExtendedMysqlExceptionSorter extends MySQLExceptionSorter {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.jboss.resource.adapter.jdbc.ExceptionSorter#isExceptionFatal(java.sql.SQLException)
+	 */
+	public boolean isExceptionFatal(SQLException ex) {
+		String sqlState = ex.getSQLState();
+
+		if (sqlState != null && sqlState.startsWith("08")) {
+			return true;
+		}
+
+		return super.isExceptionFatal(ex);
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,127 @@
+/*
+ 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.integration.jboss;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.jboss.resource.adapter.jdbc.ValidConnectionChecker;
+
+import com.mysql.jdbc.SQLError;
+
+/**
+ * A more efficient connection checker for JBoss.
+ * 
+ * @version $Id: MysqlValidConnectionChecker.java,v 1.1.2.1 2005/05/13 18:58:42
+ *          mmatthews Exp $
+ */
+public final class MysqlValidConnectionChecker implements
+		ValidConnectionChecker, Serializable {
+
+	private static final long serialVersionUID = 3258689922776119348L;
+
+	private Method pingMethod;
+	
+	private Method pingMethodWrapped;
+
+	private final static Object[] NO_ARGS_OBJECT_ARRAY = new Object[0];
+
+	public MysqlValidConnectionChecker() {
+		try {
+			// Avoid classloader goofiness
+			Class mysqlConnection = Thread.currentThread()
+					.getContextClassLoader().loadClass(
+							"com.mysql.jdbc.Connection");
+
+			pingMethod = mysqlConnection.getMethod("ping", null);
+			
+			Class mysqlConnectionWrapper = Thread.currentThread()
+			.getContextClassLoader().loadClass(
+					"com.mysql.jdbc.jdbc2.optional.ConnectionWrapper");
+			
+			pingMethodWrapped = mysqlConnectionWrapper.getMethod("ping", null);
+		} catch (Exception ex) {
+			// Punt, we'll use 'SELECT 1' to do the check
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see org.jboss.resource.adapter.jdbc.ValidConnectionChecker#isValidConnection(java.sql.Connection)
+	 */
+	public SQLException isValidConnection(Connection conn) {
+		if (conn instanceof com.mysql.jdbc.Connection) {
+			if (pingMethod != null) {
+				try {
+					this.pingMethod.invoke(conn, NO_ARGS_OBJECT_ARRAY);
+	
+					return null;
+				} catch (Exception ex) {
+					if (ex instanceof SQLException) {
+						return (SQLException) ex;
+					}
+	
+					return SQLError.createSQLException("Ping failed: " + ex.toString());
+				}
+			}
+		} else if (conn instanceof com.mysql.jdbc.jdbc2.optional.ConnectionWrapper) {
+			if (pingMethodWrapped != null) {
+				try {
+					this.pingMethodWrapped.invoke(conn, NO_ARGS_OBJECT_ARRAY);
+	
+					return null;
+				} catch (Exception ex) {
+					if (ex instanceof SQLException) {
+						return (SQLException) ex;
+					}
+	
+					return SQLError.createSQLException("Ping failed: " + ex.toString());
+				}
+			}
+		}
+
+		// Punt and use 'SELECT 1'
+
+		Statement pingStatement = null;
+
+		try {
+			pingStatement.executeQuery("SELECT 1").close();
+
+			return null;
+		} catch (SQLException sqlEx) {
+			return sqlEx;
+		} finally {
+			if (pingStatement != null) {
+				try {
+					pingStatement.close();
+				} catch (SQLException sqlEx) {
+					// can't do anything about it here
+				}
+			}
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1765 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+ */
+
+package com.mysql.jdbc.jdbc2.optional;
+
+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.CallableStatement;
+import java.sql.Clob;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.Calendar;
+import java.util.Map;
+
+import com.mysql.jdbc.SQLError;
+
+/**
+ * Wraps callable statements created by pooled connections.
+ * 
+ * @version $Id: CallableStatementWrapper.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class CallableStatementWrapper extends PreparedStatementWrapper
+		implements CallableStatement {
+
+	/**
+	 * @param c
+	 * @param conn
+	 * @param toWrap
+	 */
+	public CallableStatementWrapper(ConnectionWrapper c,
+			MysqlPooledConnection conn, CallableStatement toWrap) {
+		super(c, conn, toWrap);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#registerOutParameter(int, int)
+	 */
+	public void registerOutParameter(int parameterIndex, int sqlType)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).registerOutParameter(
+						parameterIndex, sqlType);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#registerOutParameter(int, int, int)
+	 */
+	public void registerOutParameter(int parameterIndex, int sqlType, int scale)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).registerOutParameter(
+						parameterIndex, sqlType, scale);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#wasNull()
+	 */
+	public boolean wasNull() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).wasNull();
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getString(int)
+	 */
+	public String getString(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getString(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBoolean(int)
+	 */
+	public boolean getBoolean(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getBoolean(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getByte(int)
+	 */
+	public byte getByte(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getByte(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getShort(int)
+	 */
+	public short getShort(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getShort(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getInt(int)
+	 */
+	public int getInt(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getInt(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getLong(int)
+	 */
+	public long getLong(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getLong(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getFloat(int)
+	 */
+	public float getFloat(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getFloat(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getDouble(int)
+	 */
+	public double getDouble(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getDouble(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBigDecimal(int, int)
+	 */
+	public BigDecimal getBigDecimal(int parameterIndex, int scale)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getBigDecimal(
+						parameterIndex, scale);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBytes(int)
+	 */
+	public byte[] getBytes(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getBytes(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getDate(int)
+	 */
+	public Date getDate(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getDate(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getTime(int)
+	 */
+	public Time getTime(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getTime(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getTimestamp(int)
+	 */
+	public Timestamp getTimestamp(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getTimestamp(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getObject(int)
+	 */
+	public Object getObject(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getObject(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBigDecimal(int)
+	 */
+	public BigDecimal getBigDecimal(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getBigDecimal(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getObject(int, java.util.Map)
+	 */
+	public Object getObject(int parameterIndex, Map typeMap)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getObject(
+						parameterIndex, typeMap);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getRef(int)
+	 */
+	public Ref getRef(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getRef(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBlob(int)
+	 */
+	public Blob getBlob(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getBlob(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getClob(int)
+	 */
+	public Clob getClob(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getClob(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getArray(int)
+	 */
+	public Array getArray(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getArray(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getDate(int, java.util.Calendar)
+	 */
+	public Date getDate(int parameterIndex, Calendar cal) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getDate(
+						parameterIndex, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getTime(int, java.util.Calendar)
+	 */
+	public Time getTime(int parameterIndex, Calendar cal) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getTime(
+						parameterIndex, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getTimestamp(int, java.util.Calendar)
+	 */
+	public Timestamp getTimestamp(int parameterIndex, Calendar cal)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getTimestamp(
+						parameterIndex, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#registerOutParameter(int, int,
+	 *      java.lang.String)
+	 */
+	public void registerOutParameter(int paramIndex, int sqlType,
+			String typeName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).registerOutParameter(
+						paramIndex, sqlType, typeName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#registerOutParameter(java.lang.String,
+	 *      int)
+	 */
+	public void registerOutParameter(String parameterName, int sqlType)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).registerOutParameter(
+						parameterName, sqlType);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#registerOutParameter(java.lang.String,
+	 *      int, int)
+	 */
+	public void registerOutParameter(String parameterName, int sqlType,
+			int scale) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).registerOutParameter(
+						parameterName, sqlType, scale);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#registerOutParameter(java.lang.String,
+	 *      int, java.lang.String)
+	 */
+	public void registerOutParameter(String parameterName, int sqlType,
+			String typeName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).registerOutParameter(
+						parameterName, sqlType, typeName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getURL(int)
+	 */
+	public URL getURL(int parameterIndex) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getURL(parameterIndex);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setURL(java.lang.String, java.net.URL)
+	 */
+	public void setURL(String parameterName, URL val) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setURL(parameterName,
+						val);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setNull(java.lang.String, int)
+	 */
+	public void setNull(String parameterName, int sqlType) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setNull(parameterName,
+						sqlType);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setBoolean(java.lang.String, boolean)
+	 */
+	public void setBoolean(String parameterName, boolean x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setBoolean(
+						parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setByte(java.lang.String, byte)
+	 */
+	public void setByte(String parameterName, byte x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt)
+						.setByte(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setShort(java.lang.String, short)
+	 */
+	public void setShort(String parameterName, short x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setShort(parameterName,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setInt(java.lang.String, int)
+	 */
+	public void setInt(String parameterName, int x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setInt(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setLong(java.lang.String, long)
+	 */
+	public void setLong(String parameterName, long x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt)
+						.setLong(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setFloat(java.lang.String, float)
+	 */
+	public void setFloat(String parameterName, float x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setFloat(parameterName,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setDouble(java.lang.String, double)
+	 */
+	public void setDouble(String parameterName, double x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setDouble(parameterName,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setBigDecimal(java.lang.String,
+	 *      java.math.BigDecimal)
+	 */
+	public void setBigDecimal(String parameterName, BigDecimal x)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setBigDecimal(
+						parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setString(java.lang.String,
+	 *      java.lang.String)
+	 */
+	public void setString(String parameterName, String x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setString(parameterName,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setBytes(java.lang.String, byte[])
+	 */
+	public void setBytes(String parameterName, byte[] x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setBytes(parameterName,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date)
+	 */
+	public void setDate(String parameterName, Date x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt)
+						.setDate(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time)
+	 */
+	public void setTime(String parameterName, Time x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt)
+						.setTime(parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setTimestamp(java.lang.String,
+	 *      java.sql.Timestamp)
+	 */
+	public void setTimestamp(String parameterName, Timestamp x)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setTimestamp(
+						parameterName, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setAsciiStream(java.lang.String,
+	 *      java.io.InputStream, int)
+	 */
+	public void setAsciiStream(String parameterName, InputStream x, int 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);
+		}
+
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setBinaryStream(java.lang.String,
+	 *      java.io.InputStream, int)
+	 */
+	public void setBinaryStream(String parameterName, InputStream x, int 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);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setObject(java.lang.String,
+	 *      java.lang.Object, int, int)
+	 */
+	public void setObject(String parameterName, Object x, int targetSqlType,
+			int scale) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setObject(parameterName,
+						x, targetSqlType, scale);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setObject(java.lang.String,
+	 *      java.lang.Object, int)
+	 */
+	public void setObject(String parameterName, Object x, int targetSqlType)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setObject(parameterName,
+						x, targetSqlType);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setObject(java.lang.String,
+	 *      java.lang.Object)
+	 */
+	public void setObject(String parameterName, Object x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setObject(parameterName,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setCharacterStream(java.lang.String,
+	 *      java.io.Reader, int)
+	 */
+	public void setCharacterStream(String parameterName, Reader reader,
+			int 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);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setDate(java.lang.String, java.sql.Date,
+	 *      java.util.Calendar)
+	 */
+	public void setDate(String parameterName, Date x, Calendar cal)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setDate(parameterName,
+						x, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setTime(java.lang.String, java.sql.Time,
+	 *      java.util.Calendar)
+	 */
+	public void setTime(String parameterName, Time x, Calendar cal)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setTime(parameterName,
+						x, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setTimestamp(java.lang.String,
+	 *      java.sql.Timestamp, java.util.Calendar)
+	 */
+	public void setTimestamp(String parameterName, Timestamp x, Calendar cal)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setTimestamp(
+						parameterName, x, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#setNull(java.lang.String, int,
+	 *      java.lang.String)
+	 */
+	public void setNull(String parameterName, int sqlType, String typeName)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((CallableStatement) this.wrappedStmt).setNull(parameterName,
+						sqlType, typeName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getString(int)
+	 */
+	public String getString(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getString(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBoolean(int)
+	 */
+	public boolean getBoolean(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getBoolean(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getByte(int)
+	 */
+	public byte getByte(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getByte(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getShort(int)
+	 */
+	public short getShort(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getShort(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getInt(int)
+	 */
+	public int getInt(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getInt(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getLong(int)
+	 */
+	public long getLong(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getLong(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getFloat(int)
+	 */
+	public float getFloat(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getFloat(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getDouble(int)
+	 */
+	public double getDouble(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getDouble(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBytes(int)
+	 */
+	public byte[] getBytes(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getBytes(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getDate(int)
+	 */
+	public Date getDate(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getDate(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getTime(int)
+	 */
+	public Time getTime(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getTime(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getTimestamp(int)
+	 */
+	public Timestamp getTimestamp(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getTimestamp(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getObject(int)
+	 */
+	public Object getObject(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getObject(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBigDecimal(int)
+	 */
+	public BigDecimal getBigDecimal(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getBigDecimal(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getObject(int, java.util.Map)
+	 */
+	public Object getObject(String parameterName, Map typeMap)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getObject(
+						parameterName, typeMap);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getRef(int)
+	 */
+	public Ref getRef(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getRef(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getBlob(int)
+	 */
+	public Blob getBlob(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getBlob(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getClob(int)
+	 */
+	public Clob getClob(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getClob(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getArray(int)
+	 */
+	public Array getArray(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getArray(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getDate(int, java.util.Calendar)
+	 */
+	public Date getDate(String parameterName, Calendar cal) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getDate(
+						parameterName, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getTime(int, java.util.Calendar)
+	 */
+	public Time getTime(String parameterName, Calendar cal) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getTime(
+						parameterName, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getTimestamp(int, java.util.Calendar)
+	 */
+	public Timestamp getTimestamp(String parameterName, Calendar cal)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt).getTimestamp(
+						parameterName, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.CallableStatement#getURL(java.lang.String)
+	 */
+	public URL getURL(String parameterName) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((CallableStatement) this.wrappedStmt)
+						.getURL(parameterName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,840 @@
+/*
+ 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.jdbc2.optional;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Savepoint;
+import java.sql.Statement;
+
+import com.mysql.jdbc.MysqlErrorNumbers;
+import com.mysql.jdbc.SQLError;
+
+/**
+ * This class serves as a wrapper for the org.gjt.mm.mysql.jdbc2.Connection
+ * class. It is returned to the application server which may wrap it again and
+ * then return it to the application client in response to
+ * dataSource.getConnection().
+ * 
+ * <p>
+ * All method invocations are forwarded to org.gjt.mm.mysql.jdbc2.Connection
+ * unless the close method was previously called, in which case a sqlException
+ * is thrown. The close method performs a 'logical close' on the connection.
+ * </p>
+ * 
+ * <p>
+ * All sqlExceptions thrown by the physical connection are intercepted and sent
+ * to connectionEvent listeners before being thrown to client.
+ * </p>
+ * 
+ * @author Todd Wolff todd.wolff_at_prodigy.net
+ * 
+ * @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;
+
+	private MysqlPooledConnection mpc = null;
+
+	private String invalidHandleStr = "Logical handle no longer valid";
+
+	private boolean closed;
+	private boolean isForXa;
+	
+	/**
+	 * 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 ConnectionWrapper(MysqlPooledConnection mysqlPooledConnection,
+			com.mysql.jdbc.Connection 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);
+		}
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#setAutoCommit
+	 */
+	public void setAutoCommit(boolean autoCommit) throws SQLException {
+		checkClosed();
+
+		if (autoCommit && isInGlobalTx()) {
+			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) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#getAutoCommit()
+	 */
+	public boolean getAutoCommit() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.getAutoCommit();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return false; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#setCatalog()
+	 */
+	public void setCatalog(String catalog) throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.setCatalog(catalog);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @return the current catalog
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public String getCatalog() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.getCatalog();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#isClosed()
+	 */
+	public boolean isClosed() throws SQLException {
+		return (this.closed || this.mc.isClosed());
+	}
+
+	public boolean isMasterConnection() throws SQLException {
+		return this.mc.isMasterConnection();
+	}
+
+	/**
+	 * @see Connection#setHoldability(int)
+	 */
+	public void setHoldability(int arg0) throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.setHoldability(arg0);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * @see Connection#getHoldability()
+	 */
+	public int getHoldability() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.getHoldability();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return Statement.CLOSE_CURRENT_RESULT; // we don't reach this code,
+												// compiler can't tell
+	}
+
+	/**
+	 * Allows clients to determine how long this connection has been idle.
+	 * 
+	 * @return how long the connection has been idle.
+	 */
+	public long getIdleFor() {
+		return this.mc.getIdleFor();
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @return a metadata instance
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public java.sql.DatabaseMetaData getMetaData() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.getMetaData();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#setReadOnly()
+	 */
+	public void setReadOnly(boolean readOnly) throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.setReadOnly(readOnly);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#isReadOnly()
+	 */
+	public boolean isReadOnly() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.isReadOnly();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return false; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#setSavepoint()
+	 */
+	public java.sql.Savepoint setSavepoint() throws SQLException {
+		checkClosed();
+
+		if (isInGlobalTx()) {
+			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) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#setSavepoint(String)
+	 */
+	public java.sql.Savepoint setSavepoint(String arg0) throws SQLException {
+		checkClosed();
+
+		if (isInGlobalTx()) {
+			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) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#setTransactionIsolation()
+	 */
+	public void setTransactionIsolation(int level) throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.setTransactionIsolation(level);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#getTransactionIsolation()
+	 */
+	public int getTransactionIsolation() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.getTransactionIsolation();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return TRANSACTION_REPEATABLE_READ; // we don't reach this code,
+											// compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#setTypeMap()
+	 */
+	public void setTypeMap(java.util.Map map) throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.setTypeMap(map);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#getTypeMap()
+	 */
+	public java.util.Map getTypeMap() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.getTypeMap();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#getWarnings
+	 */
+	public java.sql.SQLWarning getWarnings() throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.getWarnings();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public void clearWarnings() throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.clearWarnings();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * The physical connection is not actually closed. the physical connection
+	 * is closed when the application server calls
+	 * mysqlPooledConnection.close(). this object is de-referenced by the pooled
+	 * connection each time mysqlPooledConnection.getConnection() is called by
+	 * app server.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public void close() throws SQLException {
+		close(true);
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	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);
+		}
+
+		try {
+			this.mc.commit();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#createStatement()
+	 */
+	public java.sql.Statement createStatement() throws SQLException {
+		checkClosed();
+
+		try {
+			return new StatementWrapper(this, this.mpc, this.mc
+					.createStatement());
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#createStatement()
+	 */
+	public java.sql.Statement createStatement(int resultSetType,
+			int resultSetConcurrency) throws SQLException {
+		checkClosed();
+
+		try {
+			return new StatementWrapper(this, this.mpc, this.mc
+					.createStatement(resultSetType, resultSetConcurrency));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#createStatement(int, int, int)
+	 */
+	public java.sql.Statement createStatement(int arg0, int arg1, int arg2)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return new StatementWrapper(this, this.mpc, this.mc
+					.createStatement(arg0, arg1, arg2));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#nativeSQL()
+	 */
+	public String nativeSQL(String sql) throws SQLException {
+		checkClosed();
+
+		try {
+			return this.mc.nativeSQL(sql);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#prepareCall()
+	 */
+	public java.sql.CallableStatement prepareCall(String sql)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return new CallableStatementWrapper(this, this.mpc, this.mc
+					.prepareCall(sql));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#prepareCall()
+	 */
+	public java.sql.CallableStatement prepareCall(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		checkClosed();
+
+		try {
+			return new CallableStatementWrapper(this, this.mpc, this.mc
+					.prepareCall(sql, resultSetType, resultSetConcurrency));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#prepareCall(String, int, int, int)
+	 */
+	public java.sql.CallableStatement prepareCall(String arg0, int arg1,
+			int arg2, int arg3) throws SQLException {
+		checkClosed();
+
+		try {
+			return new CallableStatementWrapper(this, this.mpc, this.mc
+					.prepareCall(arg0, arg1, arg2, arg3));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	public java.sql.PreparedStatement clientPrepare(String sql) throws SQLException
+	{
+		checkClosed();
+
+		try {
+			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
+	{
+		checkClosed();
+
+		try {
+			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.
+	 * 
+	 * @see java.sql.Connection#prepareStatement()
+	 */
+	public java.sql.PreparedStatement prepareStatement(String sql)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return new PreparedStatementWrapper(this, this.mpc, this.mc
+					.prepareStatement(sql));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#prepareStatement()
+	 */
+	public java.sql.PreparedStatement prepareStatement(String sql,
+			int resultSetType, int resultSetConcurrency) throws SQLException {
+		checkClosed();
+
+		try {
+			return new PreparedStatementWrapper(this, this.mpc, this.mc
+					.prepareStatement(sql, resultSetType, resultSetConcurrency));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, int, int, int)
+	 */
+	public java.sql.PreparedStatement prepareStatement(String arg0, int arg1,
+			int arg2, int arg3) throws SQLException {
+		checkClosed();
+
+		try {
+			return new PreparedStatementWrapper(this, this.mpc, this.mc
+					.prepareStatement(arg0, arg1, arg2, arg3));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, int)
+	 */
+	public java.sql.PreparedStatement prepareStatement(String arg0, int arg1)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return new PreparedStatementWrapper(this, this.mpc, this.mc
+					.prepareStatement(arg0, arg1));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, int[])
+	 */
+	public java.sql.PreparedStatement prepareStatement(String arg0, int[] arg1)
+			throws SQLException {
+		checkClosed();
+
+		try {
+			return new PreparedStatementWrapper(this, this.mpc, this.mc
+					.prepareStatement(arg0, arg1));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#prepareStatement(String, String[])
+	 */
+	public java.sql.PreparedStatement prepareStatement(String arg0,
+			String[] arg1) throws SQLException {
+		checkClosed();
+
+		try {
+			return new PreparedStatementWrapper(this, this.mpc, this.mc
+					.prepareStatement(arg0, arg1));
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+
+		return null; // we don't reach this code, compiler can't tell
+	}
+
+	/**
+	 * @see Connection#releaseSavepoint(Savepoint)
+	 */
+	public void releaseSavepoint(Savepoint arg0) throws SQLException {
+		checkClosed();
+
+		try {
+			this.mc.releaseSavepoint(arg0);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * Passes call to method on physical connection instance. Notifies listeners
+	 * of any caught exceptions before re-throwing to client.
+	 * 
+	 * @see java.sql.Connection#rollback()
+	 */
+	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);
+		}
+		
+		try {
+			this.mc.rollback();
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	/**
+	 * @see Connection#rollback(Savepoint)
+	 */
+	public void rollback(Savepoint arg0) 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);
+		}
+		
+		try {
+			this.mc.rollback(arg0);
+		} catch (SQLException sqlException) {
+			checkAndFireConnectionError(sqlException);
+		}
+	}
+
+	public boolean isSameResource(Connection c) {
+		if (c instanceof ConnectionWrapper) {
+			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 false;
+	}
+	
+	protected void close(boolean fireClosedEvent) throws SQLException {
+		synchronized (this.mpc) {
+			if (this.closed) {
+				return;
+			}
+
+			if (!isInGlobalTx() 
+					&& this.mc.getRollbackOnPooledClose()
+					&& !this.getAutoCommit()) {
+				rollback();
+			}
+
+			if (fireClosedEvent) {
+				this.mpc.callListener(
+						MysqlPooledConnection.CONNECTION_CLOSED_EVENT, null);
+			}
+
+			// set closed status to true so that if application client tries to
+			// make additional
+			// calls a sqlException will be thrown. The physical connection is
+			// re-used by the pooled connection each time getConnection is
+			// called.
+			this.closed = true;
+		}
+	}
+
+	private void checkClosed() throws SQLException {
+		if (this.closed) {
+			throw SQLError.createSQLException(this.invalidHandleStr);
+		}
+	}
+
+	protected 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();
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,85 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.PooledConnection;
+
+/**
+ * This class is used to obtain a physical connection and instantiate and return
+ * a MysqlPooledConnection. J2EE application servers map client calls to
+ * dataSource.getConnection to this class based upon mapping set within
+ * deployment descriptor. This class extends MysqlDataSource.
+ * 
+ * @see javax.sql.PooledConnection
+ * @see javax.sql.ConnectionPoolDataSource
+ * @see org.gjt.mm.mysql.MysqlDataSource
+ * @author Todd Wolff <todd.wolff_at_prodigy.net>
+ */
+public class MysqlConnectionPoolDataSource extends MysqlDataSource implements
+		ConnectionPoolDataSource {
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Returns a pooled connection.
+	 * 
+	 * @exception SQLException
+	 *                if an error occurs
+	 * @return a PooledConnection
+	 */
+	public synchronized PooledConnection getPooledConnection()
+			throws SQLException {
+		Connection connection = getConnection();
+		MysqlPooledConnection mysqlPooledConnection = new MysqlPooledConnection(
+				(com.mysql.jdbc.Connection)connection);
+
+		return mysqlPooledConnection;
+	}
+
+	/**
+	 * This method is invoked by the container. Obtains physical connection
+	 * using mySql.Driver class and returns a mysqlPooledConnection object.
+	 * 
+	 * @param s
+	 *            user name
+	 * @param s1
+	 *            password
+	 * @exception SQLException
+	 *                if an error occurs
+	 * @return a PooledConnection
+	 */
+	public synchronized PooledConnection getPooledConnection(String s, String s1)
+			throws SQLException {
+		Connection connection = getConnection(s, s1);
+		MysqlPooledConnection mysqlPooledConnection = new MysqlPooledConnection(
+				(com.mysql.jdbc.Connection)connection);
+
+		return mysqlPooledConnection;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSource.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,427 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import com.mysql.jdbc.ConnectionProperties;
+import com.mysql.jdbc.NonRegisteringDriver;
+
+import java.io.PrintWriter;
+import java.io.Serializable;
+
+import java.sql.SQLException;
+
+import java.util.Properties;
+
+import javax.naming.NamingException;
+import javax.naming.Reference;
+import javax.naming.Referenceable;
+import javax.naming.StringRefAddr;
+
+import javax.sql.DataSource;
+
+/**
+ * A JNDI DataSource for a Mysql JDBC connection
+ * 
+ * @author Mark Matthews
+ */
+public class MysqlDataSource extends ConnectionProperties implements
+		DataSource, Referenceable, Serializable {
+	/** The driver to create connections with */
+	protected static com.mysql.jdbc.Driver mysqlDriver = null;
+
+	static {
+		try {
+			mysqlDriver = (com.mysql.jdbc.Driver) Class.forName(
+					"com.mysql.jdbc.Driver").newInstance();
+		} catch (Exception E) {
+			throw new RuntimeException(
+					"Can not load Driver class com.mysql.jdbc.Driver");
+		}
+	}
+
+	/** Log stream */
+	protected PrintWriter logWriter = null;
+
+	/** Database Name */
+	protected String databaseName = null;
+
+	/** Character Encoding */
+	protected String encoding = null;
+
+	/** Hostname */
+	protected String hostName = null;
+
+	/** Password */
+	protected String password = null;
+
+	/** The profileSql property */
+	protected String profileSql = "false";
+
+	/** The JDBC URL */
+	protected String url = null;
+
+	/** User name */
+	protected String user = null;
+
+	/** Should we construct the URL, or has it been set explicitly */
+	protected boolean explicitUrl = false;
+
+	/** Port number */
+	protected int port = 3306;
+
+	/**
+	 * Default no-arg constructor for Serialization
+	 */
+	public MysqlDataSource() {
+	}
+
+	/**
+	 * Creates a new connection using the already configured username and
+	 * password.
+	 * 
+	 * @return a connection to the database
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public java.sql.Connection getConnection() throws SQLException {
+		return getConnection(this.user, this.password);
+	}
+
+	/**
+	 * Creates a new connection with the given username and password
+	 * 
+	 * @param userID
+	 *            the user id to connect with
+	 * @param password
+	 *            the password to connect with
+	 * 
+	 * @return a connection to the database
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public java.sql.Connection getConnection(String userID, String pass)
+			throws SQLException {
+		Properties props = new Properties();
+
+		if (userID != null) {
+			props.setProperty(NonRegisteringDriver.USER_PROPERTY_KEY, userID);
+		}
+
+		if (pass != null) {
+			props.setProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, pass);
+		}
+
+		exposeAsProperties(props);
+
+		return getConnection(props);
+	}
+
+	/**
+	 * Sets the database name.
+	 * 
+	 * @param dbName
+	 *            the name of the database
+	 */
+	public void setDatabaseName(String dbName) {
+		this.databaseName = dbName;
+	}
+
+	/**
+	 * Gets the name of the database
+	 * 
+	 * @return the name of the database for this data source
+	 */
+	public String getDatabaseName() {
+		return (this.databaseName != null) ? this.databaseName : "";
+	}
+
+	/**
+	 * Sets the log writer for this data source.
+	 * 
+	 * @see javax.sql.DataSource#setLogWriter(PrintWriter)
+	 */
+	public void setLogWriter(PrintWriter output) throws SQLException {
+		this.logWriter = output;
+	}
+
+	/**
+	 * Returns the log writer for this data source
+	 * 
+	 * @return the log writer for this data source
+	 */
+	public java.io.PrintWriter getLogWriter() {
+		return this.logWriter;
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param seconds
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void setLoginTimeout(int seconds) throws SQLException {
+	}
+
+	/**
+	 * Returns the login timeout
+	 * 
+	 * @return the login timeout
+	 */
+	public int getLoginTimeout() {
+		return 0;
+	}
+
+	/**
+	 * Sets the password
+	 * 
+	 * @param pass
+	 *            the password
+	 */
+	public void setPassword(String pass) {
+		this.password = pass;
+	}
+
+	/**
+	 * Sets the database port.
+	 * 
+	 * @param p
+	 *            the port
+	 */
+	public void setPort(int p) {
+		this.port = p;
+	}
+
+	/**
+	 * Returns the port number
+	 * 
+	 * @return the port number
+	 */
+	public int getPort() {
+		return this.port;
+	}
+
+	/**
+	 * Sets the port number
+	 * 
+	 * @param p
+	 *            the port
+	 * 
+	 * @see #setPort
+	 */
+	public void setPortNumber(int p) {
+		setPort(p);
+	}
+
+	/**
+	 * Returns the port number
+	 * 
+	 * @return the port number
+	 */
+	public int getPortNumber() {
+		return getPort();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param ref
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void setPropertiesViaRef(Reference ref) throws SQLException {
+		super.initializeFromRef(ref);
+	}
+
+	/**
+	 * Required method to support this class as a <CODE>Referenceable</CODE>.
+	 * 
+	 * @return a Reference to this data source
+	 * 
+	 * @throws NamingException
+	 *             if a JNDI error occurs
+	 */
+	public Reference getReference() throws NamingException {
+		String factoryName = "com.mysql.jdbc.jdbc2.optional.MysqlDataSourceFactory";
+		Reference ref = new Reference(getClass().getName(), factoryName, null);
+		ref.add(new StringRefAddr(NonRegisteringDriver.USER_PROPERTY_KEY,
+				getUser()));
+		ref.add(new StringRefAddr(NonRegisteringDriver.PASSWORD_PROPERTY_KEY,
+				this.password));
+		ref.add(new StringRefAddr("serverName", getServerName()));
+		ref.add(new StringRefAddr("port", "" + getPort()));
+		ref.add(new StringRefAddr("databaseName", getDatabaseName()));
+		ref.add(new StringRefAddr("url", getUrl()));
+		ref.add(new StringRefAddr("explicitUrl", String
+				.valueOf(this.explicitUrl)));
+
+		//
+		// Now store all of the 'non-standard' properties...
+		//
+		try {
+			storeToRef(ref);
+		} catch (SQLException sqlEx) {
+			throw new NamingException(sqlEx.getMessage());
+		}
+
+		return ref;
+	}
+
+	/**
+	 * Sets the server name.
+	 * 
+	 * @param serverName
+	 *            the server name
+	 */
+	public void setServerName(String serverName) {
+		this.hostName = serverName;
+	}
+
+	/**
+	 * Returns the name of the database server
+	 * 
+	 * @return the name of the database server
+	 */
+	public String getServerName() {
+		return (this.hostName != null) ? this.hostName : "";
+	}
+
+	//
+	// I've seen application servers use both formats
+	// URL or url (doh)
+	//
+
+	/**
+	 * Sets the URL for this connection
+	 * 
+	 * @param url
+	 *            the URL for this connection
+	 */
+	public void setURL(String url) {
+		setUrl(url);
+	}
+
+	/**
+	 * Returns the URL for this connection
+	 * 
+	 * @return the URL for this connection
+	 */
+	public String getURL() {
+		return getUrl();
+	}
+
+	/**
+	 * This method is used by the app server to set the url string specified
+	 * within the datasource deployment descriptor. It is discovered using
+	 * introspection and matches if property name in descriptor is "url".
+	 * 
+	 * @param url
+	 *            url to be used within driver.connect
+	 */
+	public void setUrl(String url) {
+		this.url = url;
+		this.explicitUrl = true;
+	}
+
+	/**
+	 * Returns the JDBC URL that will be used to create the database connection.
+	 * 
+	 * @return the URL for this connection
+	 */
+	public String getUrl() {
+		if (!this.explicitUrl) {
+			String builtUrl = "jdbc:mysql://";
+			builtUrl = builtUrl + getServerName() + ":" + getPort() + "/"
+					+ getDatabaseName();
+
+			return builtUrl;
+		}
+
+		return this.url;
+	}
+
+	/**
+	 * Sets the user ID.
+	 * 
+	 * @param userID
+	 *            the User ID
+	 */
+	public void setUser(String userID) {
+		this.user = userID;
+	}
+
+	/**
+	 * Returns the configured user for this connection
+	 * 
+	 * @return the user for this connection
+	 */
+	public String getUser() {
+		return this.user;
+	}
+
+	/**
+	 * Creates a connection using the specified properties.
+	 * 
+	 * @param props
+	 *            the properties to connect with
+	 * 
+	 * @return a connection to the database
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected java.sql.Connection getConnection(Properties props)
+			throws SQLException {
+		String jdbcUrlToUse = null;
+
+		if (!this.explicitUrl) {
+			StringBuffer jdbcUrl = new StringBuffer("jdbc:mysql://");
+
+			if (this.hostName != null) {
+				jdbcUrl.append(this.hostName);
+			}
+
+			jdbcUrl.append(":");
+			jdbcUrl.append(this.port);
+			jdbcUrl.append("/");
+
+			if (this.databaseName != null) {
+				jdbcUrl.append(this.databaseName);
+			}
+
+			jdbcUrlToUse = jdbcUrl.toString();
+		} else {
+			jdbcUrlToUse = this.url;
+		}
+
+		return mysqlDriver.connect(jdbcUrlToUse, props);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,144 @@
+/*
+ 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.jdbc2.optional;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.Name;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+
+import com.mysql.jdbc.NonRegisteringDriver;
+
+/**
+ * Factory class for MysqlDataSource objects
+ * 
+ * @author Mark Matthews
+ */
+public class MysqlDataSourceFactory implements ObjectFactory {
+	/**
+	 * The class name for a standard MySQL DataSource.
+	 */
+	protected final static String DATA_SOURCE_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlDataSource";
+
+	/**
+	 * The class name for a poolable MySQL DataSource.
+	 */
+	protected final static String POOL_DATA_SOURCE_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource";
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param refObj
+	 *            DOCUMENT ME!
+	 * @param nm
+	 *            DOCUMENT ME!
+	 * @param ctx
+	 *            DOCUMENT ME!
+	 * @param env
+	 *            DOCUMENT ME!
+	 * @return DOCUMENT ME!
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public Object getObjectInstance(Object refObj, Name nm, Context ctx,
+			Hashtable env) throws Exception {
+		Reference ref = (Reference) refObj;
+		String className = ref.getClassName();
+
+		if ((className != null)
+				&& (className.equals(DATA_SOURCE_CLASS_NAME) || className
+						.equals(POOL_DATA_SOURCE_CLASS_NAME))) {
+			MysqlDataSource dataSource = null;
+
+			try {
+				dataSource = (MysqlDataSource) Class.forName(className)
+						.newInstance();
+			} catch (Exception ex) {
+				throw new RuntimeException("Unable to create DataSource of "
+						+ "class '" + className + "', reason: " + ex.toString());
+			}
+
+			int portNumber = 3306;
+
+			String portNumberAsString = nullSafeRefAddrStringGet("port", ref);
+			
+			if (portNumberAsString != null) {
+				portNumber = Integer.parseInt(portNumberAsString);
+			}
+
+			dataSource.setPort(portNumber);
+			
+			String user = nullSafeRefAddrStringGet(NonRegisteringDriver.USER_PROPERTY_KEY, ref);
+
+			if (user != null) {
+				dataSource.setUser(user);
+			}
+
+			String password = nullSafeRefAddrStringGet(NonRegisteringDriver.PASSWORD_PROPERTY_KEY, ref);
+
+			if (password != null) {
+				dataSource.setPassword(password);
+			}
+
+			String serverName = nullSafeRefAddrStringGet("serverName", ref);
+
+			if (serverName != null) {
+				dataSource.setServerName(serverName);
+			}
+
+			String databaseName = nullSafeRefAddrStringGet("databaseName", ref);
+
+			if (databaseName != null) {
+				dataSource.setDatabaseName(databaseName);
+			}
+
+			String explicitUrlAsString = nullSafeRefAddrStringGet("explicitUrl", ref);
+
+			if (explicitUrlAsString != null) {
+				if (Boolean.valueOf(explicitUrlAsString).booleanValue()) {
+					dataSource.setUrl(nullSafeRefAddrStringGet("url", ref));
+				}
+			}
+
+			dataSource.setPropertiesViaRef(ref);
+
+			return dataSource;
+		}
+
+		// We can't create an instance of the reference
+		return null;
+	}
+	
+	private String nullSafeRefAddrStringGet(String referenceName, Reference ref) {
+		RefAddr refAddr = ref.get(referenceName);
+		
+		String asString = refAddr != null ? (String)refAddr.getContent() : null;
+		
+		return asString;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,212 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+ 
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+import javax.sql.ConnectionEvent;
+import javax.sql.ConnectionEventListener;
+import javax.sql.PooledConnection;
+
+import com.mysql.jdbc.SQLError;
+
+/**
+ * This class is used to wrap and return a physical connection within a logical
+ * handle. It also registers and notifies ConnectionEventListeners of any
+ * ConnectionEvents
+ * 
+ * @see javax.sql.PooledConnection
+ * @see org.gjt.mm.mysql.jdbc2.optional.LogicalHandle
+ * @author Todd Wolff <todd.wolff_at_prodigy.net>
+ */
+public class MysqlPooledConnection implements PooledConnection {
+
+	/**
+	 * The flag for an exception being thrown.
+	 */
+	public static final int CONNECTION_ERROR_EVENT = 1;
+
+	/**
+	 * The flag for a connection being closed.
+	 */
+	public static final int CONNECTION_CLOSED_EVENT = 2;
+
+	// ~ Instance/static variables .............................................
+
+	private Hashtable eventListeners;
+
+	private Connection logicalHandle;
+
+	private com.mysql.jdbc.Connection physicalConn;
+
+	// ~ Constructors ..........................................................
+
+	/**
+	 * Construct a new MysqlPooledConnection and set instance variables
+	 * 
+	 * @param connection
+	 *            physical connection to db
+	 */
+	public MysqlPooledConnection(com.mysql.jdbc.Connection connection) {
+		this.logicalHandle = null;
+		this.physicalConn = connection;
+		this.eventListeners = new Hashtable(10);
+	}
+
+	// ~ Methods ...............................................................
+
+	/**
+	 * Adds ConnectionEventListeners to a hash table to be used for notification
+	 * of ConnectionEvents
+	 * 
+	 * @param connectioneventlistener
+	 *            listener to be notified with ConnectionEvents
+	 */
+	public synchronized void addConnectionEventListener(
+			ConnectionEventListener connectioneventlistener) {
+
+		if (this.eventListeners != null) {
+			this.eventListeners.put(connectioneventlistener,
+					connectioneventlistener);
+		}
+	}
+
+	/**
+	 * Removes ConnectionEventListeners from hash table used for notification of
+	 * ConnectionEvents
+	 * 
+	 * @param connectioneventlistener
+	 *            listener to be removed
+	 */
+	public synchronized void removeConnectionEventListener(
+			ConnectionEventListener connectioneventlistener) {
+
+		if (this.eventListeners != null) {
+			this.eventListeners.remove(connectioneventlistener);
+		}
+	}
+
+	/**
+	 * Invoked by the container. Return a logicalHandle object that wraps a
+	 * physical connection.
+	 * 
+	 * @see java.sql.DataSource#getConnection()
+	 */
+	public synchronized Connection getConnection() throws SQLException {
+		return getConnection(true, false);
+		
+	}
+	
+	protected synchronized Connection getConnection(boolean resetServerState, 
+			boolean forXa)
+		throws SQLException {
+		if (this.physicalConn == null) {
+
+			SQLException sqlException = SQLError.createSQLException(
+					"Physical Connection doesn't exist");
+			callListener(CONNECTION_ERROR_EVENT, sqlException);
+
+			throw sqlException;
+		}
+
+		try {
+
+			if (this.logicalHandle != null) {
+				((ConnectionWrapper) this.logicalHandle).close(false);
+			}
+
+			if (resetServerState) {
+				((com.mysql.jdbc.Connection) this.physicalConn).resetServerState();
+			}
+
+			this.logicalHandle = new ConnectionWrapper(this, this.physicalConn, forXa);
+		} catch (SQLException sqlException) {
+			callListener(CONNECTION_ERROR_EVENT, sqlException);
+
+			throw sqlException;
+		}
+
+		return this.logicalHandle;
+	}
+
+	/**
+	 * Invoked by the container (not the client), and should close the physical
+	 * connection. This will be called if the pool is destroyed or the
+	 * connectionEventListener receives a connectionErrorOccurred event.
+	 * 
+	 * @see java.sql.DataSource#close()
+	 */
+	public synchronized void close() throws SQLException {
+		if (this.physicalConn != null) {
+			this.physicalConn.close();
+		}
+
+		this.physicalConn = null;
+	}
+
+	/**
+	 * Notifies all registered ConnectionEventListeners of ConnectionEvents.
+	 * Instantiates a new ConnectionEvent which wraps sqlException and invokes
+	 * either connectionClose or connectionErrorOccurred on listener as
+	 * appropriate.
+	 * 
+	 * @param eventType
+	 *            value indicating whether connectionClosed or
+	 *            connectionErrorOccurred called
+	 * @param sqlException
+	 *            the exception being thrown
+	 */
+	protected synchronized void callListener(int eventType,
+			SQLException sqlException) {
+
+		if (this.eventListeners == null) {
+
+			return;
+		}
+
+		Enumeration enumeration = this.eventListeners.keys();
+		ConnectionEvent connectionevent = new ConnectionEvent(this,
+				sqlException);
+
+		while (enumeration.hasMoreElements()) {
+
+			ConnectionEventListener connectioneventlistener = (ConnectionEventListener) enumeration
+					.nextElement();
+			ConnectionEventListener connectioneventlistener1 = (ConnectionEventListener) this.eventListeners
+					.get(connectioneventlistener);
+
+			if (eventType == CONNECTION_CLOSED_EVENT) {
+				connectioneventlistener1.connectionClosed(connectionevent);
+			} else if (eventType == CONNECTION_ERROR_EVENT) {
+				connectioneventlistener1
+						.connectionErrorOccurred(connectionevent);
+			}
+		}
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXAConnection.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,637 @@
+/*
+ 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.jdbc2.optional;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.sql.XAConnection;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.mysql.jdbc.log.Log;
+
+/*
+ * XA BEGIN <xid> [JOIN | RESUME] XA START TRANSACTION <xid> [JOIN | RESUME] XA
+ * COMMIT <xid> [ONE PHASE] XA END <xid> [SUSPEND [FOR MIGRATE]] XA PREPARE
+ * <xid> XA RECOVER XA ROLLBACK <xid>
+ */
+
+/**
+ * An object that provides support for distributed transactions. An
+ * <code>XAConnection</code> object may be enlisted in a distributed
+ * transaction by means of an <code>XAResource</code> object. A transaction
+ * manager, usually part of a middle tier server, manages an
+ * <code>XAConnection</code> object through the <code>XAResource</code>
+ * object.
+ * <P>
+ * An application programmer does not use this interface directly; rather, it is
+ * used by a transaction manager working in the middle tier server.
+ * 
+ * @since 1.4
+ */
+public class MysqlXAConnection extends MysqlPooledConnection implements
+		XAConnection, XAResource {
+
+	private com.mysql.jdbc.Connection underlyingConnection;
+
+	private final static Map MYSQL_ERROR_CODES_TO_XA_ERROR_CODES;
+
+	private Log log;
+
+	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));
+
+		MYSQL_ERROR_CODES_TO_XA_ERROR_CODES = Collections.unmodifiableMap(temp);
+	}
+
+	/**
+	 * @param connection
+	 */
+	public MysqlXAConnection(com.mysql.jdbc.Connection connection)
+			throws SQLException {
+		super(connection);
+		this.underlyingConnection = connection;
+		this.log = connection.getLog();
+	}
+
+	/**
+	 * Retrieves an <code>XAResource</code> object that the transaction
+	 * manager will use to manage this <code>XAConnection</code> object's
+	 * participation in a distributed transaction.
+	 * 
+	 * @return the <code>XAResource</code> object
+	 * @exception SQLException
+	 *                if a database access error occurs
+	 */
+	public XAResource getXAResource() throws SQLException {
+		return this;
+	}
+
+	/**
+	 * Obtains the current transaction timeout value set for this XAResource
+	 * instance. If XAResource.setTransactionTimeout was not used prior to
+	 * invoking this method, the return value is the default timeout set for the
+	 * resource manager; otherwise, the value used in the previous
+	 * setTransactionTimeout call is returned.
+	 * 
+	 * @return the transaction timeout value in seconds.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible exception values are
+	 *             XAER_RMERR and XAER_RMFAIL.
+	 */
+	public int getTransactionTimeout() throws XAException {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	/**
+	 * Sets the current transaction timeout value for this XAResource instance.
+	 * Once set, this timeout value is effective until setTransactionTimeout is
+	 * invoked again with a different value.
+	 * 
+	 * To reset the timeout value to the default value used by the resource
+	 * manager, set the value to zero. If the timeout operation is performed
+	 * successfully, the method returns true; otherwise false.
+	 * 
+	 * If a resource manager does not support explicitly setting the transaction
+	 * timeout value, this method returns false.
+	 * 
+	 * @parameter seconds The transaction timeout value in seconds.
+	 * 
+	 * @return true if the transaction timeout value is set successfully;
+	 *         otherwise false.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible exception values are
+	 *             XAER_RMERR, XAER_RMFAIL, or XAER_INVAL.
+	 */
+	public boolean setTransactionTimeout(int arg0) throws XAException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	/**
+	 * This method is called to determine if the resource manager instance
+	 * represented by the target object is the same as the resouce manager
+	 * instance represented by the parameter xares.
+	 * 
+	 * @parameter xares An XAResource object whose resource manager instance is
+	 *            to be compared with the resource manager instance of the
+	 *            target object.
+	 * 
+	 * @return true if it's the same RM instance; otherwise false.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible exception values are
+	 *             XAER_RMERR and XAER_RMFAIL.
+	 */
+	public boolean isSameRM(XAResource xares) throws XAException {
+
+		if (xares instanceof MysqlXAConnection) {
+			return this.underlyingConnection
+					.isSameResource(((MysqlXAConnection) xares).underlyingConnection);
+		}
+
+		return false;
+	}
+
+	/**
+	 * This method is called to obtain a list of prepared transaction branches
+	 * from a resource manager. The transaction manager calls this method during
+	 * recovery to obtain the list of transaction branches that are currently in
+	 * prepared or heuristically completed states. 
+	 * 
+	 * The flag parameter indicates where the recover scan should start or end, 
+	 * or start and end. This method may be invoked one or more times during a 
+	 * recovery scan. The resource manager maintains a cursor which marks the 
+	 * current position of the prepared or heuristically completed transaction list. 
+	 * Each invocation of the recover method moves the cursor passed the set of Xids 
+	 * that are returned. 
+	 * 
+	 * Two consecutive invocation of this method that starts from the
+	 * beginning of the list must return the same list of transaction branches
+	 * unless one of the following takes place: 
+	 * 
+	 * - the transaction manager invokes the commit, forget, prepare, or rollback method for that resource
+	 * manager, between the two consecutive invocation of the recovery scan. 
+	 * 
+	 * - the resource manager heuristically completes some transaction branches
+	 * between the two invocation of the recovery scan.
+	 * 
+	 * @param flag
+	 *            One of TMSTARTRSCAN, TMENDRSCAN, TMNOFLAGS. TMNOFLAGS must be
+	 *            used when no other flags are set in the parameter.
+	 * 
+	 * @returns The resource manager returns zero or more XIDs of the
+	 *          transaction branches that are currently in a prepared or
+	 *          heuristically completed state. If an error occurs during the
+	 *          operation, the resource manager should throw the appropriate
+	 *          XAException.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible values are XAER_RMERR,
+	 *             XAER_RMFAIL, XAER_INVAL, and XAER_PROTO.
+	 */
+	public Xid[] recover(int flag) throws XAException {
+		return recover(this.underlyingConnection, flag);
+	}
+	
+	protected static Xid[] recover(Connection c, int flag) throws XAException {
+		/*
+		    The XA RECOVER statement returns information for those XA transactions on the MySQL server that are in the PREPARED state. (See Section 13.4.7.2, “XA Transaction States”.) The output includes a row for each such XA transaction on the server, regardless of which client started it.
+
+			XA RECOVER output rows look like this (for an example xid value consisting of the parts 'abc', 'def', and 7):
+
+			mysql> XA RECOVER;
+			+----------+--------------+--------------+--------+
+			| formatID | gtrid_length | bqual_length | data   |
+			+----------+--------------+--------------+--------+
+			|        7 |            3 |            3 | abcdef |
+			+----------+--------------+--------------+--------+
+
+			The output columns have the following meanings:
+
+      			formatID is the formatID part of the transaction xid
+			    gtrid_length is the length in bytes of the gtrid part of the xid
+			    bqual_length is the length in bytes of the bqual part of the xid
+ 			    data is the concatenation of the gtrid and bqual parts of the xid
+		 */
+		
+		boolean startRscan = ((flag & TMSTARTRSCAN) > 0);
+		boolean endRscan = ((flag & TMENDRSCAN) > 0);
+		
+		if (!startRscan && !endRscan && flag != TMNOFLAGS) {
+			throw new MysqlXAException(XAException.XAER_INVAL, 
+					"Invalid flag, must use TMNOFLAGS, or any combination of TMSTARTRSCAN and TMENDRSCAN",
+					null);
+		}
+
+		//
+		// We return all recovered XIDs at once, so if not 
+		// TMSTARTRSCAN, return no new XIDs
+		//
+		// We don't attempt to maintain state to check for TMNOFLAGS
+		// "outside" of a scan
+		//
+		
+		if (!startRscan) {
+			return new Xid[0];
+		}
+		
+		ResultSet rs = null;
+		Statement stmt = null;
+
+		List recoveredXidList = new ArrayList();
+
+		try {
+			// TODO: Cache this for lifetime of XAConnection
+			stmt = c.createStatement();
+
+			rs = stmt.executeQuery("XA RECOVER");
+
+			while (rs.next()) {
+				final int formatId = rs.getInt(1);
+				int gtridLength = rs.getInt(2);
+				int bqualLength = rs.getInt(3);
+				byte[] gtridAndBqual = rs.getBytes(4);
+
+				final byte[] gtrid = new byte[gtridLength];
+				final byte[] bqual = new byte[bqualLength];
+
+				if (gtridAndBqual.length != (gtridLength + bqualLength)) {
+					throw new MysqlXAException(XAException.XA_RBPROTO,
+							"Error while recovering XIDs from RM. GTRID and BQUAL are wrong sizes", 
+							null);
+				}
+
+				System.arraycopy(gtridAndBqual, 0, gtrid, 0,
+						gtridLength);
+				System.arraycopy(gtridAndBqual, gtridLength, bqual, 0,
+						bqualLength);
+
+				recoveredXidList.add(new MysqlXid(gtrid, bqual, 
+						formatId));
+			}
+		} catch (SQLException sqlEx) {
+			throw mapXAExceptionFromSQLException(sqlEx);
+		} finally {
+			if (rs != null) {
+				try {
+					rs.close();
+				} catch (SQLException sqlEx) {
+					throw mapXAExceptionFromSQLException(sqlEx);
+				}
+			}
+
+			if (stmt != null) {
+				try {
+					stmt.close();
+				} catch (SQLException sqlEx) {
+					throw mapXAExceptionFromSQLException(sqlEx);
+				}
+			}
+		}
+
+		int numXids = recoveredXidList.size();
+
+		Xid[] asXids = new Xid[numXids];
+		Object[] asObjects = recoveredXidList.toArray();
+
+		for (int i = 0; i < numXids; i++) {
+			asXids[i] = (Xid) asObjects[i];
+		}
+
+		return asXids;
+	}
+
+	/**
+	 * Asks the resource manager to prepare for a transaction commit of the
+	 * transaction specified in xid.
+	 * 
+	 * @parameter xid A global transaction identifier.
+	 * 
+	 * @returns A value indicating the resource manager's vote on the outcome of
+	 *          the transaction.
+	 * 
+	 * The possible values are: XA_RDONLY or XA_OK. If the resource manager
+	 * wants to roll back the transaction, it should do so by raising an
+	 * appropriate XAException in the prepare method.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible exception values are: XA_RB*,
+	 *             XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
+	 *             XAER_PROTO.
+	 */
+	public int prepare(Xid xid) throws XAException {
+		StringBuffer commandBuf = new StringBuffer();
+		commandBuf.append("XA PREPARE ");
+		commandBuf.append(xidToString(xid));
+
+		dispatchCommand(commandBuf.toString());
+
+		return XA_OK; // TODO: Check for read-only
+	}
+
+	/**
+	 * Tells the resource manager to forget about a heuristically completed
+	 * transaction branch.
+	 * 
+	 * @parameter xid A global transaction identifier.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible exception values are
+	 *             XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, or
+	 *             XAER_PROTO.
+	 */
+	public void forget(Xid xid) throws XAException {
+		// TODO Auto-generated method stub
+	}
+
+	/**
+	 * Informs the resource manager to roll back work done on behalf of a
+	 * transaction branch.
+	 * 
+	 * @parameter xid A global transaction identifier.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible XAExceptions are XA_HEURHAZ,
+	 *             XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL,
+	 *             XAER_NOTA, XAER_INVAL, or XAER_PROTO.
+	 * 
+	 * If the transaction branch is already marked rollback-only the resource
+	 * manager may throw one of the XA_RB* exceptions.
+	 * 
+	 * Upon return, the resource manager has rolled back the branch's work and
+	 * has released all held resources.
+	 */
+	public void rollback(Xid xid) throws XAException {
+		StringBuffer commandBuf = new StringBuffer();
+		commandBuf.append("XA ROLLBACK ");
+		commandBuf.append(xidToString(xid));
+
+		try {
+			dispatchCommand(commandBuf.toString());
+		} finally {
+			this.underlyingConnection.setInGlobalTx(false);
+		}
+	}
+
+	/**
+	 * Ends the work performed on behalf of a transaction branch.
+	 * 
+	 * The resource manager disassociates the XA resource from the transaction
+	 * branch specified and lets the transaction complete.
+	 * 
+	 * If TMSUSPEND is specified in the flags, the transaction branch is
+	 * temporarily suspended in an incomplete state. The transaction context is
+	 * in a suspended state and must be resumed via the start method with
+	 * TMRESUME specified.
+	 * 
+	 * If TMFAIL is specified, the portion of work has failed. The resource
+	 * manager may mark the transaction as rollback-only
+	 * 
+	 * If TMSUCCESS is specified, the portion of work has completed
+	 * successfully.
+	 * 
+	 * @parameter xid A global transaction identifier that is the same as the
+	 *            identifier used previously in the start method.
+	 * 
+	 * @parameter flags One of TMSUCCESS, TMFAIL, or TMSUSPEND.
+	 * 
+	 * @throws XAException -
+	 *             An error has occurred. Possible XAException values are
+	 *             XAER_RMERR, XAER_RMFAIL, XAER_NOTA, XAER_INVAL, XAER_PROTO,
+	 *             or XA_RB*.
+	 */
+	public void end(Xid xid, int flags) throws XAException {
+		StringBuffer commandBuf = new StringBuffer();
+		commandBuf.append("XA END ");
+		commandBuf.append(xidToString(xid));
+
+		switch (flags) {
+		case TMSUCCESS:
+			break; // no-op
+		case TMSUSPEND:
+			commandBuf.append(" SUSPEND");
+			break;
+		case TMFAIL:
+			break; // no-op
+		default:
+			throw new XAException(XAException.XAER_INVAL);
+		}
+
+		dispatchCommand(commandBuf.toString());
+	}
+
+	/**
+	 * Starts work on behalf of a transaction branch specified in xid.
+	 * 
+	 * If TMJOIN is specified, the start applies to joining a transaction
+	 * previously seen by the resource manager.
+	 * 
+	 * If TMRESUME is specified, the start applies to resuming a suspended
+	 * transaction specified in the parameter xid.
+	 * 
+	 * If neither TMJOIN nor TMRESUME is specified and the transaction specified
+	 * by xid has previously been seen by the resource manager, the resource
+	 * manager throws the XAException exception with XAER_DUPID error code.
+	 * 
+	 * @parameter xid A global transaction identifier to be associated with the
+	 *            resource.
+	 * 
+	 * @parameter flags One of TMNOFLAGS, TMJOIN, or TMRESUME.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible exceptions are XA_RB*,
+	 *             XAER_RMERR, XAER_RMFAIL, XAER_DUPID, XAER_OUTSIDE, XAER_NOTA,
+	 *             XAER_INVAL, or XAER_PROTO.
+	 */
+	public void start(Xid xid, int flags) throws XAException {
+		StringBuffer commandBuf = new StringBuffer();
+		commandBuf.append("XA START ");
+		commandBuf.append(xidToString(xid));
+
+		switch (flags) {
+		case TMJOIN:
+			commandBuf.append(" JOIN");
+			break;
+		case TMRESUME:
+			commandBuf.append(" RESUME");
+			break;
+		case TMNOFLAGS:
+			// no-op
+			break;
+		default:
+			throw new XAException(XAException.XAER_INVAL);
+		}
+
+		dispatchCommand(commandBuf.toString());
+		
+		this.underlyingConnection.setInGlobalTx(true);
+	}
+
+	/**
+	 * Commits the global transaction specified by xid.
+	 * 
+	 * @parameter xid A global transaction identifier
+	 * @parameter onePhase - If true, the resource manager should use a
+	 *            one-phase commit protocol to commit the work done on behalf of
+	 *            xid.
+	 * 
+	 * @throws XAException
+	 *             An error has occurred. Possible XAExceptions are XA_HEURHAZ,
+	 *             XA_HEURCOM, XA_HEURRB, XA_HEURMIX, XAER_RMERR, XAER_RMFAIL,
+	 *             XAER_NOTA, XAER_INVAL, or XAER_PROTO.
+	 * 
+	 * If the resource manager did not commit the transaction and the parameter
+	 * onePhase is set to true, the resource manager may throw one of the XA_RB*
+	 * exceptions.
+	 * 
+	 * Upon return, the resource manager has rolled back the branch's work and
+	 * has released all held resources.
+	 */
+
+	public void commit(Xid xid, boolean onePhase) throws XAException {
+		StringBuffer commandBuf = new StringBuffer();
+		commandBuf.append("XA COMMIT ");
+		commandBuf.append(xidToString(xid));
+
+		if (onePhase) {
+			commandBuf.append(" ONE PHASE");
+		}
+
+		try {
+			dispatchCommand(commandBuf.toString());
+		} finally {
+			this.underlyingConnection.setInGlobalTx(false);
+		}
+	}
+
+	private ResultSet dispatchCommand(String command) throws XAException {
+		Statement stmt = null;
+
+		try {
+			if (this.log.isDebugEnabled()) {
+				this.log.logDebug("Executing XA statement: " + command);
+			}
+
+			// TODO: Cache this for lifetime of XAConnection
+			stmt = this.underlyingConnection.createStatement();
+			
+			
+			stmt.execute(command);
+
+			ResultSet rs = stmt.getResultSet();
+			
+			return rs;
+		} catch (SQLException sqlEx) {
+			throw mapXAExceptionFromSQLException(sqlEx);
+		} finally {
+			if (stmt != null) {
+				try {
+					stmt.close();
+				} catch (SQLException sqlEx) {
+				}
+			}
+		}
+	}
+
+	protected static XAException mapXAExceptionFromSQLException(SQLException sqlEx) {
+
+		Integer xaCode = (Integer) MYSQL_ERROR_CODES_TO_XA_ERROR_CODES
+				.get(new Integer(sqlEx.getErrorCode()));
+
+		if (xaCode != null) {
+			return new MysqlXAException(xaCode.intValue(), sqlEx.getMessage(), null);
+		}
+
+		// Punt? We don't know what the error code is here
+		return new MysqlXAException(sqlEx.getMessage(), null);
+	}
+
+	private static String xidToString(Xid xid) {
+		byte[] gtrid = xid.getGlobalTransactionId();
+
+		byte[] btrid = xid.getBranchQualifier();
+
+		int lengthAsString = 6; // for (0x and ,) * 2
+
+		if (gtrid != null) {
+			lengthAsString += (2 * gtrid.length);
+		}
+
+		if (btrid != null) {
+			lengthAsString += (2 * btrid.length);
+		}
+
+		String formatIdInHex = Integer.toHexString(xid.getFormatId());
+
+		lengthAsString += formatIdInHex.length();
+		lengthAsString += 3; // for the '.' after formatId
+
+		StringBuffer asString = new StringBuffer(lengthAsString);
+
+		asString.append("0x");
+
+		if (gtrid != null) {
+			for (int i = 0; i < gtrid.length; i++) {
+				String asHex = Integer.toHexString(gtrid[i] & 0xff);
+
+				if (asHex.length() == 1) {
+					asString.append("0");
+				}
+
+				asString.append(asHex);
+			}
+		}
+
+		asString.append(",");
+
+		if (btrid != null) {
+			asString.append("0x");
+
+			for (int i = 0; i < btrid.length; i++) {
+				String asHex = Integer.toHexString(btrid[i] & 0xff);
+
+				if (asHex.length() == 1) {
+					asString.append("0");
+				}
+
+				asString.append(asHex);
+			}
+		}
+
+		asString.append(",0x");
+		asString.append(formatIdInHex);
+
+		return asString.toString();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see javax.sql.PooledConnection#getConnection()
+	 */
+	public synchronized Connection getConnection() throws SQLException {
+		Connection connToWrap = getConnection(false, true);
+		
+		return connToWrap;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,75 @@
+/*
+ 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.jdbc2.optional;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import javax.sql.XAConnection;
+
+/**
+ * @author mmatthew
+ * 
+ * To change this generated comment edit the template variable "typecomment":
+ * Window>Preferences>Java>Templates. To enable and disable the creation of type
+ * comments go to Window>Preferences>Java>Code Generation.
+ */
+public class MysqlXADataSource extends MysqlDataSource implements
+		javax.sql.XADataSource {
+
+	/**
+	 * @see javax.sql.XADataSource#getXAConnection()
+	 */
+	public XAConnection getXAConnection() throws SQLException {
+
+		Connection conn = getConnection();
+
+		return wrapConnection(conn);
+	}
+
+	/**
+	 * @see javax.sql.XADataSource#getXAConnection(String, String)
+	 */
+	public XAConnection getXAConnection(String user, String password)
+			throws SQLException {
+
+		Connection conn = getConnection(user, password);
+
+		return wrapConnection(conn);
+	}
+
+	/**
+	 * Wraps a connection as a 'fake' XAConnection
+	 */
+
+	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 MysqlXAConnection((com.mysql.jdbc.Connection) conn);
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,66 @@
+/*
+ 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.jdbc2.optional;
+
+import javax.transaction.xa.XAException;
+
+/**
+ * The stock XAException class isn't too friendly (i.e. no
+ * error messages), so we extend it a bit.
+ */
+class MysqlXAException extends XAException {
+	private static final long serialVersionUID = -9075817535836563004L;
+	
+	private String message;
+	private String xidAsString;
+	
+	public MysqlXAException(int errorCode, String message, String xidAsString) {
+		super(errorCode);
+		this.message = message;
+		this.xidAsString = xidAsString;
+	}
+	
+	public MysqlXAException(String message, String xidAsString) {
+		super();
+		
+		this.message = message;
+		this.xidAsString = xidAsString;
+	}
+
+	public String getMessage() {
+		String superMessage = super.getMessage();
+		StringBuffer returnedMessage = new StringBuffer();
+		
+		if (superMessage != null) {
+			returnedMessage.append(superMessage);
+			returnedMessage.append(":");
+		}
+		
+		if (this.message != null) {
+			returnedMessage.append(this.message);
+		}
+		
+		return returnedMessage.toString();
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,112 @@
+/*
+ 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.jdbc2.optional;
+
+import javax.transaction.xa.Xid;
+
+/**
+ * Implementation of the XID interface for MySQL XA
+ * 
+ * @version $Id: $
+ */
+public class MysqlXid implements Xid {
+
+	int hash = 0;
+
+	byte[] myBqual;
+
+	int myFormatId;
+
+	byte[] myGtrid;
+
+	public MysqlXid(byte[] gtrid, byte[] bqual, int formatId) {
+		this.myGtrid = gtrid;
+		this.myBqual = bqual;
+		this.myFormatId = formatId;
+	}
+
+	public boolean equals(Object another) {
+
+		if (another instanceof Xid) {
+			Xid anotherAsXid = (Xid) another;
+
+			if (this.myFormatId != anotherAsXid.getFormatId()) {
+				return false;
+			}
+
+			byte[] otherBqual = anotherAsXid.getBranchQualifier();
+			byte[] otherGtrid = anotherAsXid.getGlobalTransactionId();
+
+			if (otherGtrid != null && otherGtrid.length == this.myGtrid.length) {
+				int length = otherGtrid.length;
+
+				for (int i = 0; i < length; i++) {
+					if (otherGtrid[i] != this.myGtrid[i]) {
+						return false;
+					}
+				}
+
+				if (otherBqual != null && otherBqual.length == myBqual.length) {
+					length = otherBqual.length;
+
+					for (int i = 0; i < length; i++) {
+						if (otherBqual[i] != this.myBqual[i]) {
+							return false;
+						}
+					}
+				} else {
+					return false;
+				}
+
+				return true;
+			} else {
+				return false;
+			}
+		} else {
+			return false;
+		}
+	}
+
+	public byte[] getBranchQualifier() {
+		return this.myBqual;
+	}
+
+	public int getFormatId() {
+		return this.myFormatId;
+	};
+
+	public byte[] getGlobalTransactionId() {
+		return this.myGtrid;
+	}
+
+	public synchronized int hashCode() {
+		if (this.hash == 0) {
+			for (int i = 0; i < this.myGtrid.length; i++) {
+				this.hash = 33 * this.hash + this.myGtrid[i];
+			}
+		}
+
+		return this.hash;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,854 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import com.mysql.jdbc.SQLError;
+
+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.ParameterMetaData;
+import java.sql.PreparedStatement;
+import java.sql.Ref;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import java.util.Calendar;
+
+/**
+ * Wraps prepared statements so that errors can be reported correctly to
+ * ConnectionEventListeners.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: PreparedStatementWrapper.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class PreparedStatementWrapper extends StatementWrapper implements
+		PreparedStatement {
+	PreparedStatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn,
+			PreparedStatement toWrap) {
+		super(c, conn, toWrap);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setArray(int, java.sql.Array)
+	 */
+	public void setArray(int parameterIndex, Array x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setArray(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setAsciiStream(int, java.io.InputStream,
+	 *      int)
+	 */
+	public void setAsciiStream(int parameterIndex, InputStream x, int 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);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setBigDecimal(int, java.math.BigDecimal)
+	 */
+	public void setBigDecimal(int parameterIndex, BigDecimal x)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBigDecimal(
+						parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setBinaryStream(int, java.io.InputStream,
+	 *      int)
+	 */
+	public void setBinaryStream(int parameterIndex, InputStream x, int 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);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setBlob(int, java.sql.Blob)
+	 */
+	public void setBlob(int parameterIndex, Blob x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBlob(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setBoolean(int, boolean)
+	 */
+	public void setBoolean(int parameterIndex, boolean x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBoolean(
+						parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setByte(int, byte)
+	 */
+	public void setByte(int parameterIndex, byte x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setByte(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setBytes(int, byte[])
+	 */
+	public void setBytes(int parameterIndex, byte[] x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setBytes(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setCharacterStream(int, java.io.Reader,
+	 *      int)
+	 */
+	public void setCharacterStream(int parameterIndex, Reader reader, int 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);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setClob(int, java.sql.Clob)
+	 */
+	public void setClob(int parameterIndex, Clob x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setClob(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setDate(int, java.sql.Date)
+	 */
+	public void setDate(int parameterIndex, Date x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setDate(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setDate(int, java.sql.Date,
+	 *      java.util.Calendar)
+	 */
+	public void setDate(int parameterIndex, Date x, Calendar cal)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setDate(parameterIndex,
+						x, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setDouble(int, double)
+	 */
+	public void setDouble(int parameterIndex, double x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setDouble(
+						parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setFloat(int, float)
+	 */
+	public void setFloat(int parameterIndex, float x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setFloat(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setInt(int, int)
+	 */
+	public void setInt(int parameterIndex, int x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt)
+						.setInt(parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setLong(int, long)
+	 */
+	public void setLong(int parameterIndex, long x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setLong(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#getMetaData()
+	 */
+	public ResultSetMetaData getMetaData() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((PreparedStatement) this.wrappedStmt).getMetaData();
+			}
+
+			throw SQLError.createSQLException(
+					"No operations allowed after statement closed",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setNull(int, int)
+	 */
+	public void setNull(int parameterIndex, int sqlType) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNull(parameterIndex,
+						sqlType);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setNull(int, int, java.lang.String)
+	 */
+	public void setNull(int parameterIndex, int sqlType, String typeName)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setNull(parameterIndex,
+						sqlType, typeName);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setObject(int, java.lang.Object)
+	 */
+	public void setObject(int parameterIndex, Object x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setObject(
+						parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setObject(int, java.lang.Object, int)
+	 */
+	public void setObject(int parameterIndex, Object x, int targetSqlType)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setObject(
+						parameterIndex, x, targetSqlType);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setObject(int, java.lang.Object, int,
+	 *      int)
+	 */
+	public void setObject(int parameterIndex, Object x, int targetSqlType,
+			int scale) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setObject(
+						parameterIndex, x, scale);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#getParameterMetaData()
+	 */
+	public ParameterMetaData getParameterMetaData() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((PreparedStatement) this.wrappedStmt)
+						.getParameterMetaData();
+			}
+
+			throw SQLError.createSQLException(
+					"No operations allowed after statement closed",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setRef(int, java.sql.Ref)
+	 */
+	public void setRef(int parameterIndex, Ref x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt)
+						.setRef(parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setShort(int, short)
+	 */
+	public void setShort(int parameterIndex, short x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setShort(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setString(int, java.lang.String)
+	 */
+	public void setString(int parameterIndex, String x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setString(
+						parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setTime(int, java.sql.Time)
+	 */
+	public void setTime(int parameterIndex, Time x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setTime(parameterIndex,
+						x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setTime(int, java.sql.Time,
+	 *      java.util.Calendar)
+	 */
+	public void setTime(int parameterIndex, Time x, Calendar cal)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setTime(parameterIndex,
+						x, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setTimestamp(int, java.sql.Timestamp)
+	 */
+	public void setTimestamp(int parameterIndex, Timestamp x)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setTimestamp(
+						parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setTimestamp(int, java.sql.Timestamp,
+	 *      java.util.Calendar)
+	 */
+	public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setTimestamp(
+						parameterIndex, x, cal);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#setURL(int, java.net.URL)
+	 */
+	public void setURL(int parameterIndex, URL x) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt)
+						.setURL(parameterIndex, x);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param parameterIndex
+	 *            DOCUMENT ME!
+	 * @param x
+	 *            DOCUMENT ME!
+	 * @param length
+	 *            DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 * 
+	 * @see java.sql.PreparedStatement#setUnicodeStream(int,
+	 *      java.io.InputStream, int)
+	 * @deprecated
+	 */
+	public void setUnicodeStream(int parameterIndex, InputStream x, int length)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).setUnicodeStream(
+						parameterIndex, x, length);
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#addBatch()
+	 */
+	public void addBatch() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).addBatch();
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#clearParameters()
+	 */
+	public void clearParameters() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((PreparedStatement) this.wrappedStmt).clearParameters();
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#execute()
+	 */
+	public boolean execute() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((PreparedStatement) this.wrappedStmt).execute();
+			}
+
+			throw SQLError.createSQLException(
+					"No operations allowed after statement closed",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#executeQuery()
+	 */
+	public ResultSet executeQuery() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				ResultSet rs = ((PreparedStatement) this.wrappedStmt)
+						.executeQuery();
+
+				((com.mysql.jdbc.ResultSet) rs).setWrapperStatement(this);
+
+				return rs;
+			}
+
+			throw SQLError.createSQLException(
+					"No operations allowed after statement closed",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.PreparedStatement#executeUpdate()
+	 */
+	public int executeUpdate() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return ((PreparedStatement) this.wrappedStmt).executeUpdate();
+			}
+
+			throw SQLError.createSQLException(
+					"No operations allowed after statement closed",
+					SQLError.SQL_STATE_GENERAL_ERROR);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return -1; // we actually never get here, but the compiler can't figure
+
+		// that out
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,829 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import com.mysql.jdbc.SQLError;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.Statement;
+
+/**
+ * Wraps statements so that errors can be reported correctly to
+ * ConnectionEventListeners.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: StatementWrapper.java,v 1.1.2.1 2005/05/13 18:58:38 mmatthews
+ *          Exp $
+ */
+public class StatementWrapper extends WrapperBase implements Statement {
+	protected Statement wrappedStmt;
+
+	protected ConnectionWrapper wrappedConn;
+
+	protected StatementWrapper(ConnectionWrapper c, MysqlPooledConnection conn,
+			Statement toWrap) {
+		this.pooledConnection = conn;
+		this.wrappedStmt = toWrap;
+		this.wrappedConn = c;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getConnection()
+	 */
+	public Connection getConnection() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedConn;
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#setCursorName(java.lang.String)
+	 */
+	public void setCursorName(String name) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setCursorName(name);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#setEscapeProcessing(boolean)
+	 */
+	public void setEscapeProcessing(boolean enable) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setEscapeProcessing(enable);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#setFetchDirection(int)
+	 */
+	public void setFetchDirection(int direction) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setFetchDirection(direction);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getFetchDirection()
+	 */
+	public int getFetchDirection() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getFetchDirection();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return ResultSet.FETCH_FORWARD; // we actually never get here, but the
+										// compiler can't figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#setFetchSize(int)
+	 */
+	public void setFetchSize(int rows) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setFetchSize(rows);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getFetchSize()
+	 */
+	public int getFetchSize() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getFetchSize();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0; // we actually never get here, but the compiler can't figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getGeneratedKeys()
+	 */
+	public ResultSet getGeneratedKeys() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getGeneratedKeys();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#setMaxFieldSize(int)
+	 */
+	public void setMaxFieldSize(int max) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setMaxFieldSize(max);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getMaxFieldSize()
+	 */
+	public int getMaxFieldSize() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getMaxFieldSize();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0; // we actually never get here, but the compiler can't figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#setMaxRows(int)
+	 */
+	public void setMaxRows(int max) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setMaxRows(max);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getMaxRows()
+	 */
+	public int getMaxRows() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getMaxRows();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0; // we actually never get here, but the compiler can't figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getMoreResults()
+	 */
+	public boolean getMoreResults() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getMoreResults();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getMoreResults(int)
+	 */
+	public boolean getMoreResults(int current) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getMoreResults(current);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#setQueryTimeout(int)
+	 */
+	public void setQueryTimeout(int seconds) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.setQueryTimeout(seconds);
+			} else {
+				throw SQLError.createSQLException("Statement already closed",
+						SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getQueryTimeout()
+	 */
+	public int getQueryTimeout() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getQueryTimeout();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getResultSet()
+	 */
+	public ResultSet getResultSet() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				ResultSet rs = this.wrappedStmt.getResultSet();
+
+				((com.mysql.jdbc.ResultSet) rs).setWrapperStatement(this);
+
+				return rs;
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getResultSetConcurrency()
+	 */
+	public int getResultSetConcurrency() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getResultSetConcurrency();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return 0;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getResultSetHoldability()
+	 */
+	public int getResultSetHoldability() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getResultSetHoldability();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return Statement.CLOSE_CURRENT_RESULT;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getResultSetType()
+	 */
+	public int getResultSetType() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getResultSetType();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return ResultSet.TYPE_FORWARD_ONLY;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getUpdateCount()
+	 */
+	public int getUpdateCount() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getUpdateCount();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return -1;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#getWarnings()
+	 */
+	public SQLWarning getWarnings() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.getWarnings();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#addBatch(java.lang.String)
+	 */
+	public void addBatch(String sql) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.addBatch(sql);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#cancel()
+	 */
+	public void cancel() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.cancel();
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#clearBatch()
+	 */
+	public void clearBatch() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.clearBatch();
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#clearWarnings()
+	 */
+	public void clearWarnings() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.clearWarnings();
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#close()
+	 */
+	public void close() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				this.wrappedStmt.close();
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		} finally {
+			this.wrappedStmt = null;
+			this.pooledConnection = null;
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#execute(java.lang.String, int)
+	 */
+	public boolean execute(String sql, int autoGeneratedKeys)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.execute(sql, autoGeneratedKeys);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#execute(java.lang.String, int[])
+	 */
+	public boolean execute(String sql, int[] columnIndexes) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.execute(sql, columnIndexes);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#execute(java.lang.String, java.lang.String[])
+	 */
+	public boolean execute(String sql, String[] columnNames)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.execute(sql, columnNames);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#execute(java.lang.String)
+	 */
+	public boolean execute(String sql) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.execute(sql);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return false; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#executeBatch()
+	 */
+	public int[] executeBatch() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.executeBatch();
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#executeQuery(java.lang.String)
+	 */
+	public ResultSet executeQuery(String sql) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+
+				ResultSet rs = this.wrappedStmt.executeQuery(sql);
+				((com.mysql.jdbc.ResultSet) rs).setWrapperStatement(this);
+
+				return rs;
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return null; // we actually never get here, but the compiler can't
+						// figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#executeUpdate(java.lang.String, int)
+	 */
+	public int executeUpdate(String sql, int autoGeneratedKeys)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.executeUpdate(sql, autoGeneratedKeys);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return -1; // we actually never get here, but the compiler can't figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#executeUpdate(java.lang.String, int[])
+	 */
+	public int executeUpdate(String sql, int[] columnIndexes)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.executeUpdate(sql, columnIndexes);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return -1; // we actually never get here, but the compiler can't figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#executeUpdate(java.lang.String,
+	 *      java.lang.String[])
+	 */
+	public int executeUpdate(String sql, String[] columnNames)
+			throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.executeUpdate(sql, columnNames);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return -1; // we actually never get here, but the compiler can't figure
+
+		// that out
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.sql.Statement#executeUpdate(java.lang.String)
+	 */
+	public int executeUpdate(String sql) throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				return this.wrappedStmt.executeUpdate(sql);
+			}
+
+			throw SQLError.createSQLException("Statement already closed",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+
+		return -1; // we actually never get here, but the compiler can't figure
+
+		// that out
+	}
+
+	public void enableStreamingResults() throws SQLException {
+		try {
+			if (this.wrappedStmt != null) {
+				((com.mysql.jdbc.Statement) this.wrappedStmt)
+						.enableStreamingResults();
+			} else {
+				throw SQLError.createSQLException(
+						"No operations allowed after statement closed",
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+		} catch (SQLException sqlEx) {
+			checkAndFireConnectionError(sqlEx);
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,153 @@
+package com.mysql.jdbc.jdbc2.optional;
+
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.sql.XAConnection;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.mysql.jdbc.Connection;
+
+public class SuspendableXAConnection extends MysqlPooledConnection implements
+XAConnection, XAResource {
+
+	public SuspendableXAConnection(Connection connection) {
+		super(connection);
+		this.underlyingConnection = connection;
+	}
+
+	private static final Map XIDS_TO_PHYSICAL_CONNECTIONS = 
+		new HashMap();
+	
+	private Xid currentXid;
+	
+	private XAConnection currentXAConnection;
+	private XAResource currentXAResource;
+	
+	private Connection underlyingConnection;
+	
+	private static synchronized XAConnection findConnectionForXid(Connection connectionToWrap, Xid xid) 
+		throws SQLException {
+		// TODO: check for same GTRID, but different BQUALs...MySQL doesn't allow this yet
+		
+		// Note, we don't need to check for XIDs here, because MySQL itself will complain
+		// with a XAER_NOTA if need be.
+		
+		XAConnection conn = (XAConnection)XIDS_TO_PHYSICAL_CONNECTIONS.get(xid);
+
+		if (conn == null) {
+			conn = new MysqlXAConnection(connectionToWrap);
+		}
+		
+		return conn;
+	}
+	
+	private static synchronized void removeXAConnectionMapping(Xid xid) {
+		XIDS_TO_PHYSICAL_CONNECTIONS.remove(xid);
+	}
+	
+	private synchronized void switchToXid(Xid xid) throws XAException {
+		if (xid == null) {
+			throw new XAException();
+		}
+		
+		try {
+			if (!xid.equals(this.currentXid)) {
+				XAConnection toSwitchTo = findConnectionForXid(this.underlyingConnection, xid);
+				this.currentXAConnection = toSwitchTo;
+				this.currentXid = xid;
+				this.currentXAResource = toSwitchTo.getXAResource();
+			}
+		} catch (SQLException sqlEx) {
+			throw new XAException();
+		}
+	}
+	
+	public XAResource getXAResource() throws SQLException {
+		return this;
+	}
+
+	public void commit(Xid xid, boolean arg1) throws XAException {
+		switchToXid(xid);
+		this.currentXAResource.commit(xid, arg1);
+		removeXAConnectionMapping(xid);
+	}
+
+	public void end(Xid xid, int arg1) throws XAException {
+		switchToXid(xid);
+		this.currentXAResource.end(xid, arg1);
+	}
+
+	public void forget(Xid xid) throws XAException {
+		switchToXid(xid);
+		this.currentXAResource.forget(xid);
+		// remove?
+		removeXAConnectionMapping(xid);
+	}
+
+	public int getTransactionTimeout() throws XAException {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public boolean isSameRM(XAResource xaRes) throws XAException {
+		return xaRes == this;
+	}
+
+	public int prepare(Xid xid) throws XAException {
+		switchToXid(xid);
+		return this.currentXAResource.prepare(xid);
+	}
+
+	public Xid[] recover(int flag) throws XAException {
+		return MysqlXAConnection.recover(this.underlyingConnection, flag);
+	}
+
+	public void rollback(Xid xid) throws XAException {
+		switchToXid(xid);
+		this.currentXAResource.rollback(xid);
+		removeXAConnectionMapping(xid);
+	}
+
+	public boolean setTransactionTimeout(int arg0) throws XAException {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public void start(Xid xid, int arg1) throws XAException {
+		switchToXid(xid);
+		
+		if (arg1 != XAResource.TMJOIN) {
+			this.currentXAResource.start(xid, arg1);
+			
+			return;
+		}
+		
+		//
+		// Emulate join, by using resume on the same physical connection
+		//
+		
+		this.currentXAResource.start(xid, XAResource.TMRESUME);
+	}
+
+	public synchronized java.sql.Connection getConnection() throws SQLException {
+		if (this.currentXAConnection == null) {
+			return getConnection(false, true);
+		}
+			
+		return this.currentXAConnection.getConnection();
+	}
+
+	public void close() throws SQLException {
+		if (this.currentXAConnection == null) {
+			super.close();
+		} else {
+			removeXAConnectionMapping(this.currentXid);
+			this.currentXAConnection.close();
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,61 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+ 
+ */
+package com.mysql.jdbc.jdbc2.optional;
+
+import java.sql.SQLException;
+
+import com.mysql.jdbc.SQLError;
+
+/**
+ * Base class for all wrapped instances created by LogicalHandle
+ * 
+ * @author Mark matthews
+ * 
+ * @version $Id: WrapperBase.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+abstract class WrapperBase {
+	protected MysqlPooledConnection pooledConnection;
+
+	/**
+	 * Fires connection error event if required, before re-throwing exception
+	 * 
+	 * @param sqlEx
+	 *            the SQLException that has ocurred
+	 * @throws SQLException
+	 *             (rethrown)
+	 */
+	protected void checkAndFireConnectionError(SQLException sqlEx)
+			throws SQLException {
+		if (this.pooledConnection != null) {
+			if (SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE.equals(sqlEx
+					.getSQLState())) {
+				this.pooledConnection.callListener(
+						MysqlPooledConnection.CONNECTION_ERROR_EVENT, sqlEx);
+			}
+		}
+
+		throw sqlEx;
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/CommonsLogger.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/CommonsLogger.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/CommonsLogger.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,108 @@
+/*
+ Copyright (C) 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.log;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+public class CommonsLogger implements com.mysql.jdbc.log.Log {
+	private Log logger;
+	
+	public CommonsLogger(String instanceName) {
+		logger = LogFactory.getLog(instanceName);
+	}
+
+	public boolean isDebugEnabled() {
+		return this.logger.isInfoEnabled();
+	}
+
+	public boolean isErrorEnabled() {
+		return this.logger.isErrorEnabled();
+	}
+
+	public boolean isFatalEnabled() {
+		return this.logger.isFatalEnabled();
+	}
+
+	public boolean isInfoEnabled() {
+		return this.logger.isInfoEnabled();
+	}
+
+	public boolean isTraceEnabled() {
+		return this.logger.isTraceEnabled();
+	}
+
+	public boolean isWarnEnabled() {
+		return this.logger.isWarnEnabled();
+	}
+
+	public void logDebug(Object msg) {
+		this.logger.debug(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	public void logDebug(Object msg, Throwable thrown) {
+		this.logger.debug(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	public void logError(Object msg) {
+		this.logger.error(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	public void logError(Object msg, Throwable thrown) {
+		this.logger.fatal(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	public void logFatal(Object msg) {
+		this.logger.fatal(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	public void logFatal(Object msg, Throwable thrown) {
+		this.logger.fatal(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	public void logInfo(Object msg) {
+		this.logger.info(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	public void logInfo(Object msg, Throwable thrown) {
+		this.logger.info(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	public void logTrace(Object msg) {
+		this.logger.trace(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	public void logTrace(Object msg, Throwable thrown) {
+		this.logger.trace(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	public void logWarn(Object msg) {
+		this.logger.warn(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	public void logWarn(Object msg, Throwable thrown) {
+		this.logger.warn(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Jdk14Logger.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Jdk14Logger.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Jdk14Logger.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,298 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.log;
+
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Logging functionality for JDK1.4
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: Jdk14Logger.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class Jdk14Logger implements Log {
+	private static final Level DEBUG = Level.FINE;
+
+	private static final Level ERROR = Level.SEVERE;
+
+	private static final Level FATAL = Level.SEVERE;
+
+	private static final Level INFO = Level.INFO;
+
+	private static final Level TRACE = Level.FINEST;
+
+	private static final Level WARN = Level.WARNING;
+
+	/**
+	 * The underlying logger from JDK-1.4
+	 */
+	protected Logger jdkLogger = null;
+
+	/**
+	 * Creates a new Jdk14Logger object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public Jdk14Logger(String name) {
+		this.jdkLogger = Logger.getLogger(name);
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isDebugEnabled()
+	 */
+	public boolean isDebugEnabled() {
+		return this.jdkLogger.isLoggable(Level.FINE);
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isErrorEnabled()
+	 */
+	public boolean isErrorEnabled() {
+		return this.jdkLogger.isLoggable(Level.SEVERE);
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isFatalEnabled()
+	 */
+	public boolean isFatalEnabled() {
+		return this.jdkLogger.isLoggable(Level.SEVERE);
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isInfoEnabled()
+	 */
+	public boolean isInfoEnabled() {
+		return this.jdkLogger.isLoggable(Level.INFO);
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isTraceEnabled()
+	 */
+	public boolean isTraceEnabled() {
+		return this.jdkLogger.isLoggable(Level.FINEST);
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isWarnEnabled()
+	 */
+	public boolean isWarnEnabled() {
+		return this.jdkLogger.isLoggable(Level.WARNING);
+	}
+
+	/**
+	 * Logs the given message instance using the 'debug' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logDebug(Object message) {
+		logInternal(DEBUG, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'debug' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logDebug(Object message, Throwable exception) {
+		logInternal(DEBUG, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'error' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logError(Object message) {
+		logInternal(ERROR, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'error' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logError(Object message, Throwable exception) {
+		logInternal(ERROR, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'fatal' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logFatal(Object message) {
+		logInternal(FATAL, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'fatal' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logFatal(Object message, Throwable exception) {
+		logInternal(FATAL, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'info' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logInfo(Object message) {
+		logInternal(INFO, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'info' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logInfo(Object message, Throwable exception) {
+		logInternal(INFO, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'trace' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logTrace(Object message) {
+		logInternal(TRACE, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'trace' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logTrace(Object message, Throwable exception) {
+		logInternal(TRACE, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'warn' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logWarn(Object message) {
+		logInternal(WARN, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'warn' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logWarn(Object message, Throwable exception) {
+		logInternal(WARN, message, exception);
+	}
+
+	private static final int findCallerStackDepth(StackTraceElement[] stackTrace) {
+		int numFrames = stackTrace.length;
+
+		for (int i = 0; i < numFrames; i++) {
+			String callerClassName = stackTrace[i].getClassName();
+
+			if (!callerClassName.startsWith("com.mysql.jdbc")
+					|| callerClassName.startsWith("com.mysql.jdbc.compliance")) {
+				return i;
+			}
+		}
+
+		return 0;
+	}
+
+	private void logInternal(Level level, Object msg, Throwable exception) {
+		//
+		// only go through this exercise if the message will actually
+		// be logged.
+		//
+
+		if (this.jdkLogger.isLoggable(level)) {
+			String messageAsString = null;
+			String callerMethodName = "N/A";
+			String callerClassName = "N/A";
+			int lineNumber = 0;
+			String fileName = "N/A";
+
+			if (msg instanceof ProfilerEvent) {
+				messageAsString = LogUtils.expandProfilerEventIfNecessary(msg)
+						.toString();
+			} else {
+				Throwable locationException = new Throwable();
+				StackTraceElement[] locations = locationException
+						.getStackTrace();
+
+				int frameIdx = findCallerStackDepth(locations);
+
+				if (frameIdx != 0) {
+					callerClassName = locations[frameIdx].getClassName();
+					callerMethodName = locations[frameIdx].getMethodName();
+					lineNumber = locations[frameIdx].getLineNumber();
+					fileName = locations[frameIdx].getFileName();
+				}
+
+				messageAsString = String.valueOf(msg);
+			}
+
+			if (exception == null) {
+				this.jdkLogger.logp(level, callerClassName, callerMethodName,
+						messageAsString);
+			} else {
+				this.jdkLogger.logp(level, callerClassName, callerMethodName,
+						messageAsString, exception);
+			}
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Log.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Log.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Log.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,184 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.log;
+
+/**
+ * Unified interface to logging facilities on different platforms
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: Log.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public interface Log {
+	/**
+	 * Is the 'debug' log level enabled?
+	 * 
+	 * @return true if so.
+	 */
+	boolean isDebugEnabled();
+
+	/**
+	 * Is the 'error' log level enabled?
+	 * 
+	 * @return true if so.
+	 */
+	boolean isErrorEnabled();
+
+	/**
+	 * Is the 'fatal' log level enabled?
+	 * 
+	 * @return true if so.
+	 */
+	boolean isFatalEnabled();
+
+	/**
+	 * Is the 'info' log level enabled?
+	 * 
+	 * @return true if so.
+	 */
+	boolean isInfoEnabled();
+
+	/**
+	 * Is the 'trace' log level enabled?
+	 * 
+	 * @return true if so.
+	 */
+	boolean isTraceEnabled();
+
+	/**
+	 * Is the 'warn' log level enabled?
+	 * 
+	 * @return true if so.
+	 */
+	boolean isWarnEnabled();
+
+	/**
+	 * Logs the given message instance using the 'debug' level
+	 * 
+	 * @param msg
+	 *            the message to log
+	 */
+	void logDebug(Object msg);
+
+	/**
+	 * Logs the given message and Throwable at the 'debug' level.
+	 * 
+	 * @param msg
+	 *            the message to log
+	 * @param thrown
+	 *            the throwable to log (may be null)
+	 */
+	void logDebug(Object msg, Throwable thrown);
+
+	/**
+	 * Logs the given message instance using the 'error' level
+	 * 
+	 * @param msg
+	 *            the message to log
+	 */
+	void logError(Object msg);
+
+	/**
+	 * Logs the given message and Throwable at the 'error' level.
+	 * 
+	 * @param msg
+	 *            the message to log
+	 * @param thrown
+	 *            the throwable to log (may be null)
+	 */
+	void logError(Object msg, Throwable thrown);
+
+	/**
+	 * Logs the given message instance using the 'fatal' level
+	 * 
+	 * @param msg
+	 *            the message to log
+	 */
+	void logFatal(Object msg);
+
+	/**
+	 * Logs the given message and Throwable at the 'fatal' level.
+	 * 
+	 * @param msg
+	 *            the message to log
+	 * @param thrown
+	 *            the throwable to log (may be null)
+	 */
+	void logFatal(Object msg, Throwable thrown);
+
+	/**
+	 * Logs the given message instance using the 'info' level
+	 * 
+	 * @param msg
+	 *            the message to log
+	 */
+	void logInfo(Object msg);
+
+	/**
+	 * Logs the given message and Throwable at the 'info' level.
+	 * 
+	 * @param msg
+	 *            the message to log
+	 * @param thrown
+	 *            the throwable to log (may be null)
+	 */
+	void logInfo(Object msg, Throwable thrown);
+
+	/**
+	 * Logs the given message instance using the 'trace' level
+	 * 
+	 * @param msg
+	 *            the message to log
+	 */
+	void logTrace(Object msg);
+
+	/**
+	 * Logs the given message and Throwable at the 'trace' level.
+	 * 
+	 * @param msg
+	 *            the message to log
+	 * @param thrown
+	 *            the throwable to log (may be null)
+	 */
+	void logTrace(Object msg, Throwable thrown);
+
+	/**
+	 * Logs the given message instance using the 'warn' level
+	 * 
+	 * @param msg
+	 *            the message to log
+	 */
+	void logWarn(Object msg);
+
+	/**
+	 * Logs the given message and Throwable at the 'warn' level.
+	 * 
+	 * @param msg
+	 *            the message to log
+	 * @param thrown
+	 *            the throwable to log (may be null)
+	 */
+	void logWarn(Object msg, Throwable thrown);
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Log4JLogger.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Log4JLogger.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/Log4JLogger.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,213 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.log;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+/**
+ * Implementation of log interface for Apache Log4j
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: Log4JLogger.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class Log4JLogger implements Log {
+
+	private Logger logger;
+
+	public Log4JLogger(String instanceName) {
+		this.logger = Logger.getLogger(instanceName);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#isDebugEnabled()
+	 */
+	public boolean isDebugEnabled() {
+		return this.logger.isDebugEnabled();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#isErrorEnabled()
+	 */
+	public boolean isErrorEnabled() {
+		return this.logger.isEnabledFor(Level.ERROR);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#isFatalEnabled()
+	 */
+	public boolean isFatalEnabled() {
+		return this.logger.isEnabledFor(Level.FATAL);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#isInfoEnabled()
+	 */
+	public boolean isInfoEnabled() {
+		return this.logger.isInfoEnabled();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#isTraceEnabled()
+	 */
+	public boolean isTraceEnabled() {
+		return this.logger.isDebugEnabled();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#isWarnEnabled()
+	 */
+	public boolean isWarnEnabled() {
+		return this.logger.isEnabledFor(Level.WARN);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logDebug(java.lang.Object)
+	 */
+	public void logDebug(Object msg) {
+		this.logger.debug(LogUtils.expandProfilerEventIfNecessary(LogUtils
+				.expandProfilerEventIfNecessary(msg)));
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logDebug(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logDebug(Object msg, Throwable thrown) {
+		this.logger.debug(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logError(java.lang.Object)
+	 */
+	public void logError(Object msg) {
+		this.logger.error(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logError(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logError(Object msg, Throwable thrown) {
+		this.logger.error(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logFatal(java.lang.Object)
+	 */
+	public void logFatal(Object msg) {
+		this.logger.fatal(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logFatal(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logFatal(Object msg, Throwable thrown) {
+		this.logger.fatal(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logInfo(java.lang.Object)
+	 */
+	public void logInfo(Object msg) {
+		this.logger.info(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logInfo(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logInfo(Object msg, Throwable thrown) {
+		this.logger.info(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logTrace(java.lang.Object)
+	 */
+	public void logTrace(Object msg) {
+		this.logger.debug(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logTrace(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logTrace(Object msg, Throwable thrown) {
+		this.logger.debug(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logWarn(java.lang.Object)
+	 */
+	public void logWarn(Object msg) {
+		this.logger.warn(LogUtils.expandProfilerEventIfNecessary(msg));
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.log.Log#logWarn(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logWarn(Object msg, Throwable thrown) {
+		this.logger.warn(LogUtils.expandProfilerEventIfNecessary(msg), thrown);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/LogFactory.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/LogFactory.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/LogFactory.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,105 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.log;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.sql.SQLException;
+
+import com.mysql.jdbc.SQLError;
+
+/**
+ * Creates instances of loggers for the driver to use.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: LogFactory.java 4946 2006-02-17 18:44:36Z mmatthews $
+ */
+public class LogFactory {
+
+	/**
+	 * Returns a logger instance of the given class, with the given instance
+	 * name.
+	 * 
+	 * @param className
+	 *            the class to instantiate
+	 * @param instanceName
+	 *            the instance name
+	 * @return a logger instance
+	 * @throws SQLException
+	 *             if unable to create a logger instance
+	 */
+	public static Log getLogger(String className, String instanceName)
+			throws SQLException {
+
+		if (className == null) {
+			throw SQLError.createSQLException("Logger class can not be NULL",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		if (instanceName == null) {
+			throw SQLError.createSQLException("Logger instance name can not be NULL",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+
+		try {
+			Class loggerClass = null;
+			
+			try {
+				loggerClass = Class.forName(className);
+			} catch (ClassNotFoundException nfe) {
+				loggerClass = Class.forName(Log.class.getPackage().getName() + "." + className);
+			}
+		
+			Constructor constructor = loggerClass
+					.getConstructor(new Class[] { String.class });
+
+			return (Log) constructor.newInstance(new Object[] { instanceName });
+		} catch (ClassNotFoundException cnfe) {
+			throw SQLError.createSQLException("Unable to load class for logger '"
+					+ className + "'", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (NoSuchMethodException nsme) {
+			throw SQLError.createSQLException(
+					"Logger class does not have a single-arg constructor that takes an instance name",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (InstantiationException inse) {
+			throw SQLError.createSQLException("Unable to instantiate logger class '"
+					+ className + "', exception in constructor?",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (InvocationTargetException ite) {
+			throw SQLError.createSQLException("Unable to instantiate logger class '"
+					+ className + "', exception in constructor?",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (IllegalAccessException iae) {
+			throw SQLError.createSQLException("Unable to instantiate logger class '"
+					+ className + "', constructor not public",
+					SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		} catch (ClassCastException cce) {
+			throw SQLError.createSQLException("Logger class '" + className
+					+ "' does not implement the '" + Log.class.getName()
+					+ "' interface", SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/LogUtils.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/LogUtils.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/LogUtils.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,164 @@
+/*
+ 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.log;
+
+import com.mysql.jdbc.Util;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+public class LogUtils {
+
+    public static final String CALLER_INFORMATION_NOT_AVAILABLE = "Caller information not available";
+
+    private static final String LINE_SEPARATOR = System
+			.getProperty("line.separator");
+
+	private static final int LINE_SEPARATOR_LENGTH = LINE_SEPARATOR.length();
+
+	public static Object expandProfilerEventIfNecessary(
+			Object possibleProfilerEvent) {
+
+		if (possibleProfilerEvent instanceof ProfilerEvent) {
+			StringBuffer msgBuf = new StringBuffer();
+
+			ProfilerEvent evt = (ProfilerEvent) possibleProfilerEvent;
+
+			Throwable locationException = evt.getEventCreationPoint();
+
+			if (locationException == null) {
+				locationException = new Throwable();
+			}
+
+			msgBuf.append("Profiler Event: [");
+
+			boolean appendLocationInfo = false;
+			
+			switch (evt.getEventType()) {
+			case ProfilerEvent.TYPE_EXECUTE:
+				msgBuf.append("EXECUTE");
+
+				break;
+
+			case ProfilerEvent.TYPE_FETCH:
+				msgBuf.append("FETCH");
+
+				break;
+
+			case ProfilerEvent.TYPE_OBJECT_CREATION:
+				msgBuf.append("CONSTRUCT");
+
+				break;
+
+			case ProfilerEvent.TYPE_PREPARE:
+				msgBuf.append("PREPARE");
+
+				break;
+
+			case ProfilerEvent.TYPE_QUERY:
+				msgBuf.append("QUERY");
+
+				break;
+
+			case ProfilerEvent.TYPE_WARN:
+				msgBuf.append("WARN");
+				appendLocationInfo = true;
+				
+				break;
+
+			default:
+				msgBuf.append("UNKNOWN");
+			}
+
+			msgBuf.append("] ");
+			msgBuf.append(findCallingClassAndMethod(locationException));
+			msgBuf.append(" duration: ");
+			msgBuf.append(evt.getEventDurationMillis());
+			msgBuf.append(" ms, connection-id: ");
+			msgBuf.append(evt.getConnectionId());
+			msgBuf.append(", statement-id: ");
+			msgBuf.append(evt.getStatementId());
+			msgBuf.append(", resultset-id: ");
+			msgBuf.append(evt.getResultSetId());
+
+			String evtMessage = evt.getMessage();
+
+			if (evtMessage != null) {
+				msgBuf.append(", message: ");
+				msgBuf.append(evtMessage);
+			}
+
+			if (appendLocationInfo) {
+				msgBuf
+					.append("\n\nFull stack trace of location where event occurred:\n\n");
+				msgBuf.append(Util.stackTraceToString(locationException));
+				msgBuf.append("\n");
+			}
+			
+			return msgBuf;
+		}
+		
+		return possibleProfilerEvent;
+
+	}
+
+	public static String findCallingClassAndMethod(Throwable t) {
+		String stackTraceAsString = Util.stackTraceToString(t);
+
+		String callingClassAndMethod = CALLER_INFORMATION_NOT_AVAILABLE;
+
+		int endInternalMethods = stackTraceAsString
+				.lastIndexOf("com.mysql.jdbc");
+
+		if (endInternalMethods != -1) {
+			int endOfLine = -1;
+			int compliancePackage = stackTraceAsString.indexOf(
+					"com.mysql.jdbc.compliance", endInternalMethods);
+
+			if (compliancePackage != -1) {
+				endOfLine = compliancePackage - LINE_SEPARATOR_LENGTH;
+			} else {
+				endOfLine = stackTraceAsString.indexOf(LINE_SEPARATOR,
+						endInternalMethods);
+			}
+
+			if (endOfLine != -1) {
+				int nextEndOfLine = stackTraceAsString.indexOf(LINE_SEPARATOR,
+						endOfLine + LINE_SEPARATOR_LENGTH);
+
+				if (nextEndOfLine != -1) {
+					callingClassAndMethod = stackTraceAsString.substring(
+							endOfLine + LINE_SEPARATOR_LENGTH, nextEndOfLine);
+				} else {
+					callingClassAndMethod = stackTraceAsString
+							.substring(endOfLine + LINE_SEPARATOR_LENGTH);
+				}
+			}
+		}
+
+		if (!callingClassAndMethod.startsWith("\tat ") && 
+				!callingClassAndMethod.startsWith("at ")) {
+			return "at " + callingClassAndMethod;
+		}
+
+		return callingClassAndMethod;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/NullLogger.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/NullLogger.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/NullLogger.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,196 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.log;
+
+/**
+ * A logger that does nothing. Used before the log is configured via the URL or
+ * properties.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: NullLogger.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class NullLogger implements Log {
+
+	/**
+	 * Creates a new NullLogger with the given name
+	 * 
+	 * @param instanceName
+	 *            (ignored)
+	 */
+	public NullLogger(String instanceName) {
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isDebugEnabled()
+	 */
+	public boolean isDebugEnabled() {
+		// XXX Auto-generated method stub
+		return false;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isErrorEnabled()
+	 */
+	public boolean isErrorEnabled() {
+		// XXX Auto-generated method stub
+		return false;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isFatalEnabled()
+	 */
+	public boolean isFatalEnabled() {
+		// XXX Auto-generated method stub
+		return false;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isInfoEnabled()
+	 */
+	public boolean isInfoEnabled() {
+		// XXX Auto-generated method stub
+		return false;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isTraceEnabled()
+	 */
+	public boolean isTraceEnabled() {
+		// XXX Auto-generated method stub
+		return false;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isWarnEnabled()
+	 */
+	public boolean isWarnEnabled() {
+		// XXX Auto-generated method stub
+		return false;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logDebug(java.lang.Object)
+	 */
+	public void logDebug(Object msg) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logDebug(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logDebug(Object msg, Throwable thrown) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logError(java.lang.Object)
+	 */
+	public void logError(Object msg) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logError(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logError(Object msg, Throwable thrown) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logFatal(java.lang.Object)
+	 */
+	public void logFatal(Object msg) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logFatal(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logFatal(Object msg, Throwable thrown) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logInfo(java.lang.Object)
+	 */
+	public void logInfo(Object msg) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logInfo(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logInfo(Object msg, Throwable thrown) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logTrace(java.lang.Object)
+	 */
+	public void logTrace(Object msg) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logTrace(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logTrace(Object msg, Throwable thrown) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logWarn(java.lang.Object)
+	 */
+	public void logWarn(Object msg) {
+		// XXX Auto-generated method stub
+
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#logWarn(java.lang.Object,
+	 *      java.lang.Throwable)
+	 */
+	public void logWarn(Object msg, Throwable thrown) {
+		// XXX Auto-generated method stub
+
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/StandardLogger.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/StandardLogger.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/log/StandardLogger.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,321 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.log;
+
+import com.mysql.jdbc.Util;
+import com.mysql.jdbc.profiler.ProfilerEvent;
+
+import java.util.Date;
+
+/**
+ * Provides logging facilities for those platforms that don't have built-in
+ * facilities. Simply logs messages to STDERR.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: StandardLogger.java 4597 2005-11-22 21:13:51Z mmatthews $
+ */
+public class StandardLogger implements Log {
+	private static final int FATAL = 0;
+
+	private static final int ERROR = 1;
+
+	private static final int WARN = 2;
+
+	private static final int INFO = 3;
+
+	private static final int DEBUG = 4;
+
+	private static final int TRACE = 5;
+
+	public static StringBuffer bufferedLog = null;
+
+	private boolean logLocationInfo = true;
+
+	/**
+	 * Creates a new StandardLogger object.
+	 * 
+	 * @param name
+	 *            the name of the configuration to use -- ignored
+	 */
+	public StandardLogger(String name) {
+		this(name, false);
+	}
+
+	public StandardLogger(String name, boolean logLocationInfo) {
+		this.logLocationInfo = logLocationInfo;
+	}
+
+	public static void saveLogsToBuffer() {
+		if (bufferedLog == null) {
+			bufferedLog = new StringBuffer();
+		}
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isDebugEnabled()
+	 */
+	public boolean isDebugEnabled() {
+		return true;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isErrorEnabled()
+	 */
+	public boolean isErrorEnabled() {
+		return true;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isFatalEnabled()
+	 */
+	public boolean isFatalEnabled() {
+		return true;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isInfoEnabled()
+	 */
+	public boolean isInfoEnabled() {
+		return true;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isTraceEnabled()
+	 */
+	public boolean isTraceEnabled() {
+		return true;
+	}
+
+	/**
+	 * @see com.mysql.jdbc.log.Log#isWarnEnabled()
+	 */
+	public boolean isWarnEnabled() {
+		return true;
+	}
+
+	/**
+	 * Logs the given message instance using the 'debug' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logDebug(Object message) {
+		logInternal(DEBUG, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'debug' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logDebug(Object message, Throwable exception) {
+		logInternal(DEBUG, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'error' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logError(Object message) {
+		logInternal(ERROR, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'error' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logError(Object message, Throwable exception) {
+		logInternal(ERROR, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'fatal' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logFatal(Object message) {
+		logInternal(FATAL, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'fatal' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logFatal(Object message, Throwable exception) {
+		logInternal(FATAL, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'info' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logInfo(Object message) {
+		logInternal(INFO, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'info' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logInfo(Object message, Throwable exception) {
+		logInternal(INFO, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'trace' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logTrace(Object message) {
+		logInternal(TRACE, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'trace' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logTrace(Object message, Throwable exception) {
+		logInternal(TRACE, message, exception);
+	}
+
+	/**
+	 * Logs the given message instance using the 'warn' level
+	 * 
+	 * @param message
+	 *            the message to log
+	 */
+	public void logWarn(Object message) {
+		logInternal(WARN, message, null);
+	}
+
+	/**
+	 * Logs the given message and Throwable at the 'warn' level.
+	 * 
+	 * @param message
+	 *            the message to log
+	 * @param exception
+	 *            the throwable to log (may be null)
+	 */
+	public void logWarn(Object message, Throwable exception) {
+		logInternal(WARN, message, exception);
+	}
+
+	private void logInternal(int level, Object msg, Throwable exception) {
+		StringBuffer msgBuf = new StringBuffer();
+		msgBuf.append(new Date().toString());
+		msgBuf.append(" ");
+
+		switch (level) {
+		case FATAL:
+			msgBuf.append("FATAL: ");
+
+			break;
+
+		case ERROR:
+			msgBuf.append("ERROR: ");
+
+			break;
+
+		case WARN:
+			msgBuf.append("WARN: ");
+
+			break;
+
+		case INFO:
+			msgBuf.append("INFO: ");
+
+			break;
+
+		case DEBUG:
+			msgBuf.append("DEBUG: ");
+
+			break;
+
+		case TRACE:
+			msgBuf.append("TRACE: ");
+
+			break;
+		}
+
+		if (msg instanceof ProfilerEvent) {
+			msgBuf.append(LogUtils.expandProfilerEventIfNecessary(msg));
+
+		} else {
+			if (this.logLocationInfo && level != TRACE) {
+				Throwable locationException = new Throwable();
+				msgBuf.append(LogUtils
+						.findCallingClassAndMethod(locationException));
+				msgBuf.append(" ");
+			}
+			
+			if (msg != null) {
+				msgBuf.append(String.valueOf(msg));
+			}
+		}
+
+		if (exception != null) {
+			msgBuf.append("\n");
+			msgBuf.append("\n");
+			msgBuf.append("EXCEPTION STACK TRACE:");
+			msgBuf.append("\n");
+			msgBuf.append("\n");
+			msgBuf.append(Util.stackTraceToString(exception));
+		}
+
+		String messageAsString = msgBuf.toString();
+
+		System.err.println(messageAsString);
+
+		if (bufferedLog != null) {
+			bufferedLog.append(messageAsString);
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/ProfileEventSink.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/ProfileEventSink.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/ProfileEventSink.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.profiler;
+
+import com.mysql.jdbc.Connection;
+import com.mysql.jdbc.log.Log;
+
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author mmatthew
+ */
+public class ProfileEventSink {
+
+	private static final Map CONNECTIONS_TO_SINKS = new HashMap();
+
+	private Connection ownerConnection = null;
+
+	private Log log = null;
+
+	/**
+	 * Returns the ProfileEventSink that handles profiler events for the given
+	 * connection.
+	 * 
+	 * @param conn
+	 *            the connection to handle events for
+	 * @return the ProfileEventSink that handles profiler events
+	 */
+	public static synchronized ProfileEventSink getInstance(Connection conn) {
+		ProfileEventSink sink = (ProfileEventSink) CONNECTIONS_TO_SINKS
+				.get(conn);
+
+		if (sink == null) {
+			sink = new ProfileEventSink(conn);
+			CONNECTIONS_TO_SINKS.put(conn, sink);
+		}
+
+		return sink;
+	}
+
+	/**
+	 * Process a profiler event
+	 * 
+	 * @param evt
+	 *            the event to process
+	 */
+	public void consumeEvent(ProfilerEvent evt) {
+		if (evt.eventType == ProfilerEvent.TYPE_WARN) {
+			this.log.logWarn(evt);
+		} else {
+			this.log.logInfo(evt);
+		}
+	}
+
+	public static synchronized void removeInstance(Connection conn) {
+		CONNECTIONS_TO_SINKS.remove(conn);
+	}
+
+	private ProfileEventSink(Connection conn) {
+		this.ownerConnection = conn;
+
+		try {
+			this.log = this.ownerConnection.getLog();
+		} catch (SQLException sqlEx) {
+			throw new RuntimeException("Unable to get logger from connection");
+		}
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/ProfilerEvent.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/ProfilerEvent.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/profiler/ProfilerEvent.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,501 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.profiler;
+
+import java.util.Date;
+
+import com.mysql.jdbc.Util;
+
+/**
+ * @author mmatthew
+ */
+public class ProfilerEvent {
+
+	/**
+	 * A Profiler warning event
+	 */
+	public static final byte TYPE_WARN = 0;
+
+	/**
+	 * Profiler creating object type event
+	 */
+	public static final byte TYPE_OBJECT_CREATION = 1;
+
+	/**
+	 * Profiler event for prepared statements being prepared
+	 */
+	public static final byte TYPE_PREPARE = 2;
+
+	/**
+	 * Profiler event for a query being executed
+	 */
+	public static final byte TYPE_QUERY = 3;
+
+	/**
+	 * Profiler event for prepared statements being executed
+	 */
+	public static final byte TYPE_EXECUTE = 4;
+
+	/**
+	 * Profiler event for result sets being retrieved
+	 */
+	public static final byte TYPE_FETCH = 5;
+
+	/**
+	 * Type of event
+	 */
+	protected byte eventType;
+
+	/**
+	 * Associated connection (-1 for none)
+	 */
+	protected long connectionId;
+
+	/**
+	 * Associated statement (-1 for none)
+	 */
+	protected int statementId;
+
+	/**
+	 * Associated result set (-1 for none)
+	 */
+	protected int resultSetId;
+
+	/**
+	 * When was the event created?
+	 */
+	protected long eventCreationTime;
+
+	/**
+	 * How long did the event last?
+	 */
+	protected int eventDurationMillis;
+
+	/**
+	 * The hostname the event occurred on (as an index into a dictionary, used
+	 * by 'remote' profilers for efficiency)?
+	 */
+	protected int hostNameIndex;
+
+	/**
+	 * The hostname the event occurred on
+	 */
+	protected String hostName;
+
+	/**
+	 * The catalog the event occurred on (as an index into a dictionary, used by
+	 * 'remote' profilers for efficiency)?
+	 */
+	protected int catalogIndex;
+
+	/**
+	 * The catalog the event occurred on
+	 */
+	protected String catalog;
+
+	/**
+	 * Where was the event created (as an index into a dictionary, used by
+	 * 'remote' profilers for efficiency)?
+	 */
+	protected int eventCreationPointIndex;
+
+	/**
+	 * Where was the event created (as a Throwable)?
+	 */
+	protected Throwable eventCreationPoint;
+
+	/**
+	 * Where was the event created (as a string description of the
+	 * eventCreationPoint)?
+	 */
+	protected String eventCreationPointDesc;
+
+	/**
+	 * Optional event message
+	 */
+	protected String message;
+
+	/**
+	 * Creates a new profiler event
+	 * 
+	 * @param eventType
+	 *            the event type (from the constants TYPE_????)
+	 * @param hostName
+	 *            the hostname where the event occurs
+	 * @param catalog
+	 *            the catalog in use
+	 * @param connectionId
+	 *            the connection id (-1 if N/A)
+	 * @param statementId
+	 *            the statement id (-1 if N/A)
+	 * @param resultSetId
+	 *            the result set id (-1 if N/A)
+	 * @param eventCreationTime
+	 *            when was the event created?
+	 * @param eventDurationMillis
+	 *            how long did the event last?
+	 * @param eventCreationPointDesc
+	 *            event creation point as a string
+	 * @param eventCreationPoint
+	 *            event creation point as a Throwable
+	 * @param message
+	 *            optional message
+	 */
+	public ProfilerEvent(byte eventType, String hostName, String catalog,
+			long connectionId, int statementId, int resultSetId,
+			long eventCreationTime, int eventDurationMillis,
+			String eventCreationPointDesc, Throwable eventCreationPoint,
+			String message) {
+		this.eventType = eventType;
+		this.connectionId = connectionId;
+		this.statementId = statementId;
+		this.resultSetId = resultSetId;
+		this.eventCreationTime = eventCreationTime;
+		this.eventDurationMillis = eventDurationMillis;
+		this.eventCreationPoint = eventCreationPoint;
+		this.eventCreationPointDesc = eventCreationPointDesc;
+		this.message = message;
+	}
+
+	/**
+	 * Returns the description of when this event was created.
+	 * 
+	 * @return a description of when this event was created.
+	 */
+	public String getEventCreationPointAsString() {
+		if (this.eventCreationPointDesc == null) {
+			this.eventCreationPointDesc = Util
+					.stackTraceToString(this.eventCreationPoint);
+		}
+
+		return this.eventCreationPointDesc;
+	}
+
+	/**
+	 * Returns a representation of this event as a String.
+	 * 
+	 * @return a String representation of this event.
+	 */
+	public String toString() {
+		StringBuffer buf = new StringBuffer(32);
+
+		switch (this.eventType) {
+		case TYPE_EXECUTE:
+			buf.append("EXECUTE");
+			break;
+
+		case TYPE_FETCH:
+			buf.append("FETCH");
+			break;
+
+		case TYPE_OBJECT_CREATION:
+			buf.append("CONSTRUCT");
+			break;
+
+		case TYPE_PREPARE:
+			buf.append("PREPARE");
+			break;
+
+		case TYPE_QUERY:
+			buf.append("QUERY");
+			break;
+
+		case TYPE_WARN:
+			buf.append("WARN");
+			break;
+		default:
+			buf.append("UNKNOWN");
+		}
+
+		buf.append(" created: ");
+		buf.append(new Date(this.eventCreationTime));
+		buf.append(" duration: ");
+		buf.append(this.eventDurationMillis);
+		buf.append(" connection: ");
+		buf.append(this.connectionId);
+		buf.append(" statement: ");
+		buf.append(this.statementId);
+		buf.append(" resultset: ");
+		buf.append(this.resultSetId);
+
+		if (this.message != null) {
+			buf.append(" message: ");
+			buf.append(this.message);
+
+		}
+
+		if (this.eventCreationPointDesc != null) {
+			buf.append("\n\nEvent Created at:\n");
+			buf.append(this.eventCreationPointDesc);
+		}
+
+		return buf.toString();
+	}
+
+	/**
+	 * Unpacks a binary representation of this event.
+	 * 
+	 * @param buf
+	 *            the binary representation of this event
+	 * @return the unpacked Event
+	 * @throws Exception
+	 *             if an error occurs while unpacking the event
+	 */
+	public static ProfilerEvent unpack(byte[] buf) throws Exception {
+		int pos = 0;
+
+		byte eventType = buf[pos++];
+		long connectionId = readInt(buf, pos);
+		pos += 8;
+		int statementId = readInt(buf, pos);
+		pos += 4;
+		int resultSetId = readInt(buf, pos);
+		pos += 4;
+		long eventCreationTime = readLong(buf, pos);
+		pos += 8;
+		int eventDurationMillis = readInt(buf, pos);
+		pos += 4;
+		int eventCreationPointIndex = readInt(buf, pos);
+		pos += 4;
+		byte[] eventCreationAsBytes = readBytes(buf, pos);
+		pos += 4;
+
+		if (eventCreationAsBytes != null) {
+			pos += eventCreationAsBytes.length;
+		}
+
+		byte[] message = readBytes(buf, pos);
+		pos += 4;
+
+		if (message != null) {
+			pos += message.length;
+		}
+
+		return new ProfilerEvent(eventType, "", "", connectionId, statementId,
+				resultSetId, eventCreationTime, eventDurationMillis,
+				new String(eventCreationAsBytes, "ISO8859_1"), null,
+				new String(message, "ISO8859_1"));
+	}
+
+	/**
+	 * Creates a binary representation of this event.
+	 * 
+	 * @return a binary representation of this event
+	 * @throws Exception
+	 *             if an error occurs while packing this event.
+	 */
+	public byte[] pack() throws Exception {
+
+		int len = 1 + 4 + 4 + 4 + 8 + 4 + 4;
+
+		byte[] eventCreationAsBytes = null;
+
+		getEventCreationPointAsString();
+
+		if (this.eventCreationPointDesc != null) {
+			eventCreationAsBytes = this.eventCreationPointDesc
+					.getBytes("ISO8859_1");
+			len += (4 + eventCreationAsBytes.length);
+		} else {
+			len += 4;
+		}
+
+		byte[] messageAsBytes = null;
+
+		if (messageAsBytes != null) {
+			messageAsBytes = this.message.getBytes("ISO8859_1");
+			len += (4 + messageAsBytes.length);
+		} else {
+			len += 4;
+		}
+
+		byte[] buf = new byte[len];
+
+		int pos = 0;
+
+		buf[pos++] = this.eventType;
+		pos = writeLong(this.connectionId, buf, pos);
+		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 = writeInt(this.eventCreationPointIndex, buf, pos);
+
+		if (eventCreationAsBytes != null) {
+			pos = writeBytes(eventCreationAsBytes, buf, pos);
+		} else {
+			pos = writeInt(0, buf, pos);
+		}
+
+		if (messageAsBytes != null) {
+			pos = writeBytes(messageAsBytes, buf, pos);
+		} else {
+			pos = writeInt(0, buf, pos);
+		}
+
+		return buf;
+	}
+
+	private static int writeInt(int i, byte[] buf, int pos) {
+
+		buf[pos++] = (byte) (i & 0xff);
+		buf[pos++] = (byte) (i >>> 8);
+		buf[pos++] = (byte) (i >>> 16);
+		buf[pos++] = (byte) (i >>> 24);
+
+		return pos;
+	}
+
+	private static int writeLong(long l, byte[] buf, int pos) {
+		buf[pos++] = (byte) (l & 0xff);
+		buf[pos++] = (byte) (l >>> 8);
+		buf[pos++] = (byte) (l >>> 16);
+		buf[pos++] = (byte) (l >>> 24);
+		buf[pos++] = (byte) (l >>> 32);
+		buf[pos++] = (byte) (l >>> 40);
+		buf[pos++] = (byte) (l >>> 48);
+		buf[pos++] = (byte) (l >>> 56);
+
+		return pos;
+	}
+
+	private static int writeBytes(byte[] msg, byte[] buf, int pos) {
+		pos = writeInt(msg.length, buf, pos);
+
+		System.arraycopy(msg, 0, buf, pos, msg.length);
+
+		return pos + msg.length;
+	}
+
+	private static int readInt(byte[] buf, int pos) {
+		return (buf[pos++] & 0xff) | ((buf[pos++] & 0xff) << 8)
+				| ((buf[pos++] & 0xff) << 16) | ((buf[pos++] & 0xff) << 24);
+
+	}
+
+	private static long readLong(byte[] buf, int pos) {
+		return (long) (buf[pos++] & 0xff) | ((long) (buf[pos++] & 0xff) << 8)
+				| ((long) (buf[pos++] & 0xff) << 16)
+				| ((long) (buf[pos++] & 0xff) << 24)
+				| ((long) (buf[pos++] & 0xff) << 32)
+				| ((long) (buf[pos++] & 0xff) << 40)
+				| ((long) (buf[pos++] & 0xff) << 48)
+				| ((long) (buf[pos++] & 0xff) << 56);
+	}
+
+	private static byte[] readBytes(byte[] buf, int pos) {
+		int length = readInt(buf, pos);
+
+		pos += 4;
+
+		byte[] msg = new byte[length];
+		System.arraycopy(buf, pos, msg, 0, length);
+
+		return msg;
+	}
+
+	/**
+	 * Returns the catalog in use
+	 * 
+	 * @return the catalog in use
+	 */
+	public String getCatalog() {
+		return this.catalog;
+	}
+
+	/**
+	 * Returns the id of the connection in use when this event was created.
+	 * 
+	 * @return the connection in use
+	 */
+	public long getConnectionId() {
+		return this.connectionId;
+	}
+
+	/**
+	 * Returns the point (as a Throwable stacktrace) where this event was
+	 * created.
+	 * 
+	 * @return the point where this event was created
+	 */
+	public Throwable getEventCreationPoint() {
+		return this.eventCreationPoint;
+	}
+
+	/**
+	 * Returns the time (in System.currentTimeMillis() form) when this event was
+	 * created
+	 * 
+	 * @return the time this event was created
+	 */
+	public long getEventCreationTime() {
+		return this.eventCreationTime;
+	}
+
+	/**
+	 * Returns the duration of the event in milliseconds
+	 * 
+	 * @return the duration of the event in milliseconds
+	 */
+	public int getEventDurationMillis() {
+		return this.eventDurationMillis;
+	}
+
+	/**
+	 * Returns the event type flag
+	 * 
+	 * @return the event type flag
+	 */
+	public byte getEventType() {
+		return this.eventType;
+	}
+
+	/**
+	 * Returns the id of the result set in use when this event was created.
+	 * 
+	 * @return the result set in use
+	 */
+	public int getResultSetId() {
+		return this.resultSetId;
+	}
+
+	/**
+	 * Returns the id of the statement in use when this event was created.
+	 * 
+	 * @return the statement in use
+	 */
+	public int getStatementId() {
+		return this.statementId;
+	}
+
+	/**
+	 * Returns the optional message for this event
+	 * 
+	 * @return the message stored in this event
+	 */
+	public String getMessage() {
+		return this.message;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/trace/Tracer.aj
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/trace/Tracer.aj	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/trace/Tracer.aj	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,259 @@
+/*
+      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.trace;
+
+import java.io.PrintStream;
+import java.sql.SQLException;
+
+import com.mysql.jdbc.log.Log;
+import com.mysql.jdbc.log.StandardLogger;
+import com.mysql.jdbc.Connection;
+
+import org.aspectj.lang.JoinPoint;
+
+public aspect Tracer {
+
+    pointcut constructors(): execution(* *(..)) && within(com.mysql.jdbc.* ) 
+    	&& within (!com.mysql.jdbc.trace.*) && within(!com.mysql.jdbc.log.*)
+    	&& within (!com.mysql.jdbc.Util);
+  
+    pointcut methods(): execution(* *(..)) && within(com.mysql.jdbc.* ) 
+    	&& within(!com.mysql.jdbc.trace.*) && within(!com.mysql.jdbc.log.*)
+    	&& within (!com.mysql.jdbc.Util);
+
+    before(): constructors() && methods() {
+		entry(thisJoinPoint, false);
+    }
+    
+    after() returning (Object o): constructors() && methods() {
+	  exit(thisJoinPoint, false, o);
+    }
+
+	private Log standardLogger = new StandardLogger("MySQL", false);
+	
+    private ThreadLocal stream = new ThreadLocal() {
+	    protected Object initialValue() {
+			return System.err;
+	    }
+	};
+	
+	private ThreadLocal log = new ThreadLocal() {
+		protected Object initialValue() {
+			return standardLogger;
+	    }
+	};
+	
+    private ThreadLocal callDepth = new ThreadLocal() {
+	    protected Object initialValue() {
+			return new Integer(0);
+	    }
+	};
+
+    private PrintStream getStream() { 
+		return (PrintStream)stream.get(); 
+    }
+    
+    private void setStream(PrintStream s) { 
+		stream.set(s); 
+    }
+    
+    private int  getCallDepth() { 
+		return ((Integer)(callDepth.get())).intValue();
+    }
+    
+    private void setCallDepth(int n) { 
+		callDepth.set(new Integer(n)); 
+    }
+    
+    private Log getLog() { 
+		return (Log)log.get();
+    }
+    
+    private void setLog(Log l) { 
+		log.set(l); 
+    }
+
+    private void entry(JoinPoint jp, boolean isConstructor) {
+		
+		if (jp.getTarget() instanceof com.mysql.jdbc.Connection) {
+			if ("getLog".equals(jp.getSignature().getName())) {
+				return;
+			}
+			
+    		try {
+    			Log connectionLog = ((com.mysql.jdbc.Connection)jp.getTarget()).getLog();
+
+    			if (getLog() != connectionLog) {
+    				setLog(connectionLog);
+	    		}
+	    	} catch (SQLException ex) {
+	    		// swallow it, can't do anything here
+	    	}
+    	}
+    	
+    	if ("com.mysql.jdbc.Buffer".equals(jp.getSignature().getDeclaringTypeName())
+    		&& ("toString".equals(jp.getSignature().getName())
+    		 || "dumpClampedBytes".equals(jp.getSignature().getName()))) {
+    		return;
+    	}
+    	
+    	if ("com.mysql.jdbc.StringUtils".equals(jp.getSignature().getDeclaringTypeName())
+    		&& "dumpAsHex".equals(jp.getSignature().getName())) {
+    		return;
+    	}
+    	
+		setCallDepth(getCallDepth() + 1);
+		printEntering(jp, isConstructor);
+    }
+
+    private void exit(JoinPoint jp,  boolean isConstructor, Object returnValue) {
+    	if (jp.getTarget() instanceof com.mysql.jdbc.Connection) {
+			if ("getLog".equals(jp.getSignature().getName())) {
+				return;
+			}
+		}
+		
+    	if ("com.mysql.jdbc.Buffer".equals(jp.getSignature().getDeclaringTypeName())
+    		&& ("toString".equals(jp.getSignature().getName())
+    		 || "dumpClampedBytes".equals(jp.getSignature().getName()))) {
+    		return;
+    	}
+    	
+    	if ("com.mysql.jdbc.StringUtils".equals(jp.getSignature().getDeclaringTypeName())
+    		&& "dumpAsHex".equals(jp.getSignature().getName())) {
+    		return;
+    	}
+			
+		printExiting(jp, isConstructor, returnValue);
+		setCallDepth(getCallDepth() - 1);
+    }
+
+    private void printEntering (JoinPoint jp, boolean isConstructor) {
+    	
+    	
+    	if (getLog().isTraceEnabled()) {
+    		
+    		StringBuffer buf = new StringBuffer(80);
+			printIndent(buf);
+			buf.append("--> ");
+
+			buf.append(jp.getSourceLocation().getFileName());
+			buf.append(":");
+			buf.append(jp.getSourceLocation().getLine());
+			buf.append(" ");
+			buf.append(jp.getSignature().getDeclaringTypeName());
+			buf.append(".");
+			buf.append(jp.getSignature().getName());
+    		printParameters(jp, buf);
+
+			getLog().logTrace(buf);
+		}
+    }
+
+    private void printExiting (JoinPoint jp, boolean isConstructor, Object returnValue) {
+    	if (getLog().isTraceEnabled()) {
+    		StringBuffer buf = new StringBuffer(80);
+			printIndent(buf);
+			
+			buf.append("<--  ");
+			buf.append(jp.getSourceLocation().getFileName());
+			buf.append(":");
+			buf.append(jp.getSourceLocation().getLine());
+			buf.append(" ");
+			buf.append(jp.getSignature().getDeclaringTypeName());
+			buf.append(".");
+			buf.append(jp.getSignature().getName());
+			buf.append("(..) returning ");
+			
+			boolean isString = returnValue instanceof String;
+  			
+  			if (isString) {
+  				buf.append("\"");
+  			}
+  	    	
+  	    	buf.append(returnValue);
+  	    	
+  	    	if (isString) {
+  				buf.append("\"");
+  			}
+
+			getLog().logTrace(buf);
+		}
+    }
+
+
+
+    private void printIndent(StringBuffer buf) {
+		for (int i = 0; i < getCallDepth(); i++) {
+	    	buf.append(" ");
+	    }
+    }
+    
+    private void printParameters(JoinPoint jp, StringBuffer buf) {
+  		Object[] params = jp.getArgs();
+	 	
+  		buf.append("(");
+  		
+  		for (int i = 0; i < params.length; i++) {
+  			boolean isString = params[i] instanceof String;
+  			
+  			if (isString) {
+  				buf.append("\"");
+  			}
+  	    	
+  	    	if (params[i] != null) {
+  	    		Class paramClass = params[i].getClass();
+  	    		String paramClassName = null;
+  	    		
+  	    		if (paramClass != null) {
+  	    			paramClassName = paramClass.getName();
+  	    		}
+  	    		
+  	    		if (paramClassName!= null &&
+  	    			"com.mysql.jdbc.Buffer".equals(paramClassName)
+  	    			|| "com.mysql.jdbc.ByteArrayBuffer".equals(paramClassName)
+  	    			|| "com.mysql.jdbc.ChannelBuffer".equals(paramClassName)) {
+  	    			buf.append("Network packet, data follows:\n\n");
+  	    			buf.append(params[i]);
+  	    			buf.append("\n\n");
+  	    		} else {
+  	    			buf.append(params[i]);
+  	    		}
+  	    	} else {
+  	    		buf.append("null");
+  	    	}
+  	    	
+  	    	if (isString) {
+  				buf.append("\"");
+  			}
+  			
+  	    	if (i < params.length - 1) {
+  	    		buf.append(", ");
+  	    	}
+  		}
+  		
+  		buf.append(")");
+    }
+
+}
+

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/BaseBugReport.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/BaseBugReport.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/BaseBugReport.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,263 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+ 
+ */
+package com.mysql.jdbc.util;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+import com.mysql.jdbc.Driver;
+
+/**
+ * Base class to help file bug reports for Connector/J.
+ * 
+ * <p>
+ * MySQL AB
+ * <ul>
+ * really
+ * </ul>
+ * appreciates repeatable testcases when reporting bugs, so we're giving you
+ * this class to make that job a bit easier (and standarized).
+ * 
+ * <p>
+ * To create a testcase, create a class that inherits from this class
+ * (com.mysql.jdbc.util.BaseBugReport), and override the methods 'setUp',
+ * 'tearDown' and 'runTest'.
+ * 
+ * <p>
+ * In the 'setUp' method, create code that creates your tables, and populates
+ * them with any data needed to demonstrate the bug.
+ * 
+ * <p>
+ * In the 'runTest' method, create code that demonstrates the bug using the
+ * tables and data you created in the 'setUp' method.
+ * 
+ * <p>
+ * In the 'tearDown' method, drop any tables you created in the 'setUp' method.
+ * 
+ * <p>
+ * In any of the above three methods, you should use one of the variants of the
+ * 'getConnection' method to create a JDBC connection to MySQL, which will use
+ * the default JDBC URL of 'jdbc:mysql:///test'.
+ * 
+ * <p>
+ * If you need to use a JDBC URL that is different than 'jdbc:mysql:///test',
+ * then override the method 'getUrl' as well.
+ * 
+ * <p>
+ * Use the 'assertTrue' methods to create conditions that must be met in your
+ * testcase demonstrating the behavior you are expecting (vs. the behavior you
+ * are observing, which is why you are most likely filing a bug report).
+ * 
+ * <p>
+ * Finally, create a 'main' method that creates a new instance of your testcase,
+ * and calls the 'run' method:
+ * 
+ * <p>
+ * 
+ * <pre>
+ * public static void main(String[] args) throws Exception {
+ * 	new MyBugReport().run();
+ * }
+ * </pre>
+ * 
+ * <p>
+ * When filing a potential bug with MySQL Connector/J at http://bugs.mysql.com/
+ * or on the bugs mailing list, please include the code that you have just
+ * written using this class.
+ * 
+ * @author Mark Matthews
+ * @version $Id: BaseBugReport.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public abstract class BaseBugReport {
+
+	private Connection conn;
+
+	private Driver driver;
+
+	/**
+	 * Constructor for this BugReport, sets up JDBC driver used to create
+	 * connections.
+	 */
+	public BaseBugReport() {
+		try {
+			this.driver = new Driver();
+		} catch (SQLException ex) {
+			throw new RuntimeException(ex.toString());
+		}
+	}
+
+	/**
+	 * Override this method with code that sets up the testcase for
+	 * demonstrating your bug (creating tables, populating data, etc).
+	 * 
+	 * @throws Exception
+	 *             if an error occurs during the 'setUp' phase.
+	 */
+	public abstract void setUp() throws Exception;
+
+	/**
+	 * Override this method with code that cleans up anything created in the
+	 * setUp() method.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs during the 'tearDown' phase.
+	 */
+	public abstract void tearDown() throws Exception;
+
+	/**
+	 * Override this method with code that demonstrates the bug. This method
+	 * will be called after setUp(), and before tearDown().
+	 * 
+	 * @throws Exception
+	 *             if an error occurs during your test run.
+	 */
+	public abstract void runTest() throws Exception;
+
+	/**
+	 * Runs the testcase by calling the setUp(), runTest() and tearDown()
+	 * methods. The tearDown() method is run regardless of any errors occuring
+	 * in the other methods.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs in any of the aforementioned methods.
+	 */
+	public final void run() throws Exception {
+		try {
+			setUp();
+			runTest();
+
+		} finally {
+			tearDown();
+		}
+	}
+
+	/**
+	 * Throws an exception with the given message if condition evalutates to
+	 * 'false'.
+	 * 
+	 * @param message
+	 *            the message to use in the exception
+	 * @param condition
+	 *            the condition to test for
+	 * @throws Exception
+	 *             if !condition
+	 */
+	protected final void assertTrue(String message, boolean condition)
+			throws Exception {
+		if (!condition) {
+			throw new Exception("Assertion failed: " + message);
+		}
+	}
+
+	/**
+	 * Throws an exception if condition evalutates to 'false'.
+	 * 
+	 * @param condition
+	 *            the condition to test for
+	 * @throws Exception
+	 *             if !condition
+	 */
+	protected final void assertTrue(boolean condition) throws Exception {
+		assertTrue("(no message given)", condition);
+	}
+
+	/**
+	 * Provides the JDBC URL to use to demonstrate the bug. The
+	 * java.sql.Connection that you use to demonstrate this bug will be provided
+	 * by the getConnection() method using this URL.
+	 * 
+	 * The default value is 'jdbc:mysql:///test'
+	 */
+	public String getUrl() {
+		return "jdbc:mysql:///test";
+	}
+
+	/**
+	 * Provides a connection to the JDBC URL specified in getUrl().
+	 * 
+	 * If a connection already exists, that connection is returned. Otherwise a
+	 * new connection is created.
+	 * 
+	 * @return a connection to the JDBC URL specified in getUrl().
+	 * 
+	 * @throws SQLException
+	 *             if an error is caused while creating the connection.
+	 */
+	public final synchronized Connection getConnection() throws SQLException {
+		if (this.conn == null || this.conn.isClosed()) {
+			this.conn = getNewConnection();
+		}
+
+		return this.conn;
+	}
+
+	/**
+	 * Use this if you need to get a new connection for your bug report (i.e.
+	 * there's more than one connection involved).
+	 * 
+	 * @return a new connection to the JDBC URL specified in getUrl().
+	 * 
+	 * @throws SQLException
+	 *             if an error is caused while creating the connection.
+	 */
+	public final synchronized Connection getNewConnection() throws SQLException {
+		return getConnection(getUrl());
+	}
+
+	/**
+	 * Returns a connection using the given URL.
+	 * 
+	 * @param url
+	 *            the JDBC URL to use
+	 * @return a new java.sql.Connection to the JDBC URL.
+	 * @throws SQLException
+	 *             if an error occurs getting the connection.
+	 */
+	public final synchronized Connection getConnection(String url)
+			throws SQLException {
+		return getConnection(url, null);
+	}
+
+	/**
+	 * Returns a connection using the given URL and properties.
+	 * 
+	 * @param url
+	 *            the JDBC URL to use
+	 * @param props
+	 *            the JDBC properties to use
+	 * @return a new java.sql.Connection to the JDBC URL.
+	 * @throws SQLException
+	 *             if an error occurs getting the connection.
+	 */
+	public final synchronized Connection getConnection(String url,
+			Properties props) throws SQLException {
+
+		// Don't follow this example in your own code
+		// This is to bypass the java.sql.DriverManager
+
+		return this.driver.connect(url, props);
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,43 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.util;
+
+import com.mysql.jdbc.SQLError;
+
+/**
+ * Creates XML file describing mapping of MySQL error #'s to SQL92 and X/Open
+ * states.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: ErrorMappingsDocGenerator.java,v 1.1.2.1 2005/05/13 18:58:39
+ *          mmatthews Exp $
+ */
+public class ErrorMappingsDocGenerator {
+
+	public static void main(String[] args) throws Exception {
+		SQLError.dumpSqlStatesMappingsAsXml();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/LRUCache.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/LRUCache.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/LRUCache.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,51 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.util;
+
+import java.util.LinkedHashMap;
+import java.util.Map.Entry;
+
+/**
+ * @author Mark Matthews
+ * @version $Id: LRUCache.java 4161 2005-08-30 19:14:01Z eherman $
+ */
+public class LRUCache extends LinkedHashMap {
+    private static final long serialVersionUID = 1L;
+    protected int maxElements;
+
+	public LRUCache(int maxSize) {
+		super(maxSize);
+		this.maxElements = maxSize;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry)
+	 */
+	protected boolean removeEldestEntry(Entry eldest) {
+		return (size() > this.maxElements);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/PropertiesDocGenerator.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/PropertiesDocGenerator.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/PropertiesDocGenerator.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.util;
+
+import java.sql.SQLException;
+
+import com.mysql.jdbc.ConnectionProperties;
+
+/**
+ * Creates docbook table of connection properties from ConnectionProperties
+ * class.
+ */
+public class PropertiesDocGenerator extends ConnectionProperties {
+
+	public static void main(String[] args) throws SQLException {
+		System.out.println(new PropertiesDocGenerator().exposeAsXml());
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ReadAheadInputStream.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ReadAheadInputStream.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ReadAheadInputStream.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,309 @@
+/*
+ Copyright (C) 2002-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.util;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+import com.mysql.jdbc.log.Log;
+
+/**
+ * A non-blocking buffered input stream. Reads more if it can, won't block to
+ * fill the buffer, only blocks to satisfy a request of read(byte[])
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: ReadAheadInputStream.java,v 1.1.2.1 2005/05/13 18:58:39
+ *          mmatthews Exp $
+ */
+public class ReadAheadInputStream extends InputStream {
+
+	private final static int DEFAULT_BUFFER_SIZE = 4096;
+
+	private InputStream underlyingStream;
+
+	private byte buf[];
+
+	protected int endOfCurrentData;
+
+	protected int currentPosition;
+
+	protected boolean doDebug = false;
+	
+	protected Log log;
+
+	private void fill(int readAtLeastTheseManyBytes) throws IOException {
+		checkClosed();
+
+		this.currentPosition = 0; /* no mark: throw away the buffer */
+
+		this.endOfCurrentData = currentPosition;
+
+		// Read at least as many bytes as the caller wants, but don't
+		// block to fill the whole buffer (like java.io.BufferdInputStream
+		// does)
+
+		int bytesToRead = Math.min(this.buf.length - currentPosition,
+				readAtLeastTheseManyBytes);
+
+		int bytesAvailable = this.underlyingStream.available();
+
+		if (bytesAvailable > bytesToRead) {
+
+			// Great, there's more available, let's grab those
+			// bytes too! (read-ahead)
+
+			bytesToRead = Math.min(this.buf.length - currentPosition,
+					bytesAvailable);
+		}
+
+		if (this.doDebug) {
+			StringBuffer debugBuf = new StringBuffer();
+			debugBuf.append("  ReadAheadInputStream.fill(");
+			debugBuf.append(readAtLeastTheseManyBytes);
+			debugBuf.append("), buffer_size=");
+			debugBuf.append(this.buf.length);
+			debugBuf.append(", current_position=");
+			debugBuf.append(currentPosition);
+			debugBuf.append(", need to read ");
+			debugBuf.append(Math.min(this.buf.length - currentPosition,
+					readAtLeastTheseManyBytes));
+			debugBuf.append(" bytes to fill request,");
+
+			if (bytesAvailable > 0) {
+				debugBuf.append(" underlying InputStream reports ");
+				debugBuf.append(bytesAvailable);
+
+				debugBuf.append(" total bytes available,");
+			}
+
+			debugBuf.append(" attempting to read ");
+			debugBuf.append(bytesToRead);
+			debugBuf.append(" bytes.");
+
+			if (this.log != null) {
+				this.log.logTrace(debugBuf.toString());
+			} else {
+				System.err.println(debugBuf.toString());
+			}
+		}
+
+		int n = this.underlyingStream.read(this.buf, currentPosition,
+				bytesToRead);
+
+		if (n > 0) {
+			endOfCurrentData = n + currentPosition;
+		}
+	}
+
+	private int readFromUnderlyingStreamIfNecessary(byte[] b, int off, int len)
+			throws IOException {
+		checkClosed();
+
+		int avail = endOfCurrentData - currentPosition;
+
+		if (this.doDebug) {
+			StringBuffer debugBuf = new StringBuffer();
+			debugBuf.append("ReadAheadInputStream.readIfNecessary(");
+			debugBuf.append(b);
+			debugBuf.append(",");
+			debugBuf.append(off);
+			debugBuf.append(",");
+			debugBuf.append(len);
+			debugBuf.append(")");
+
+			if (avail <= 0) {
+				debugBuf
+						.append(" not all data available in buffer, must read from stream");
+
+				if (len >= this.buf.length) {
+					debugBuf
+							.append(", amount requested > buffer, returning direct read() from stream");
+				}
+			}
+
+			if (this.log != null) {
+				this.log.logTrace(debugBuf.toString());
+			} else {
+				System.err.println(debugBuf.toString());
+			}
+		}
+
+		if (avail <= 0) {
+
+			if (len >= this.buf.length) {
+				return this.underlyingStream.read(b, off, len);
+			}
+
+			fill(len);
+
+			avail = endOfCurrentData - currentPosition;
+
+			if (avail <= 0)
+				return -1;
+		}
+
+		int bytesActuallyRead = (avail < len) ? avail : len;
+
+		System.arraycopy(this.buf, currentPosition, b, off, bytesActuallyRead);
+
+		this.currentPosition += bytesActuallyRead;
+
+		return bytesActuallyRead;
+	}
+
+	public synchronized int read(byte b[], int off, int len) throws IOException {
+		checkClosed(); // Check for closed stream
+		if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
+			throw new IndexOutOfBoundsException();
+		} else if (len == 0) {
+			return 0;
+		}
+
+		int totalBytesRead = 0;
+
+		while (true) {
+			int bytesReadThisRound = readFromUnderlyingStreamIfNecessary(b, off
+					+ totalBytesRead, len - totalBytesRead);
+
+			// end-of-stream?
+			if (bytesReadThisRound <= 0) {
+				if (totalBytesRead == 0) {
+					totalBytesRead = bytesReadThisRound;
+				}
+
+				break;
+			}
+
+			totalBytesRead += bytesReadThisRound;
+
+			// Read _at_least_ enough bytes
+			if (totalBytesRead >= len) {
+				break;
+			}
+
+			// Nothing to read?
+			if (this.underlyingStream.available() <= 0) {
+				break;
+			}
+		}
+
+		return totalBytesRead;
+	}
+
+	public int read() throws IOException {
+		checkClosed();
+
+		if (currentPosition >= endOfCurrentData) {
+			fill(1);
+			if (currentPosition >= endOfCurrentData)
+				return -1;
+		}
+
+		return this.buf[currentPosition++] & 0xff;
+	}
+
+	public int available() throws IOException {
+		checkClosed();
+
+		return this.underlyingStream.available()
+				+ (this.endOfCurrentData - this.currentPosition);
+	}
+
+	private void checkClosed() throws IOException {
+
+		if (this.buf == null) {
+			throw new IOException("Stream closed");
+		}
+	}
+
+	/**
+	 * 
+	 */
+	public ReadAheadInputStream(InputStream toBuffer, boolean debug, Log logTo) {
+		this(toBuffer, DEFAULT_BUFFER_SIZE, debug, logTo);
+	}
+
+	public ReadAheadInputStream(InputStream toBuffer, int bufferSize,
+			boolean debug,
+			Log logTo) {
+		this.underlyingStream = toBuffer;
+		this.buf = new byte[bufferSize];
+		this.doDebug = debug;
+		this.log = logTo;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.Closeable#close()
+	 */
+	public void close() throws IOException {
+		if (this.underlyingStream != null) {
+			try {
+				this.underlyingStream.close();
+			} finally {
+				this.underlyingStream = null;
+				this.buf = null;
+				this.log = null;
+			}
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.InputStream#markSupported()
+	 */
+	public boolean markSupported() {
+		return false;
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see java.io.InputStream#skip(long)
+	 */
+	public long skip(long n) throws IOException {
+		checkClosed();
+		if (n <= 0) {
+			return 0;
+		}
+
+		long bytesAvailInBuffer = this.endOfCurrentData - this.currentPosition;
+
+		if (bytesAvailInBuffer <= 0) {
+
+			fill((int) n);
+			bytesAvailInBuffer = this.endOfCurrentData - this.currentPosition;
+			if (bytesAvailInBuffer <= 0)
+				return 0;
+		}
+
+		long bytesSkipped = (bytesAvailInBuffer < n) ? bytesAvailInBuffer : n;
+		this.currentPosition += bytesSkipped;
+		return bytesSkipped;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ResultSetUtil.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ResultSetUtil.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ResultSetUtil.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+ 
+ */
+package com.mysql.jdbc.util;
+
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+
+/**
+ * Utilities for dealing with result sets (used in testcases and profiler).
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: ResultSetUtil.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class ResultSetUtil {
+
+	public static StringBuffer appendResultSetSlashGStyle(
+			StringBuffer appendTo, ResultSet rs) throws SQLException {
+		ResultSetMetaData rsmd = rs.getMetaData();
+
+		int numFields = rsmd.getColumnCount();
+		int maxWidth = 0;
+
+		String[] fieldNames = new String[numFields];
+
+		for (int i = 0; i < numFields; i++) {
+			fieldNames[i] = rsmd.getColumnLabel(i + 1);
+
+			if (fieldNames[i].length() > maxWidth) {
+				maxWidth = fieldNames[i].length();
+			}
+		}
+
+		int rowCount = 1;
+
+		while (rs.next()) {
+			appendTo.append("*************************** ");
+			appendTo.append(rowCount++);
+			appendTo.append(". row ***************************\n");
+
+			for (int i = 0; i < numFields; i++) {
+				int leftPad = maxWidth - fieldNames[i].length();
+
+				for (int j = 0; j < leftPad; j++) {
+					appendTo.append(" ");
+				}
+
+				appendTo.append(fieldNames[i]);
+				appendTo.append(": ");
+
+				String stringVal = rs.getString(i + 1);
+
+				if (stringVal != null) {
+					appendTo.append(stringVal);
+				} else {
+					appendTo.append("NULL");
+				}
+
+				appendTo.append("\n");
+			}
+
+			appendTo.append("\n");
+		}
+
+		return appendTo;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ServerController.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ServerController.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/ServerController.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,351 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.util;
+
+import java.io.File;
+import java.io.IOException;
+
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Properties;
+
+import com.mysql.jdbc.StringUtils;
+
+/**
+ * Controls a MySQL server using Java RunTime methods
+ * 
+ * @version $Id: ServerController.java,v 1.1.2.1 2005/05/13 18:58:39 mmatthews
+ *          Exp $
+ * @author Mark Matthews
+ */
+public class ServerController {
+
+	/**
+	 * Where is the server installed?
+	 */
+	public static final String BASEDIR_KEY = "basedir";
+
+	/**
+	 * Where are the databases installed?
+	 */
+	public static final String DATADIR_KEY = "datadir";
+
+	/**
+	 * Where is the config file located?
+	 */
+
+	public static final String DEFAULTS_FILE_KEY = "defaults-file";
+
+	/**
+	 * What is the name of the executable to run?
+	 */
+
+	public static final String EXECUTABLE_NAME_KEY = "executable";
+
+	/**
+	 * What is the path to the mysql server executable (if not standard?)
+	 */
+
+	public static final String EXECUTABLE_PATH_KEY = "executablePath";
+
+	/**
+	 * The default executable to run
+	 */
+
+	/**
+	 * The process representing the MySQL server
+	 */
+	private Process serverProcess = null;
+
+	/**
+	 * The list of properties for this server
+	 */
+	private Properties serverProps = null;
+
+	/**
+	 * The system properties
+	 */
+	private Properties systemProps = null;
+
+	/**
+	 * Creates a ServerController with the directory for the MySQL server.
+	 * 
+	 * The 'datadir' is set to the same directory.
+	 * 
+	 * @param baseDir
+	 *            the base directory for the MySQL server.
+	 */
+	public ServerController(String baseDir) {
+		setBaseDir(baseDir);
+	}
+
+	/**
+	 * Creates a server controller for the MySQL server with the given basedir
+	 * and datadir.
+	 * 
+	 * @param basedir
+	 *            the basedir to use when starting MySQL.
+	 * @param datadir
+	 *            the datadir to use when starting MySQL.
+	 */
+	public ServerController(String basedir, String datadir) {
+	}
+
+	/**
+	 * Sets the basedir to use when starting MySQL.
+	 * 
+	 * @param baseDir
+	 *            the basedir to use when starting MySQL.
+	 */
+	public void setBaseDir(String baseDir) {
+		getServerProps().setProperty(BASEDIR_KEY, baseDir);
+	}
+
+	/**
+	 * Sets the data to use when starting MySQL.
+	 * 
+	 * @param dataDir
+	 *            the basedir to use when starting MySQL.
+	 */
+	public void setDataDir(String dataDir) {
+		getServerProps().setProperty(DATADIR_KEY, dataDir);
+	}
+
+	/**
+	 * Starts the server, returning a java.lang.Process instance that represents
+	 * the mysql server.
+	 * 
+	 * @return Process a java.lang.Process instance representing the mysql
+	 *         server process.
+	 * @throws IOException
+	 *             if an error occurs while starting the mysql server.
+	 */
+	public Process start() throws IOException {
+		if (this.serverProcess != null) {
+			throw new IllegalArgumentException("Server already started");
+		} else {
+			this.serverProcess = Runtime.getRuntime().exec(getCommandLine());
+
+			return this.serverProcess;
+		}
+	}
+
+	/**
+	 * Stops the server (if started)
+	 * 
+	 * @param forceIfNecessary
+	 *            use forceStop if mysqladmin doesn't shut the server down
+	 * 
+	 * @throws IOException
+	 *             if an error occurs while stopping the server
+	 */
+	public void stop(boolean forceIfNecessary) throws IOException {
+		if (this.serverProcess != null) {
+
+			String basedir = getServerProps().getProperty(BASEDIR_KEY);
+
+			StringBuffer pathBuf = new StringBuffer(basedir);
+
+			if (!basedir.endsWith(File.separator)) {
+				pathBuf.append(File.separator);
+			}
+
+			String defaultsFilePath = getServerProps().getProperty(
+					DEFAULTS_FILE_KEY);
+
+			pathBuf.append("bin");
+			pathBuf.append(File.separator);
+			pathBuf.append("mysqladmin shutdown");
+
+			System.out.println(pathBuf.toString());
+
+			Process mysqladmin = Runtime.getRuntime().exec(pathBuf.toString());
+
+			int exitStatus = -1;
+
+			try {
+				exitStatus = mysqladmin.waitFor();
+			} catch (InterruptedException ie) {
+				; // ignore
+			}
+
+			//
+			// Terminate the process if mysqladmin couldn't
+			// do it, and the user requested a force stop.
+			//
+			if (exitStatus != 0 && forceIfNecessary) {
+				forceStop();
+			}
+		}
+	}
+
+	/**
+	 * Forcefully terminates the server process (if started).
+	 */
+	public void forceStop() {
+		if (this.serverProcess != null) {
+			this.serverProcess.destroy();
+			this.serverProcess = null;
+		}
+	}
+
+	/**
+	 * Returns the list of properties that will be used to start/control the
+	 * server.
+	 * 
+	 * @return Properties the list of properties.
+	 */
+	public synchronized Properties getServerProps() {
+		if (this.serverProps == null) {
+			this.serverProps = new Properties();
+		}
+
+		return this.serverProps;
+	}
+
+	/**
+	 * Returns the full commandline used to start the mysql server, including
+	 * and arguments to be passed to the server process.
+	 * 
+	 * @return String the commandline used to start the mysql server.
+	 */
+	private String getCommandLine() {
+		StringBuffer commandLine = new StringBuffer(getFullExecutablePath());
+		commandLine.append(buildOptionalCommandLine());
+
+		return commandLine.toString();
+	}
+
+	/**
+	 * Returns the fully-qualifed path to the 'mysqld' executable
+	 * 
+	 * @return String the path to the server executable.
+	 */
+	private String getFullExecutablePath() {
+		StringBuffer pathBuf = new StringBuffer();
+
+		String optionalExecutablePath = getServerProps().getProperty(
+				EXECUTABLE_PATH_KEY);
+
+		if (optionalExecutablePath == null) {
+			// build the path using the defaults
+			String basedir = getServerProps().getProperty(BASEDIR_KEY);
+			pathBuf.append(basedir);
+
+			if (!basedir.endsWith(File.separator)) {
+				pathBuf.append(File.separatorChar);
+			}
+
+			if (runningOnWindows()) {
+				pathBuf.append("bin");
+			} else {
+				pathBuf.append("libexec");
+			}
+
+			pathBuf.append(File.separatorChar);
+		} else {
+			pathBuf.append(optionalExecutablePath);
+
+			if (!optionalExecutablePath.endsWith(File.separator)) {
+				pathBuf.append(File.separatorChar);
+			}
+		}
+
+		String executableName = getServerProps().getProperty(
+				EXECUTABLE_NAME_KEY, "mysqld");
+
+		pathBuf.append(executableName);
+
+		return pathBuf.toString();
+	}
+
+	/**
+	 * Builds the list of command-line arguments that will be passed to the
+	 * mysql server to be started.
+	 * 
+	 * @return String the list of command-line arguments.
+	 */
+	private String buildOptionalCommandLine() {
+		StringBuffer commandLineBuf = new StringBuffer();
+
+		if (this.serverProps != null) {
+
+			for (Iterator iter = this.serverProps.keySet().iterator(); iter
+					.hasNext();) {
+				String key = (String) iter.next();
+				String value = this.serverProps.getProperty(key);
+
+				if (!isNonCommandLineArgument(key)) {
+					if (value != null && value.length() > 0) {
+						commandLineBuf.append(" \"");
+						commandLineBuf.append("--");
+						commandLineBuf.append(key);
+						commandLineBuf.append("=");
+						commandLineBuf.append(value);
+						commandLineBuf.append("\"");
+					} else {
+						commandLineBuf.append(" --");
+						commandLineBuf.append(key);
+					}
+				}
+			}
+		}
+
+		return commandLineBuf.toString();
+	}
+
+	/**
+	 * Returns true if the property does not belong as a command-line argument
+	 * 
+	 * @return boolean if the property should not be a command-line argument.
+	 */
+	private boolean isNonCommandLineArgument(String propName) {
+		return propName.equals(EXECUTABLE_NAME_KEY)
+				|| propName.equals(EXECUTABLE_PATH_KEY);
+	}
+
+	/**
+	 * Lazily creates a list of system properties.
+	 * 
+	 * @return Properties the properties from System.getProperties()
+	 */
+	private synchronized Properties getSystemProperties() {
+		if (this.systemProps == null) {
+			this.systemProps = System.getProperties();
+		}
+
+		return this.systemProps;
+	}
+
+	/**
+	 * Is this ServerController running on a Windows operating system?
+	 * 
+	 * @return boolean if this ServerController is running on Windows
+	 */
+	private boolean runningOnWindows() {
+		return StringUtils.indexOfIgnoreCase(getSystemProperties().getProperty(
+				"os.name"), "WINDOWS") != -1;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/TimezoneDump.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/TimezoneDump.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/TimezoneDump.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,86 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package com.mysql.jdbc.util;
+
+import com.mysql.jdbc.TimeUtil;
+
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+
+/**
+ * Dumps the timezone of the MySQL server represented by the JDBC url given on
+ * the commandline (or localhost/test if none provided).
+ * 
+ * @author Mark Matthews
+ */
+public class TimezoneDump {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	private static final String DEFAULT_URL = "jdbc:mysql:///test";
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Constructor for TimezoneDump.
+	 */
+	public TimezoneDump() {
+		super();
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Entry point for program when called from the command line.
+	 * 
+	 * @param args
+	 *            command-line args. Arg 1 is JDBC URL.
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public static void main(String[] args) throws Exception {
+		String jdbcUrl = DEFAULT_URL;
+
+		if ((args.length == 1) && (args[0] != null)) {
+			jdbcUrl = args[0];
+		}
+
+		Class.forName("com.mysql.jdbc.Driver").newInstance();
+
+		ResultSet rs = DriverManager.getConnection(jdbcUrl).createStatement()
+				.executeQuery("SHOW VARIABLES LIKE 'timezone'");
+
+		while (rs.next()) {
+			String timezoneFromServer = rs.getString(2);
+			System.out.println("MySQL timezone name: " + timezoneFromServer);
+
+			String canonicalTimezone = TimeUtil
+					.getCanoncialTimezone(timezoneFromServer);
+			System.out.println("Java timezone name: " + canonicalTimezone);
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,129 @@
+/*
+ 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.util;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.util.Properties;
+
+import com.mysql.jdbc.NonRegisteringDriver;
+
+/**
+ * Creates output directory structure for multi-jvm, multi-url
+ * unit, regression and compliance tests.
+ */
+public class VersionFSHierarchyMaker {
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) throws Exception {
+		if (args.length < 3) {
+			usage();
+			System.exit(1);
+		}
+		
+		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"));
+		
+		String jvmSubdirName = jvmVendor + "-" + jvmVersion;
+		String osSubdirName = osName + "-" + osArch + "-" + osVersion;
+		
+		File baseDir = new File(args[1]);
+		File mysqlVersionDir = new File(baseDir, mysqlVersion);
+		File osVersionDir = new File(mysqlVersionDir, osSubdirName);
+		File jvmVersionDir = new File(osVersionDir, jvmSubdirName);
+		
+		jvmVersionDir.mkdirs();
+
+		
+		FileOutputStream pathOut = null;
+		
+		try {
+			String propsOutputPath = args[2];
+			pathOut = new FileOutputStream(propsOutputPath);
+			String baseDirStr = baseDir.getAbsolutePath();
+			String jvmVersionDirStr = jvmVersionDir.getAbsolutePath();
+			
+			if (jvmVersionDirStr.startsWith(baseDirStr)) {
+				jvmVersionDirStr = jvmVersionDirStr.substring(baseDirStr.length() + 1);
+			}
+
+			pathOut.write(jvmVersionDirStr.getBytes());
+		} finally {
+			if (pathOut != null) {
+				pathOut.flush();
+				pathOut.close();
+			}
+		}
+	}
+
+	public static String removeWhitespaceChars(String input) {
+		if (input == null) {
+			return input;
+		}
+		
+		int strLen = input.length();
+		
+		StringBuffer output = new StringBuffer(strLen);
+		
+		for (int i = 0; i < strLen; i++) {
+			char c = input.charAt(i);
+			if (!Character.isDigit(c) && !Character.isLetter(c)) {
+				if (Character.isWhitespace(c)) {
+					output.append("_");
+				} else {
+					output.append(".");
+				}
+			} else {
+				output.append(c);
+			}
+		}
+		
+		return output.toString();
+	}
+	
+	private static void usage() {
+		System.err.println("Creates a fs hierarchy representing MySQL version, OS version and JVM version.");
+		System.err.println("Stores the full path as 'outputDirectory' property in file 'directoryPropPath'");
+		System.err.println();
+		System.err.println("Usage: java VersionFSHierarchyMaker unit|compliance baseDirectory directoryPropPath");
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/connPropsToDocbook.xsl
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/connPropsToDocbook.xsl	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/connPropsToDocbook.xsl	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,94 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<!-- 
+
+     This stylesheet converts the output of com.mysql.jdbc.util.PropertiesDocGenerator
+     to a DocBook table for inclusion in the product manual.
+
+-->
+
+<xsl:template match="ConnectionProperties">
+	<xsl:apply-templates select = "PropertyCategory" />
+</xsl:template>
+
+<xsl:template match="PropertyCategory">
+	<xsl:element name="formalpara">
+		<xsl:element name="title">
+			<xsl:value-of select="@name" />
+		</xsl:element>
+		<xsl:element name="para">
+			<xsl:element name="informaltable">
+				<xsl:element name="tgroup">
+					<xsl:attribute name="cols">4</xsl:attribute>
+					<xsl:element name="colspec">
+						<xsl:attribute name="colwidth">30*</xsl:attribute>
+						<xsl:attribute name="colname">cj_propstbl_prop_name</xsl:attribute>
+					</xsl:element>
+					<xsl:element name="colspec">
+						<xsl:attribute name="colwidth">50*</xsl:attribute>
+						<xsl:attribute name="colname">cj_propstbl_prop_defn</xsl:attribute>
+					</xsl:element>
+					<xsl:element name="colspec">
+						<xsl:attribute name="colwidth">10*</xsl:attribute>
+						<xsl:attribute name="colname">cj_propstbl_default</xsl:attribute>
+					</xsl:element>
+					<xsl:element name="colspec">
+						<xsl:attribute name="colwidth">10*</xsl:attribute>
+						<xsl:attribute name="colname">cj_propstbl_since_version</xsl:attribute>
+					</xsl:element>
+					<xsl:element name="tbody">
+						<xsl:element name="row">
+							<xsl:element name="entry">
+								<xsl:element name="emphasis">
+									<xsl:attribute name="role">bold</xsl:attribute>
+									Property Name
+								</xsl:element>
+							</xsl:element>
+							<xsl:element name="entry">
+								<xsl:element name="emphasis">
+									<xsl:attribute name="role">bold</xsl:attribute>
+								Definition	
+								</xsl:element>
+							</xsl:element>
+							<xsl:element name="entry">
+								<xsl:element name="emphasis">
+									<xsl:attribute name="role">bold</xsl:attribute>
+								Default Value	
+								</xsl:element>
+							</xsl:element>
+							<xsl:element name="entry">
+								<xsl:element name="emphasis">
+									<xsl:attribute name="role">bold</xsl:attribute>
+								Since Version	
+								</xsl:element>
+							</xsl:element>
+						</xsl:element>
+
+						<xsl:apply-templates select = "./Property"/>
+					</xsl:element>
+				</xsl:element> <!-- end tgroup -->
+			</xsl:element> <!-- end informaltable -->
+		</xsl:element> <!-- end para -->
+	</xsl:element> <!-- end formalpara -->
+	
+</xsl:template>
+
+<xsl:template match="Property">
+	<xsl:element name="row">
+		<xsl:element name="entry">
+			<xsl:value-of select="@name" />
+		</xsl:element>
+		<xsl:element name="entry">
+			<xsl:value-of select="." />
+		</xsl:element>
+		<xsl:element name="entry">
+			<xsl:value-of select="@default" />
+		</xsl:element>
+		<xsl:element name="entry">
+			<xsl:value-of select="@since" />
+		</xsl:element>
+	</xsl:element>
+</xsl:template>
+
+</xsl:stylesheet>

Added: branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/errorMapToDocbook.xsl
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/errorMapToDocbook.xsl	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/errorMapToDocbook.xsl	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,47 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+<!-- 
+
+     This stylesheet converts the output of com.mysql.jdbc.util.ErrorMappingsDocGenerator
+     to a DocBook table for inclusion in the product manual.
+
+-->
+
+<xsl:template match="ErrorMappings">
+	<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="thead">
+    			<xsl:element name="row">
+    				<xsl:element name="entry">MySQL Error Number</xsl:element>
+    				<xsl:element name="entry">MySQL Error Name</xsl:element>
+    				<xsl:element name="entry">Legacy (X/Open) SQLState</xsl:element>
+    				<xsl:element name="entry">SQL Standard SQLState</xsl:element>
+				</xsl:element> <!-- row -->
+			</xsl:element> <!-- thead -->
+			<xsl:element name="tbody">
+				<xsl:apply-templates select = "ErrorMapping" />
+			</xsl:element> <!-- tbody -->
+		</xsl:element> <!-- tgroup -->
+	</xsl:element>
+</xsl:template>
+
+<xsl:template match="ErrorMapping">
+	<xsl:element name="row">
+		<xsl:element name="entry">
+			<xsl:value-of select="@mysqlErrorNumber" />
+		</xsl:element>
+		<xsl:element name="entry">
+			<xsl:value-of select="@mysqlErrorName" />
+		</xsl:element>
+		<xsl:element name="entry">
+			<xsl:value-of select="@legacySqlState" />
+		</xsl:element>
+		<xsl:element name="entry">
+			<xsl:value-of select="@sql92SqlState" />
+		</xsl:element>
+	</xsl:element>
+</xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/placeholder.txt
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/placeholder.txt	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/placeholder.txt	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,5 @@
+This file is here to enable this directory to be checked into the
+source repository.
+
+The ant build script will copy the latest version of the documentation
+into this directory.
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/pom.xml
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/pom.xml	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/doc/sources/pom.xml	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,37 @@
+<project>
+
+    <modelVersion>4.0.0</modelVersion>
+    <groupId>mysql</groupId>
+    <artifactId>mysql-connector-java</artifactId>
+    <version>@MYSQL_CJ_MAJOR_VERSION at .@MYSQL_CJ_MINOR_VERSION at .@MYSQL_CJ_SUBMINOR_VERSION@</version>
+    <packaging>jar</packaging>
+
+    <name>MySQL java connector</name>
+    <description>MySQL java connector</description>
+    
+    <licenses>
+    	<license>
+      		<name>The GNU General Public License, Version 2</name>
+      		<url>http://www.gnu.org/licenses/gpl.txt</url>
+      		<distribution>repo</distribution>
+      		<comments>MySQL Connector/J contains exceptions to GPL requirements when linking with other components
+      		that are licensed under OSI-approved open source licenses, see EXCEPTIONS-CONNECTOR-J
+      		in this distribution for more details.</comments>
+    	</license>
+	</licenses>
+  
+    <url>http://dev.mysql.com/usingmysql/java/</url>
+
+    <scm>
+        <connection>
+            scm:svn:http://svn.mysql.com/svnpublic/connector-j/trunk/connector-j
+        </connection>
+        <developerConnection>
+            scm:svn:http://svn.mysql.com/svnpublic/connector-j/trunk/connector-j
+        </developerConnection>
+        <url>
+            http://svn.mysql.com/svnpublic/connector-j/trunk/connector-j
+        </url>
+    </scm>
+
+</project>

Added: branches/mysql-connector-java/upstream/5.0.4/src/lib/Commons-Logging-LICENSE.txt
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/lib/Commons-Logging-LICENSE.txt	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/lib/Commons-Logging-LICENSE.txt	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed 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.

Added: branches/mysql-connector-java/upstream/5.0.4/src/lib/LICENSE-AspectJ.html
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/lib/LICENSE-AspectJ.html	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/lib/LICENSE-AspectJ.html	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,152 @@
+<html>
+
+<head>
+<title>AspectJ License</title>
+</head>
+
+<BODY BGCOLOR="white">
+
+<h2 align="center"><b>AspectJ<sup><small>TM</small></sup>
+                      Compiler and Core Tools License</b></h2>
+
+<p>This is a binary-only release.&nbsp; Source code
+is available from
+<a href="http://eclipse.org/aspectj">http://eclipse.org/aspectj</a></p>
+
+<p>The AspectJ compiler and core tools are distributed under the
+Common Public License version 1.0 available
+   <a href="http://eclipse.org/legal/cpl-v10.html">here</a>
+   and copied below.
+ This license has been approved by
+the <a href="http://www.opensource.org">Open Source Initiative</a> as
+conforming to the <a href="http://www.opensource.org/osd.html">Open
+Source Definition</a>. 
+More information about the history and rationale behind this license 
+can be found at the
+      <a href="http://eclipse.org">eclipse web site</a>.</p>
+
+<p>
+Those portions of this distribution in the <code>org.apache</code> 
+Java namespace are available under the terms of 
+the Apache Software License, Version 1.1
+(See <a href="http://jakarta.apache.org">
+        http://jakarta.apache.org</a>).
+</p>
+<hr>
+
+<P ALIGN="CENTER"><B>Common Public License - v 1.0</B>
+<P><B></B><FONT SIZE="3"></FONT>
+<P><FONT SIZE="3"></FONT><FONT SIZE="2">THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC LICENSE ("AGREEMENT").  ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>1.  DEFINITIONS</B></FONT>
+<P><FONT SIZE="2">"Contribution" means:</FONT>
+
+<UL><FONT SIZE="2">a) in the case of the initial Contributor, the initial code and documentation distributed under this Agreement, and<BR CLEAR="LEFT">
+b) in the case of each subsequent Contributor:</FONT></UL>
+
+
+<UL><FONT SIZE="2">i)	 	changes to the Program, and</FONT></UL>
+
+
+<UL><FONT SIZE="2">ii)		additions to the Program;</FONT></UL>
+
+
+<UL><FONT SIZE="2">where such changes and/or additions to the Program originate from and are distributed by that particular Contributor.  </FONT><FONT SIZE="2">A Contribution 'originates' from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf.  </FONT><FONT SIZE="2">Contributions do not include additions to the Program which:  (i) are separate modules of software distributed in conjunction with the Program under their own license agreement, and (ii) are not derivative works of the Program.  </FONT></UL>
+
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Contributor" means any person or entity that distributes the Program.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Licensed Patents " mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program.  </FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">"Program" means the Contributions distributed in accordance with this Agreement.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">"Recipient" means anyone who receives the Program under this Agreement, including all Contributors.</FONT>
+<P><FONT SIZE="2"><B></B></FONT>
+<P><FONT SIZE="2"><B>2.  GRANT OF RIGHTS</B></FONT>
+
+<UL><FONT SIZE="2"></FONT><FONT SIZE="2">a)	</FONT><FONT SIZE="2">Subject to the terms of this Agreement, each Contributor hereby grants</FONT><FONT SIZE="2"> Recipient a non-exclusive, worldwide, royalty-free copyright license to</FONT><FONT SIZE="2" COLOR="#FF0000"> </FONT><FONT SIZE="2">reproduce, prepare derivative works of, publicly display, publicly perform, distribute and sublicense the Contribution of such Contributor, if any, and such derivative works, in source code and object code form.</FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT><FONT SIZE="2">b) 	Subject to the terms of this Agreement, each Contributor hereby grants </FONT><FONT SIZE="2">Recipient a non-exclusive, worldwide,</FONT><FONT SIZE="2" COLOR="#008000"> </FONT><FONT SIZE="2">royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in source code and object code form.  This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents.  The patent license shall not apply to any other combinations which include the Contribution.  No hardware per se is licensed hereunder.   </FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2">c)	Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity.  Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise.  As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any.  For example, if a third party patent license is required to allow Recipient to distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program.</FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+
+<UL><FONT SIZE="2">d)	Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. </FONT></UL>
+
+
+<UL><FONT SIZE="2"></FONT></UL>
+
+<P><FONT SIZE="2"><B>3.  REQUIREMENTS</B></FONT>
+<P><FONT SIZE="2"><B></B>A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that:</FONT>
+
+<UL><FONT SIZE="2">a)	it complies with the terms and conditions of this Agreement; and</FONT></UL>
+
+
+<UL><FONT SIZE="2">b)	its license agreement:</FONT></UL>
+
+
+<UL><FONT SIZE="2">i)	effectively disclaims</FONT><FONT SIZE="2"> on behalf of all Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; </FONT></UL>
+
+
+<UL><FONT SIZE="2">ii) 	effectively excludes on behalf of all Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; </FONT></UL>
+
+
+<UL><FONT SIZE="2">iii)</FONT><FONT SIZE="2">	states that any provisions which differ from this Agreement are offered by that Contributor alone and not by any other party; and</FONT></UL>
+
+
+<UL><FONT SIZE="2">iv)	states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange.</FONT><FONT SIZE="2" COLOR="#0000FF"> </FONT><FONT SIZE="2" COLOR="#FF0000"></FONT></UL>
+
+
+<UL><FONT SIZE="2" COLOR="#FF0000"></FONT><FONT SIZE="2"></FONT></UL>
+
+<P><FONT SIZE="2">When the Program is made available in source code form:</FONT>
+
+<UL><FONT SIZE="2">a)	it must be made available under this Agreement; and </FONT></UL>
+
+
+<UL><FONT SIZE="2">b)	a copy of this Agreement must be included with each copy of the Program.  </FONT></UL>
+
+<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT>
+<P><FONT SIZE="2" COLOR="#0000FF"><STRIKE></STRIKE></FONT><FONT SIZE="2">Contributors may not remove or alter any copyright notices contained within the Program.  </FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">Each Contributor must identify itself as the originator of its Contribution, if any, in a manner that reasonably allows subsequent Recipients to identify the originator of the Contribution.  </FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>4.  COMMERCIAL DISTRIBUTION</B></FONT>
+<P><FONT SIZE="2">Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like.  While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors.   Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering.  The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement.  In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations.  The Indemnified Contributor may participate in any such claim at its own expense.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">For example, a Contributor might include the Program in a commercial product offering, Product X.  That Contributor is then a Commercial Contributor.  If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone.  Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2" COLOR="#0000FF"></FONT>
+<P><FONT SIZE="2" COLOR="#0000FF"></FONT><FONT SIZE="2"><B>5.  NO WARRANTY</B></FONT>
+<P><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is</FONT><FONT SIZE="2"> solely responsible for determining the appropriateness of using and distributing </FONT><FONT SIZE="2">the Program</FONT><FONT SIZE="2"> and assumes all risks associated with its exercise of rights under this Agreement</FONT><FONT SIZE="2">, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, </FONT><FONT SIZE="2">programs or equipment, and unavailability or interruption of operations</FONT><FONT SIZE="2">.  </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"><B>6.  DISCLAIMER OF LIABILITY</B></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES </FONT><FONT SIZE="2">(INCLUDING WITHOUT LIMITATION LOST PROFITS),</FONT><FONT SIZE="2"> HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"><B>7.  GENERAL</B></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">If Recipient institutes patent litigation against a Contributor with respect to a patent applicable to software (including a cross-claim or counterclaim in a lawsuit), then any patent licenses granted by that Contributor to such Recipient under this Agreement shall terminate as of the date such litigation is filed.  In addition, if Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance.  If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable.  However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive.  </FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2">Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted  and may only be modified in the following manner. The Agreement Steward reserves the right to </FONT><FONT SIZE="2">publish new versions (including revisions) of this Agreement from time to </FONT><FONT SIZE="2">time. No one other than the Agreement Steward has the right to modify this Agreement. IBM is the initial Agreement Steward.   IBM may assign the responsibility to serve as the Agreement Steward to a suitable separate entity.  </FONT><FONT SIZE="2">Each new version of the Agreement will be given a distinguishing version number.  The Program (including Contributions) may always be distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to distribute the Program (including its Contributions) under the new </FONT><FONT SIZE="2">version.  </FONT><FONT SIZE="2">Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, </FONT><FONT SIZE="2">by implication, estoppel or otherwise</FONT><FONT SIZE="2">.</FONT><FONT SIZE="2">  All rights in the Program not expressly granted under this Agreement are reserved.</FONT>
+<P><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2">This Agreement is governed by the laws of the State of New York and the intellectual property laws of the United States of America. No party to this Agreement will bring a legal action under this Agreement more than one year after the cause of action arose.  Each party waives its rights to a jury trial in any resulting litigation.</FONT>
+<P><FONT SIZE="2"></FONT><FONT SIZE="2"></FONT>
+<P><FONT SIZE="2"></FONT>
+
+<hr>
+</body>
+
+</html>

Added: branches/mysql-connector-java/upstream/5.0.4/src/lib/c3p0-LICENSE
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/lib/c3p0-LICENSE	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/lib/c3p0-LICENSE	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+

Added: branches/mysql-connector-java/upstream/5.0.4/src/lib/jboss-lgpl.txt
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/lib/jboss-lgpl.txt	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/lib/jboss-lgpl.txt	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,504 @@
+		  GNU LESSER GENERAL PUBLIC LICENSE
+		       Version 2.1, February 1999
+
+ Copyright (C) 1991, 1999 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the Lesser GPL.  It also counts
+ as the successor of the GNU Library Public License, version 2, hence
+ the version number 2.1.]
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+  This license, the Lesser General Public License, applies to some
+specially designated software packages--typically libraries--of the
+Free Software Foundation and other authors who decide to use it.  You
+can use it too, but we suggest you first think carefully about whether
+this license or the ordinary General Public License is the better
+strategy to use in any particular case, based on the explanations below.
+
+  When we speak of free software, we are referring to freedom of use,
+not price.  Our General Public Licenses are designed to make sure that
+you have the freedom to distribute copies of free software (and charge
+for this service if you wish); that you receive source code or can get
+it if you want it; that you can change the software and use pieces of
+it in new free programs; and that you are informed that you can do
+these things.
+
+  To protect your rights, we need to make restrictions that forbid
+distributors to deny you these rights or to ask you to surrender these
+rights.  These restrictions translate to certain responsibilities for
+you if you distribute copies of the library or if you modify it.
+
+  For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you.  You must make sure that they, too, receive or can get the source
+code.  If you link other code with the library, you must provide
+complete object files to the recipients, so that they can relink them
+with the library after making changes to the library and recompiling
+it.  And you must show them these terms so they know their rights.
+
+  We protect your rights with a two-step method: (1) we copyright the
+library, and (2) we offer you this license, which gives you legal
+permission to copy, distribute and/or modify the library.
+
+  To protect each distributor, we want to make it very clear that
+there is no warranty for the free library.  Also, if the library is
+modified by someone else and passed on, the recipients should know
+that what they have is not the original version, so that the original
+author's reputation will not be affected by problems that might be
+introduced by others.
+
+  Finally, software patents pose a constant threat to the existence of
+any free program.  We wish to make sure that a company cannot
+effectively restrict the users of a free program by obtaining a
+restrictive license from a patent holder.  Therefore, we insist that
+any patent license obtained for a version of the library must be
+consistent with the full freedom of use specified in this license.
+
+  Most GNU software, including some libraries, is covered by the
+ordinary GNU General Public License.  This license, the GNU Lesser
+General Public License, applies to certain designated libraries, and
+is quite different from the ordinary General Public License.  We use
+this license for certain libraries in order to permit linking those
+libraries into non-free programs.
+
+  When a program is linked with a library, whether statically or using
+a shared library, the combination of the two is legally speaking a
+combined work, a derivative of the original library.  The ordinary
+General Public License therefore permits such linking only if the
+entire combination fits its criteria of freedom.  The Lesser General
+Public License permits more lax criteria for linking other code with
+the library.
+
+  We call this license the "Lesser" General Public License because it
+does Less to protect the user's freedom than the ordinary General
+Public License.  It also provides other free software developers Less
+of an advantage over competing non-free programs.  These disadvantages
+are the reason we use the ordinary General Public License for many
+libraries.  However, the Lesser license provides advantages in certain
+special circumstances.
+
+  For example, on rare occasions, there may be a special need to
+encourage the widest possible use of a certain library, so that it becomes
+a de-facto standard.  To achieve this, non-free programs must be
+allowed to use the library.  A more frequent case is that a free
+library does the same job as widely used non-free libraries.  In this
+case, there is little to gain by limiting the free library to free
+software only, so we use the Lesser General Public License.
+
+  In other cases, permission to use a particular library in non-free
+programs enables a greater number of people to use a large body of
+free software.  For example, permission to use the GNU C Library in
+non-free programs enables many more people to use the whole GNU
+operating system, as well as its variant, the GNU/Linux operating
+system.
+
+  Although the Lesser General Public License is Less protective of the
+users' freedom, it does ensure that the user of a program that is
+linked with the Library has the freedom and the wherewithal to run
+that program using a modified version of the Library.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.  Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library".  The
+former contains code derived from the library, whereas the latter must
+be combined with the library in order to run.
+
+		  GNU LESSER GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License Agreement applies to any software library or other
+program which contains a notice placed by the copyright holder or
+other authorized party saying it may be distributed under the terms of
+this Lesser General Public License (also called "this License").
+Each licensee is addressed as "you".
+
+  A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+  The "Library", below, refers to any such software library or work
+which has been distributed under these terms.  A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language.  (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+  "Source code" for a work means the preferred form of the work for
+making modifications to it.  For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+  Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it).  Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+  
+  1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+  You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+
+  2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) The modified work must itself be a software library.
+
+    b) You must cause the files modified to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    c) You must cause the whole of the work to be licensed at no
+    charge to all third parties under the terms of this License.
+
+    d) If a facility in the modified Library refers to a function or a
+    table of data to be supplied by an application program that uses
+    the facility, other than as an argument passed when the facility
+    is invoked, then you must make a good faith effort to ensure that,
+    in the event an application does not supply such function or
+    table, the facility still operates, and performs whatever part of
+    its purpose remains meaningful.
+
+    (For example, a function in a library to compute square roots has
+    a purpose that is entirely well-defined independent of the
+    application.  Therefore, Subsection 2d requires that any
+    application-supplied function or table used by this function must
+    be optional: if the application does not supply it, the square
+    root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library.  To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License.  (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.)  Do not make any other change in
+these notices.
+
+  Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+  This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+  4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+  If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library".  Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+  However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library".  The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+  When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library.  The
+threshold for this to be true is not precisely defined by law.
+
+  If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work.  (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+  Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+
+  6. As an exception to the Sections above, you may also combine or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+  You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License.  You must supply a copy of this License.  If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License.  Also, you must do one
+of these things:
+
+    a) Accompany the work with the complete corresponding
+    machine-readable source code for the Library including whatever
+    changes were used in the work (which must be distributed under
+    Sections 1 and 2 above); and, if the work is an executable linked
+    with the Library, with the complete machine-readable "work that
+    uses the Library", as object code and/or source code, so that the
+    user can modify the Library and then relink to produce a modified
+    executable containing the modified Library.  (It is understood
+    that the user who changes the contents of definitions files in the
+    Library will not necessarily be able to recompile the application
+    to use the modified definitions.)
+
+    b) Use a suitable shared library mechanism for linking with the
+    Library.  A suitable mechanism is one that (1) uses at run time a
+    copy of the library already present on the user's computer system,
+    rather than copying library functions into the executable, and (2)
+    will operate properly with a modified version of the library, if
+    the user installs one, as long as the modified version is
+    interface-compatible with the version that the work was made with.
+
+    c) Accompany the work with a written offer, valid for at
+    least three years, to give the same user the materials
+    specified in Subsection 6a, above, for a charge no more
+    than the cost of performing this distribution.
+
+    d) If distribution of the work is made by offering access to copy
+    from a designated place, offer equivalent access to copy the above
+    specified materials from the same place.
+
+    e) Verify that the user has already received a copy of these
+    materials or that you have already sent this user a copy.
+
+  For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it.  However, as a special exception,
+the materials to be distributed need not include anything that is
+normally distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+  It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system.  Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+
+  7. You may place library facilities that are a work based on the
+Library side-by-side in a single library together with other library
+facilities not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+    a) Accompany the combined library with a copy of the same work
+    based on the Library, uncombined with any other library
+    facilities.  This must be distributed under the terms of the
+    Sections above.
+
+    b) Give prominent notice with the combined library of the fact
+    that part of it is a work based on the Library, and explaining
+    where to find the accompanying uncombined form of the same work.
+
+  8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License.  Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License.  However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+  9. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Library or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+  10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties with
+this License.
+
+  11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded.  In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+  13. The Free Software Foundation may publish revised and/or new
+versions of the Lesser General Public License from time to time.
+Such new versions will be similar in spirit to the present version,
+but may differ in detail to address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Library
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation.  If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+
+  14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission.  For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this.  Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+			    NO WARRANTY
+
+  15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU.  SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Libraries
+
+  If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change.  You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+  To apply these terms, attach the following notices to the library.  It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the library's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This library is free software; you can redistribute it and/or
+    modify it under the terms of the GNU Lesser General Public
+    License as published by the Free Software Foundation; either
+    version 2.1 of the License, or (at your option) any later version.
+
+    This library is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+    Lesser General Public License for more details.
+
+    You should have received a copy of the GNU Lesser General Public
+    License along with this library; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the
+  library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+  <signature of Ty Coon>, 1 April 1990
+  Ty Coon, President of Vice
+
+That's all there is to it!
+
+

Added: branches/mysql-connector-java/upstream/5.0.4/src/org/gjt/mm/mysql/Driver.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/org/gjt/mm/mysql/Driver.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/org/gjt/mm/mysql/Driver.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,47 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package org.gjt.mm.mysql;
+
+import java.sql.SQLException;
+
+/**
+ * Here for backwards compatibility with MM.MySQL
+ * 
+ * @author Mark Matthews
+ */
+public class Driver extends com.mysql.jdbc.Driver {
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new instance of Driver
+	 * 
+	 * @throws SQLException
+	 *             if a database error occurs.
+	 */
+	public Driver() throws SQLException {
+		super();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/BaseTestCase.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/BaseTestCase.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/BaseTestCase.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,556 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite;
+
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+
+import com.mysql.jdbc.NonRegisteringDriver;
+
+import junit.framework.TestCase;
+
+/**
+ * Base class for all test cases. Creates connections, statements, etc. and
+ * closes them.
+ * 
+ * @author Mark Matthews
+ * @version $Id: BaseTestCase.java 5440 2006-06-27 17:00:53Z 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
+	 */
+	protected static String dbUrl = "jdbc:mysql:///test";
+
+	/** Instance counter */
+	private static int instanceCount = 1;
+
+	/** 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;
+
+	/** The driver to use */
+	protected String dbClass = "com.mysql.jdbc.Driver";
+
+	/** My instance number */
+	private int myInstanceNumber = 0;
+
+	/**
+	 * PreparedStatement to be used in tests, not initialized. Cleaned up in
+	 * tearDown().
+	 */
+	protected PreparedStatement pstmt = null;
+
+	/**
+	 * ResultSet to be used in tests, not initialized. Cleaned up in tearDown().
+	 */
+	protected ResultSet rs = null;
+
+	/**
+	 * Statement to be used in tests, initialized in setUp(). Cleaned up in
+	 * tearDown().
+	 */
+	protected Statement stmt = null;
+
+	private boolean runningOnJdk131 = false;
+	
+	/**
+	 * Creates a new BaseTestCase object.
+	 * 
+	 * @param name
+	 *            The name of the JUnit test case
+	 */
+	public BaseTestCase(String name) {
+		super(name);
+		this.myInstanceNumber = instanceCount++;
+
+		String newDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url");
+
+		if ((newDbUrl != null) && (newDbUrl.trim().length() != 0)) {
+			dbUrl = newDbUrl;
+		} else {
+			String defaultDbUrl = System.getProperty("com.mysql.jdbc.testsuite.url.default");
+			
+			if ((defaultDbUrl != null) && (defaultDbUrl.trim().length() != 0)) {
+				dbUrl = defaultDbUrl;
+			}
+		}
+
+		String newDriver = System
+				.getProperty("com.mysql.jdbc.testsuite.driver");
+
+		if ((newDriver != null) && (newDriver.trim().length() != 0)) {
+			this.dbClass = newDriver;
+		}
+		
+		try {
+			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);
+
+		StringBuffer createSql = new StringBuffer(tableName.length()
+				+ columnsAndOtherStuff.length() + 10);
+		createSql.append("CREATE TABLE ");
+		createSql.append(tableName);
+		createSql.append(" ");
+		createSql.append(columnsAndOtherStuff);
+		this.stmt.executeUpdate(createSql.toString());
+	}
+
+	protected void dropTable(String tableName) throws SQLException {
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS " + tableName);
+	}
+
+	protected Connection getAdminConnection() throws SQLException {
+		return getAdminConnectionWithProps(new Properties());
+	}
+
+	protected Connection getAdminConnectionWithProps(Properties props)
+			throws SQLException {
+		String adminUrl = System.getProperty(ADMIN_CONNECTION_PROPERTY_NAME);
+
+		if (adminUrl != null) {
+			return DriverManager.getConnection(adminUrl, props);
+		} else {
+			return null;
+		}
+	}
+
+	/**
+	 * Returns a new connection with the given properties
+	 * 
+	 * @param props
+	 *            the properties to use (the URL will come from the standard for
+	 *            this testcase).
+	 * 
+	 * @return a new connection using the given properties.
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	protected Connection getConnectionWithProps(Properties props)
+			throws SQLException {
+		return DriverManager.getConnection(dbUrl, props);
+	}
+
+	protected Connection getConnectionWithProps(String url, Properties props)
+			throws SQLException {
+		return DriverManager.getConnection(url, props);
+	}
+
+	/**
+	 * Returns the per-instance counter (for messages when multi-threading
+	 * stress tests)
+	 * 
+	 * @return int the instance number
+	 */
+	protected int getInstanceNumber() {
+		return this.myInstanceNumber;
+	}
+
+	protected String getMysqlVariable(Connection c, String variableName)
+			throws SQLException {
+		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 value.toString();
+		}
+
+		return null;
+
+	}
+
+	/**
+	 * Returns the named MySQL variable from the currently connected server.
+	 * 
+	 * @param variableName
+	 *            the name of the variable to return
+	 * 
+	 * @return the value of the given variable, or NULL if it doesn't exist
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	protected String getMysqlVariable(String variableName) throws SQLException {
+		return getMysqlVariable(this.conn, variableName);
+	}
+
+	/**
+	 * Returns the properties that represent the default URL used for
+	 * connections for all testcases.
+	 * 
+	 * @return properties parsed from com.mysql.jdbc.testsuite.url
+	 * 
+	 * @throws SQLException
+	 *             if parsing fails
+	 */
+	protected Properties getPropertiesFromTestsuiteUrl() throws SQLException {
+		Properties props = new NonRegisteringDriver().parseURL(dbUrl, null);
+
+		String hostname = props
+				.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
+
+		if (hostname == null) {
+			props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY,
+					"localhost");
+		} else if (hostname.startsWith(":")) {
+			props.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY,
+					"localhost");
+			props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, hostname
+					.substring(1));
+		}
+
+		String portNumber = props
+				.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY);
+
+		if (portNumber == null) {
+			props.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, "3306");
+		}
+
+		return props;
+	}
+
+	protected int getRowCount(String tableName) throws SQLException {
+		ResultSet countRs = null;
+
+		try {
+			countRs = this.stmt.executeQuery("SELECT COUNT(*) FROM "
+					+ tableName);
+
+			countRs.next();
+
+			return countRs.getInt(1);
+		} finally {
+			if (countRs != null) {
+				countRs.close();
+			}
+		}
+	}
+
+	protected Object getSingleIndexedValueWithQuery(Connection c,
+			int columnIndex, String query) throws SQLException {
+		ResultSet valueRs = null;
+
+		Statement svStmt = null;
+
+		try {
+			svStmt = c.createStatement();
+
+			valueRs = svStmt.executeQuery(query);
+
+			if (!valueRs.next()) {
+				return null;
+			}
+
+			return valueRs.getObject(columnIndex);
+		} finally {
+			if (valueRs != null) {
+				valueRs.close();
+			}
+
+			if (svStmt != null) {
+				svStmt.close();
+			}
+		}
+	}
+
+	protected Object getSingleIndexedValueWithQuery(int columnIndex,
+			String query) throws SQLException {
+		return getSingleIndexedValueWithQuery(this.conn, columnIndex, query);
+	}
+
+	protected Object getSingleValue(String tableName, String columnName,
+			String whereClause) throws SQLException {
+		return getSingleValueWithQuery("SELECT " + columnName + " FROM "
+				+ tableName + ((whereClause == null) ? "" : whereClause));
+	}
+
+	protected Object getSingleValueWithQuery(String query) throws SQLException {
+		return getSingleIndexedValueWithQuery(1, query);
+	}
+
+	protected boolean isAdminConnectionConfigured() {
+		return System.getProperty(ADMIN_CONNECTION_PROPERTY_NAME) != null;
+	}
+
+	protected boolean isServerRunningOnWindows() throws SQLException {
+		return (getMysqlVariable("datadir").indexOf('\\') != -1);
+	}
+
+	public void logDebug(String message) {
+		if (System.getProperty("com.mysql.jdbc.testsuite.noDebugOutput") == null) {
+			System.err.println(message);
+		}
+	}
+
+	protected File newTempBinaryFile(String name, long size) throws IOException {
+		File tempFile = File.createTempFile(name, "tmp");
+		tempFile.deleteOnExit();
+		FileOutputStream fos = new FileOutputStream(tempFile);
+		BufferedOutputStream bos = new BufferedOutputStream(fos);
+		for (long i = 0; i < size; i++) {
+			bos.write((byte) i);
+		}
+		bos.close();
+		assertTrue(tempFile.exists());
+		assertEquals(size, tempFile.length());
+		return tempFile;
+	}
+
+	protected final boolean runLongTests() {
+		return runTestIfSysPropDefined("com.mysql.jdbc.testsuite.runLongTests");
+	}
+
+	/**
+	 * Checks whether a certain system property is defined, in order to
+	 * run/not-run certain tests
+	 * 
+	 * @param propName
+	 *            the property name to check for
+	 * 
+	 * @return true if the property is defined.
+	 */
+	protected boolean runTestIfSysPropDefined(String propName) {
+		String prop = System.getProperty(propName);
+
+		return (prop != null) && (prop.length() > 0);
+	}
+
+	protected boolean runMultiHostTests() {
+		return !runTestIfSysPropDefined(NO_MULTI_HOST_PROPERTY_NAME);
+	}
+
+	/**
+	 * Creates resources used by all tests.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void setUp() throws Exception {
+		System.out.println("Loading JDBC driver '" + this.dbClass + "'");
+		Class.forName(this.dbClass).newInstance();
+		System.out.println("Done.\n");
+		createdTables = new ArrayList();
+
+		// System.out.println("Establishing connection to database '" + dbUrl
+		// + "'");
+
+		if (this.dbClass.equals("gwe.sql.gweMysqlDriver")) {
+			try {
+				this.conn = DriverManager.getConnection(dbUrl, "", "");
+			} catch (Exception ex) {
+				ex.printStackTrace();
+				fail();
+			}
+		} else {
+			try {
+				this.conn = DriverManager.getConnection(dbUrl);
+			} catch (Exception ex) {
+				ex.printStackTrace();
+				fail();
+			}
+		}
+
+		System.out.println("Done.\n");
+		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;
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+			}
+		}
+	}
+
+	/**
+	 * Destroys resources created during the test case.
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void tearDown() throws Exception {
+		if (this.rs != null) {
+			try {
+				this.rs.close();
+			} catch (SQLException SQLE) {
+				;
+			}
+		}
+
+		for (int i = 0; i < createdTables.size(); i++) {
+			try {
+				dropTable((String) createdTables.get(i));
+			} catch (SQLException SQLE) {
+				;
+			}
+		}
+
+		if (this.stmt != null) {
+			try {
+				this.stmt.close();
+			} catch (SQLException SQLE) {
+				;
+			}
+		}
+
+		if (this.pstmt != null) {
+			try {
+				this.pstmt.close();
+			} catch (SQLException SQLE) {
+				;
+			}
+		}
+
+		if (this.conn != null) {
+			try {
+				this.conn.close();
+			} catch (SQLException SQLE) {
+				;
+			}
+		}
+	}
+
+	/**
+	 * Checks whether the database we're connected to meets the given version
+	 * minimum
+	 * 
+	 * @param major
+	 *            the major version to meet
+	 * @param minor
+	 *            the minor version to meet
+	 * 
+	 * @return boolean if the major/minor is met
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	protected boolean versionMeetsMinimum(int major, int minor)
+			throws SQLException {
+		return versionMeetsMinimum(major, minor, 0);
+	}
+
+	/**
+	 * Checks whether the database we're connected to meets the given version
+	 * minimum
+	 * 
+	 * @param major
+	 *            the major version to meet
+	 * @param minor
+	 *            the minor version to meet
+	 * 
+	 * @return boolean if the major/minor is met
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs.
+	 */
+	protected boolean versionMeetsMinimum(int major, int minor, int subminor)
+			throws SQLException {
+		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) {
+				// ignore
+			}
+		}
+	}
+	
+	protected boolean isRunningOnJRockit() {
+		String vmVendor = System.getProperty("java.vm.vendor");
+		
+		return (vmVendor != null && vmVendor.toUpperCase(Locale.US).startsWith("BEA"));
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/BasePerfTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/BasePerfTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/BasePerfTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,237 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.perf;
+
+import testsuite.BaseTestCase;
+
+import java.text.NumberFormat;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Base class for performance test cases. Handles statistics.
+ * 
+ * @author Mark Matthews
+ */
+public abstract class BasePerfTest extends BaseTestCase {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	/**
+	 * Confidence interval lookup table, indexed by degrees of freedom at 95%.
+	 */
+	private static final double[] T95 = { 12.706, 4.303, 3.182, 2.776, 2.571,
+			2.447, 2.365, 2.306, 2.262, 2.228, 2.201, 2.179, 2.160, 2.145,
+			2.131, 2.120, 2.110, 2.101, 2.093, 2.086, 2.080, 2.074, 2.069,
+			2.064, 2.060, 2.056, 2.052, 2.048, 2.045, 2.042 };
+
+	/**
+	 * Confidence interval lookup table, indexed by degrees of freedom at 99%.
+	 */
+	private static final double[] T99 = { 63.657, 9.925, 5.841, 4.604, 4.032,
+			3.707, 3.499, 3.355, 3.250, 3.169, 3.106, 3.055, 3.012, 2.977,
+			2.947, 2.921, 2.898, 2.878, 2.861, 2.845, 2.831, 2.819, 2.807,
+			2.797, 2.787, 2.779, 2.771, 2.763, 2.756, 2.750 };
+
+	static NumberFormat numberFormatter = NumberFormat.getInstance();
+
+	static {
+		numberFormatter.setMaximumFractionDigits(4);
+		numberFormatter.setMinimumFractionDigits(4);
+	}
+
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	/**
+	 * List of values for each iteration
+	 */
+	private List testValuesList = new ArrayList();
+
+	private double confidenceLevel = 95; // 95% by default
+
+	private double confidenceValue = 0;
+
+	private double intervalWidth = 0.1;
+
+	private double meanValue = 0;
+
+	private double squareSumValue = 0;
+
+	private double sumValue = 0;
+
+	private double variationValue = 0;
+
+	/**
+	 * The number of iterations that we have performed
+	 */
+	private int numIterations = 0;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new BasePerfTest object.
+	 * 
+	 * @param name
+	 *            the testcase name to perform.
+	 */
+	public BasePerfTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Returns the meanValue.
+	 * 
+	 * @return double
+	 */
+	public double getMeanValue() {
+		return this.meanValue;
+	}
+
+	/**
+	 * Sub-classes should override this to perform the operation to be measured.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	protected abstract void doOneIteration() throws Exception;
+
+	/**
+	 * Returns the current confidence level.
+	 * 
+	 * @return the current confindence level.
+	 */
+	protected double getCurrentConfidence() {
+		return (this.intervalWidth - this.confidenceValue) * 100;
+	}
+
+	/**
+	 * Returns the current margin of error.
+	 * 
+	 * @return the current margin of error.
+	 */
+	protected double getMarginOfError() {
+		return getConfidenceLookup()
+				* (getStandardDeviationP() / Math.sqrt(this.numIterations));
+	}
+
+	/**
+	 * Returns the current STDDEV.
+	 * 
+	 * @return the current STDDEV
+	 */
+	protected double getStandardDeviationP() {
+		if (this.numIterations < 1) {
+			return 0;
+		}
+
+		return Math
+				.sqrt(((this.numIterations * this.squareSumValue) - (this.sumValue * this.sumValue))
+						/ (this.numIterations * this.numIterations));
+	}
+
+	/**
+	 * Adds one test result to the statistics.
+	 * 
+	 * @param value
+	 *            a single result representing the value being measured in the
+	 *            test.
+	 */
+	protected void addResult(double value) {
+		this.numIterations++;
+		this.testValuesList.add(new Double(value));
+
+		this.sumValue += value;
+		this.squareSumValue += (value * value);
+		this.meanValue = this.sumValue / this.numIterations;
+		this.variationValue = (this.squareSumValue / this.numIterations)
+				- (this.meanValue * this.meanValue);
+
+		// Can only have confidence when more than one test
+		// has been completed
+		if (this.numIterations > 1) {
+			this.confidenceValue = this.intervalWidth
+					- ((2.0 * getConfidenceLookup() * Math
+							.sqrt(this.variationValue
+									/ (this.numIterations - 1.0))) / this.meanValue);
+		}
+	}
+
+	/**
+	 * Calls doIteration() the <code>numIterations</code> times, displaying
+	 * the mean, std, margin of error and confidence level.
+	 * 
+	 * @param numIterations
+	 *            the number of iterations to perform ( < 30)
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	protected void doIterations(int numIterations) throws Exception {
+		for (int i = 0; i < numIterations; i++) {
+			doOneIteration();
+		}
+	}
+
+	/**
+	 * Reports the current results to STDOUT, preceeded by
+	 * <code>additionalMessage</code> if not null.
+	 * 
+	 * @param additionalMessage
+	 *            the additional message to print, or null if no message.
+	 */
+	protected synchronized void reportResults(String additionalMessage) {
+		StringBuffer messageBuf = new StringBuffer();
+
+		if (additionalMessage != null) {
+			messageBuf.append(additionalMessage);
+			messageBuf.append(": ");
+		}
+
+		messageBuf.append(" mean: ");
+		messageBuf.append(numberFormatter.format(this.meanValue));
+		messageBuf.append(" stdevp: ");
+		messageBuf.append(numberFormatter.format(getStandardDeviationP()));
+		messageBuf.append(" m-o-e: ");
+		messageBuf.append(numberFormatter.format(getMarginOfError()));
+
+		System.out.println(messageBuf.toString());
+	}
+
+	private double getConfidenceLookup() {
+		if (this.confidenceLevel == 95) {
+			return T95[this.numIterations - 1];
+		} else if (this.confidenceLevel == 99) {
+			return T99[this.numIterations - 1];
+		} else {
+			throw new IllegalArgumentException(
+					"Confidence level must be 95 or 99");
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/LoadStorePerfTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/LoadStorePerfTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/LoadStorePerfTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,366 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.perf;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import java.text.NumberFormat;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Simple performance testing unit test.
+ * 
+ * @author Mark Matthews
+ */
+public class LoadStorePerfTest extends BasePerfTest {
+	/** The table type to use (only for MySQL), 'HEAP' by default */
+	private String tableType = "HEAP";
+
+	private boolean takeMeasurements = false;
+
+	private boolean useColumnNames = false;
+
+	private boolean largeResults = false;
+
+	/**
+	 * Constructor for LoadStorePerfTest.
+	 * 
+	 * @param name
+	 *            the name of the test to run
+	 */
+	public LoadStorePerfTest(String name) {
+		super(name);
+
+		String newTableType = System
+				.getProperty("com.mysql.jdbc.test.tabletype");
+
+		this.largeResults = "TRUE"
+				.equalsIgnoreCase(System
+						.getProperty("com.mysql.jdbc.testsuite.loadstoreperf.useBigResults"));
+
+		if ((newTableType != null) && (newTableType.length() > 0)) {
+			this.tableType = newTableType;
+
+			System.out.println("Using specified table type of '"
+					+ this.tableType + "'");
+		}
+	}
+
+	/**
+	 * Runs all tests in this test case
+	 * 
+	 * @param args
+	 *            ignored
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public static void main(String[] args) throws Exception {
+		new LoadStorePerfTest("test1000Transactions").run();
+	}
+
+	/**
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE perfLoadStore");
+		} catch (SQLException sqlEx) {
+			// ignore
+		}
+
+		String dateTimeType = "DATETIME";
+
+		if (BaseTestCase.dbUrl.indexOf("oracle") != -1) {
+			dateTimeType = "TIMESTAMP";
+		}
+
+		//
+		// Approximate a run-of-the-mill entity in a business application
+		//
+		String query = "CREATE TABLE perfLoadStore (priKey INT NOT NULL, "
+				+ "fk1 INT NOT NULL, " + "fk2 INT NOT NULL, " + "dtField "
+				+ dateTimeType + ", " + "charField1 CHAR(32), "
+				+ "charField2 CHAR(32), " + "charField3 CHAR(32), "
+				+ "charField4 CHAR(32), " + "intField1 INT, "
+				+ "intField2 INT, " + "intField3 INT, " + "intField4 INT, "
+				+ "doubleField1 DECIMAL," + "doubleField2 DOUBLE,"
+				+ "doubleField3 DOUBLE," + "doubleField4 DOUBLE,"
+				+ "PRIMARY KEY (priKey))";
+
+		if (BaseTestCase.dbUrl.indexOf("mysql") != -1) {
+			query += (" TYPE=" + this.tableType);
+		}
+
+		this.stmt.executeUpdate(query);
+
+		String currentDateValue = "NOW()";
+
+		if (BaseTestCase.dbUrl.indexOf("sqlserver") != -1) {
+			currentDateValue = "GETDATE()";
+		}
+
+		if (BaseTestCase.dbUrl.indexOf("oracle") != -1) {
+			currentDateValue = "CURRENT_TIMESTAMP";
+		}
+
+		int numLoops = 1;
+
+		if (this.largeResults) {
+			numLoops = 32;
+		}
+
+		System.out.println("Inserting " + numLoops + " rows to retrieve...");
+
+		for (int i = 0; i < numLoops; i++) {
+			this.stmt.executeUpdate("INSERT INTO perfLoadStore (" + "priKey, "
+					+ "fk1, " + "fk2, " + "dtField, " + "charField1, "
+					+ "charField2, " + "charField3, " + "charField4, "
+					+ "intField1, " + "intField2, " + "intField3, "
+					+ "intField4, " + "doubleField1," + "doubleField2,"
+					+ "doubleField3," + "doubleField4" + ") VALUES (" + i + "," // priKey
+					+ "2," // fk1
+					+ "3," // fk2
+					+ currentDateValue + "," // dtField
+					+ "'0123456789ABCDEF0123456789ABCDEF'," // charField1
+					+ "'0123456789ABCDEF0123456789ABCDEF'," // charField2
+					+ "'0123456789ABCDEF0123456789ABCDEF'," // charField3
+					+ "'0123456789ABCDEF0123456789ABCDEF'," // charField4
+					+ "7," // intField1
+					+ "8," // intField2
+					+ "9," // intField3
+					+ "10," // intField4
+					+ "1.20," // doubleField1
+					+ "2.30," // doubleField2
+					+ "3.40," // doubleField3
+					+ "4.50" // doubleField4
+					+ ")");
+		}
+	}
+
+	/**
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	public void tearDown() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE perfLoadStore");
+		} catch (SQLException sqlEx) {
+			// ignore
+		}
+
+		super.tearDown();
+	}
+
+	/**
+	 * Tests and times 1000 load/store type transactions
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void test1000Transactions() throws Exception {
+		this.takeMeasurements = false;
+		warmUp();
+		this.takeMeasurements = true;
+		doIterations(29);
+
+		reportResults("\n\nResults for instance # 1: ");
+	}
+
+	/**
+	 * Runs one iteration of the test.
+	 * 
+	 * @see testsuite.perf.BasePerfTest#doOneIteration()
+	 */
+	protected void doOneIteration() throws Exception {
+		PreparedStatement pStmtStore = this.conn
+				.prepareStatement("UPDATE perfLoadStore SET " + "priKey = ?, "
+						+ "fk1 = ?, " + "fk2 = ?, " + "dtField = ?, "
+						+ "charField1 = ?, " + "charField2 = ?, "
+						+ "charField3 = ?, " + "charField4 = ?, "
+						+ "intField1 = ?, " + "intField2 = ?, "
+						+ "intField3 = ?, " + "intField4 = ?, "
+						+ "doubleField1 = ?," + "doubleField2 = ?,"
+						+ "doubleField3 = ?," + "doubleField4 = ?"
+						+ " WHERE priKey=?");
+		PreparedStatement pStmtCheck = this.conn
+				.prepareStatement("SELECT COUNT(*) FROM perfLoadStore WHERE priKey=?");
+		PreparedStatement pStmtLoad = null;
+
+		if (this.largeResults) {
+			pStmtLoad = this.conn.prepareStatement("SELECT " + "priKey, "
+					+ "fk1, " + "fk2, " + "dtField, " + "charField1, "
+					+ "charField2, " + "charField3, " + "charField4, "
+					+ "intField1, " + "intField2, " + "intField3, "
+					+ "intField4, " + "doubleField1," + "doubleField2, "
+					+ "doubleField3," + "doubleField4" + " FROM perfLoadStore");
+		} else {
+			pStmtLoad = this.conn.prepareStatement("SELECT " + "priKey, "
+					+ "fk1, " + "fk2, " + "dtField, " + "charField1, "
+					+ "charField2, " + "charField3, " + "charField4, "
+					+ "intField1, " + "intField2, " + "intField3, "
+					+ "intField4, " + "doubleField1," + "doubleField2, "
+					+ "doubleField3," + "doubleField4"
+					+ " FROM perfLoadStore WHERE priKey=?");
+		}
+
+		NumberFormat numFormatter = NumberFormat.getInstance();
+		numFormatter.setMaximumFractionDigits(4);
+		numFormatter.setMinimumFractionDigits(4);
+
+		int transactionCount = 5000;
+
+		if (this.largeResults) {
+			transactionCount = 50;
+		}
+
+		long begin = System.currentTimeMillis();
+
+		for (int i = 0; i < transactionCount; i++) {
+			this.conn.setAutoCommit(false);
+			pStmtCheck.setInt(1, 1);
+			this.rs = pStmtCheck.executeQuery();
+
+			while (this.rs.next()) {
+				this.rs.getInt(1);
+			}
+
+			this.rs.close();
+
+			if (!this.largeResults) {
+				pStmtLoad.setInt(1, 1);
+			}
+
+			this.rs = pStmtLoad.executeQuery();
+
+			if (this.rs.next()) {
+				int key = this.rs.getInt(1);
+
+				if (!this.useColumnNames) {
+					pStmtStore.setInt(1, key); // priKey
+					pStmtStore.setInt(2, this.rs.getInt(2)); // fk1
+					pStmtStore.setInt(3, this.rs.getInt(3)); // fk2
+					pStmtStore.setTimestamp(4, this.rs.getTimestamp(4)); // dtField
+					pStmtStore.setString(5, this.rs.getString(5)); // charField1
+					pStmtStore.setString(6, this.rs.getString(7)); // charField2
+					pStmtStore.setString(7, this.rs.getString(7)); // charField3
+					pStmtStore.setString(8, this.rs.getString(8)); // charField4
+					pStmtStore.setInt(9, this.rs.getInt(9)); // intField1
+					pStmtStore.setInt(10, this.rs.getInt(10)); // intField2
+					pStmtStore.setInt(11, this.rs.getInt(11)); // intField3
+					pStmtStore.setInt(12, this.rs.getInt(12)); // intField4
+					pStmtStore.setDouble(13, this.rs.getDouble(13)); // doubleField1
+					pStmtStore.setDouble(14, this.rs.getDouble(14)); // doubleField2
+					pStmtStore.setDouble(15, this.rs.getDouble(15)); // doubleField3
+					pStmtStore.setDouble(16, this.rs.getDouble(16)); // doubleField4
+
+					pStmtStore.setInt(17, key);
+				} else {
+					/*
+					 * "UPDATE perfLoadStore SET " + "priKey = ?, " + "fk1 = ?, " +
+					 * "fk2 = ?, " + "dtField = ?, " + "charField1 = ?, " +
+					 * "charField2 = ?, " + "charField3 = ?, " + "charField4 = ?, " +
+					 * "intField1 = ?, " + "intField2 = ?, " + "intField3 = ?, " +
+					 * "intField4 = ?, " + "doubleField1 = ?," + "doubleField2 =
+					 * ?," + "doubleField3 = ?," + "doubleField4 = ?" + " WHERE
+					 * priKey=?");
+					 */
+					pStmtStore.setInt(1, key); // priKey
+					pStmtStore.setInt(2, this.rs.getInt("fk1")); // fk1
+					pStmtStore.setInt(3, this.rs.getInt("fk2")); // fk2
+					pStmtStore.setTimestamp(4, this.rs.getTimestamp("dtField")); // dtField
+					pStmtStore.setString(5, this.rs.getString("charField1")); // charField1
+					pStmtStore.setString(6, this.rs.getString("charField2")); // charField2
+					pStmtStore.setString(7, this.rs.getString("charField3")); // charField3
+					pStmtStore.setString(8, this.rs.getString("charField4")); // charField4
+					pStmtStore.setInt(9, this.rs.getInt("intField1")); // intField1
+					pStmtStore.setInt(10, this.rs.getInt("intField2")); // intField2
+					pStmtStore.setInt(11, this.rs.getInt("intField3")); // intField3
+					pStmtStore.setInt(12, this.rs.getInt("intField4")); // intField4
+					pStmtStore.setDouble(13, this.rs.getDouble("doubleField1")); // doubleField1
+					pStmtStore.setDouble(14, this.rs.getDouble("doubleField2")); // doubleField2
+					pStmtStore.setDouble(15, this.rs.getDouble("doubleField3")); // doubleField3
+					pStmtStore.setDouble(16, this.rs.getDouble("doubleField4")); // doubleField4
+
+					pStmtStore.setInt(17, key);
+				}
+
+				pStmtStore.executeUpdate();
+			}
+
+			this.rs.close();
+
+			this.conn.commit();
+			this.conn.setAutoCommit(true);
+		}
+
+		pStmtStore.close();
+		pStmtCheck.close();
+		pStmtLoad.close();
+
+		long end = System.currentTimeMillis();
+
+		long timeElapsed = (end - begin);
+
+		double timeElapsedSeconds = (double) timeElapsed / 1000;
+		double tps = transactionCount / timeElapsedSeconds;
+
+		if (this.takeMeasurements) {
+			addResult(tps);
+			System.out.print("1 [ " + numFormatter.format(getMeanValue())
+					+ " ] ");
+		} else {
+			System.out.println("Warm-up: " + tps + " trans/sec");
+		}
+	}
+
+	/**
+	 * Runs the test 10 times to get JIT going, and GC going
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	protected void warmUp() throws Exception {
+		try {
+			System.out.print("Warm-up period (10 iterations)");
+
+			for (int i = 0; i < 10; i++) {
+				doOneIteration();
+				System.out.print(".");
+			}
+
+			System.out.println();
+			System.out.println("Warm-up period ends");
+			System.out.println("\nUnits for this test are transactions/sec.");
+		} catch (Exception ex) {
+			ex.printStackTrace();
+
+			throw ex;
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/RetrievalPerfTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/RetrievalPerfTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/perf/RetrievalPerfTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,247 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.perf;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Simplistic test for performance regression.
+ * 
+ * @author Mark Matthews
+ */
+public class RetrievalPerfTest extends BaseTestCase {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	private static final int NUM_TESTS = 10000;
+
+	private static final int NUM_ROWS = 80;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Constructor for RetrievalPerfTest.
+	 * 
+	 * @param name
+	 *            name of the test to run
+	 */
+	public RetrievalPerfTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all tests.
+	 * 
+	 * @param args
+	 *            ignored
+	 */
+	public static void main(String[] args) {
+		new RetrievalPerfTest("testRetrievalMyIsam").run();
+		new RetrievalPerfTest("testRetrievalHeap").run();
+		new RetrievalPerfTest("testRetrievalCached").run();
+	}
+
+	/**
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS retrievalPerfTestHeap");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS retrievalPerfTestMyIsam");
+		this.stmt
+				.executeUpdate("CREATE TABLE retrievalPerfTestHeap (priKey INT NOT NULL PRIMARY KEY,"
+						+ "charField VARCHAR(80)) TYPE=HEAP");
+		this.stmt
+				.executeUpdate("CREATE TABLE retrievalPerfTestMyIsam (priKey INT NOT NULL PRIMARY KEY,"
+						+ "charField VARCHAR(80)) TYPE=MyISAM");
+
+		for (int i = 0; i < NUM_ROWS; i++) {
+			this.stmt
+					.executeUpdate("INSERT INTO retrievalPerfTestHeap (priKey, charField) VALUES ("
+							+ i
+							+ ",'abcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')");
+			this.stmt
+					.executeUpdate("INSERT INTO retrievalPerfTestMyIsam (priKey, charField) VALUES ("
+							+ i
+							+ ",'abcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZ')");
+		}
+	}
+
+	/**
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	public void tearDown() throws Exception {
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS retrievalPerfTestHeap");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS retrievalPerfTestMyIsam");
+		super.tearDown();
+	}
+
+	/**
+	 * Tests retrieval from the query cache
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testRetrievalCached() throws Exception {
+		this.stmt.executeUpdate("SET QUERY_CACHE_TYPE = DEMAND");
+
+		double fullBegin = System.currentTimeMillis();
+		double averageQueryTimeMs = 0;
+		double averageTraversalTimeMs = 0;
+
+		for (int i = 0; i < NUM_TESTS; i++) {
+			long queryBegin = System.currentTimeMillis();
+			this.rs = this.stmt
+					.executeQuery("SELECT SQL_CACHE * FROM retrievalPerfTestHeap");
+
+			long queryEnd = System.currentTimeMillis();
+			averageQueryTimeMs += ((double) (queryEnd - queryBegin) / NUM_TESTS);
+
+			long traverseBegin = System.currentTimeMillis();
+
+			while (this.rs.next()) {
+				this.rs.getInt(1);
+				this.rs.getString(2);
+			}
+
+			long traverseEnd = System.currentTimeMillis();
+			averageTraversalTimeMs += ((double) (traverseEnd - traverseBegin) / NUM_TESTS);
+		}
+
+		double fullEnd = System.currentTimeMillis();
+		double fullTime = (fullEnd - fullBegin) / 1000;
+		double queriesPerSec = NUM_TESTS / fullTime;
+		double rowsPerSec = (NUM_ROWS * NUM_TESTS) / fullTime;
+		System.out.println("\nQuery Cache From Heap Retrieval\n");
+		System.out.println("Full test took: " + fullTime + " seconds.");
+		System.out.println("Queries/second: " + queriesPerSec);
+		System.out.println("Rows/second: " + rowsPerSec);
+		System.out.println("Avg. Query Exec Time: " + averageQueryTimeMs
+				+ " ms");
+		System.out.println("Avg. Traversal Time: " + averageTraversalTimeMs
+				+ " ms");
+
+		// We're doing something wrong if we can't beat 45 seconds :(
+		assertTrue(fullTime < 45);
+	}
+
+	/**
+	 * Tests retrieval from HEAP tables
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testRetrievalHeap() throws Exception {
+		double fullBegin = System.currentTimeMillis();
+		double averageQueryTimeMs = 0;
+		double averageTraversalTimeMs = 0;
+
+		for (int i = 0; i < NUM_TESTS; i++) {
+			long queryBegin = System.currentTimeMillis();
+			this.rs = this.stmt
+					.executeQuery("SELECT * FROM retrievalPerfTestHeap");
+
+			long queryEnd = System.currentTimeMillis();
+			averageQueryTimeMs += ((double) (queryEnd - queryBegin) / NUM_TESTS);
+
+			long traverseBegin = System.currentTimeMillis();
+
+			while (this.rs.next()) {
+				this.rs.getInt(1);
+				this.rs.getString(2);
+			}
+
+			long traverseEnd = System.currentTimeMillis();
+			averageTraversalTimeMs += ((double) (traverseEnd - traverseBegin) / NUM_TESTS);
+		}
+
+		double fullEnd = System.currentTimeMillis();
+		double fullTime = (fullEnd - fullBegin) / 1000;
+		double queriesPerSec = NUM_TESTS / fullTime;
+		double rowsPerSec = (NUM_ROWS * NUM_TESTS) / fullTime;
+		System.out.println("\nHEAP Table Retrieval\n");
+		System.out.println("Full test took: " + fullTime + " seconds.");
+		System.out.println("Queries/second: " + queriesPerSec);
+		System.out.println("Rows/second: " + rowsPerSec);
+		System.out.println("Avg. Query Exec Time: " + averageQueryTimeMs
+				+ " ms");
+		System.out.println("Avg. Traversal Time: " + averageTraversalTimeMs
+				+ " ms");
+
+		// We're doing something wrong if we can't beat 45 seconds :(
+		assertTrue(fullTime < 45);
+	}
+
+	/**
+	 * Tests retrieval speed from MyISAM type tables
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testRetrievalMyIsam() throws Exception {
+		double fullBegin = System.currentTimeMillis();
+		double averageQueryTimeMs = 0;
+		double averageTraversalTimeMs = 0;
+
+		for (int i = 0; i < NUM_TESTS; i++) {
+			long queryBegin = System.currentTimeMillis();
+			this.rs = this.stmt
+					.executeQuery("SELECT * FROM retrievalPerfTestMyIsam");
+
+			long queryEnd = System.currentTimeMillis();
+			averageQueryTimeMs += ((double) (queryEnd - queryBegin) / NUM_TESTS);
+
+			long traverseBegin = System.currentTimeMillis();
+
+			while (this.rs.next()) {
+				this.rs.getInt(1);
+				this.rs.getString(2);
+			}
+
+			long traverseEnd = System.currentTimeMillis();
+			averageTraversalTimeMs += ((double) (traverseEnd - traverseBegin) / NUM_TESTS);
+		}
+
+		double fullEnd = System.currentTimeMillis();
+		double fullTime = (fullEnd - fullBegin) / 1000;
+		double queriesPerSec = NUM_TESTS / fullTime;
+		double rowsPerSec = (NUM_ROWS * NUM_TESTS) / fullTime;
+		System.out.println("\nMyIsam Retrieval\n");
+		System.out.println("Full test took: " + fullTime + " seconds.");
+		System.out.println("Queries/second: " + queriesPerSec);
+		System.out.println("Rows/second: " + rowsPerSec);
+		System.out.println("Avg. Query Exec Time: " + averageQueryTimeMs
+				+ " ms");
+		System.out.println("Avg. Traversal Time: " + averageTraversalTimeMs
+				+ " ms");
+
+		// We're doing something wrong if we can't beat 45 seconds :(
+		assertTrue(fullTime < 45);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/AppletRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/AppletRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/AppletRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,107 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.util.Properties;
+
+import sun.applet.AppletSecurity;
+import testsuite.BaseTestCase;
+
+/**
+ * Tests various applet-related issues.
+ * 
+ * @author Mark Matthews
+ * @version $Id: AppletRegressionTest.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class AppletRegressionTest extends BaseTestCase {
+	private final static String TOGGLE_RUN_PROPERTY = "com.mysql.jdbc.testsuite.regression.runAppletRegressionTest";
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param name
+	 */
+	public AppletRegressionTest(String name) {
+		super(name);
+
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		System.setProperty(TOGGLE_RUN_PROPERTY, "true");
+		junit.textui.TestRunner.run(AppletRegressionTest.class);
+	}
+
+	/**
+	 * Tests if the driver wors with an Applet security manager installed.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testAppletSecurityManager() throws Exception {
+		if ("true".equalsIgnoreCase(System.getProperty(TOGGLE_RUN_PROPERTY))) {
+			System.setSecurityManager(new CustomAppletSecurity());
+
+			getConnectionWithProps(new Properties());
+		}
+	}
+
+	/**
+	 * We need to customize the security manager a 'bit', so that JUnit still
+	 * works (and we can connect to various databases).
+	 */
+	class CustomAppletSecurity extends AppletSecurity {
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.SecurityManager#checkAccess(java.lang.Thread)
+		 */
+		public synchronized void checkAccess(Thread arg0) {
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.SecurityManager#checkConnect(java.lang.String, int,
+		 *      java.lang.Object)
+		 */
+		public void checkConnect(String host, int port, Object context) {
+		}
+
+		/*
+		 * (non-Javadoc)
+		 * 
+		 * @see java.lang.SecurityManager#checkConnect(java.lang.String, int)
+		 */
+		public void checkConnect(String host, int port) {
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/BlobRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/BlobRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/BlobRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,407 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.BufferedOutputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.StringReader;
+import java.sql.Blob;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Properties;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests fixes for BLOB handling.
+ * 
+ * @author Mark Matthews
+ * @version $Id: BlobRegressionTest.java,v 1.1.2.19 2005/03/09 18:16:16
+ *          mmatthews Exp $
+ */
+public class BlobRegressionTest extends BaseTestCase {
+	/**
+	 * Creates a new BlobRegressionTest.
+	 * 
+	 * @param name
+	 *            name of the test to run
+	 */
+	public BlobRegressionTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(BlobRegressionTest.class);
+	}
+
+	/**
+	 * 
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testBug2670() throws Exception {
+		if (!isRunningOnJdk131()) {
+			try {
+				byte[] blobData = new byte[32];
+
+				for (int i = 0; i < blobData.length; i++) {
+					blobData[i] = 1;
+				}
+
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2670");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBug2670(blobField LONGBLOB)");
+
+				PreparedStatement pStmt = this.conn
+						.prepareStatement("INSERT INTO testBug2670 (blobField) VALUES (?)");
+				pStmt.setBytes(1, blobData);
+				pStmt.executeUpdate();
+
+				this.rs = this.stmt
+						.executeQuery("SELECT blobField FROM testBug2670");
+				this.rs.next();
+
+				Blob blob = this.rs.getBlob(1);
+
+				//
+				// Test mid-point insertion
+				//
+				blob.setBytes(4, new byte[] { 2, 2, 2, 2 });
+
+				byte[] newBlobData = blob.getBytes(1L, (int) blob.length());
+
+				assertTrue("Blob changed length",
+						blob.length() == blobData.length);
+
+				assertTrue(
+						"New data inserted wrongly",
+						((newBlobData[3] == 2) && (newBlobData[4] == 2)
+								&& (newBlobData[5] == 2) && (newBlobData[6] == 2)));
+
+				//
+				// Test end-point insertion
+				//
+				blob.setBytes(32, new byte[] { 2, 2, 2, 2 });
+
+				assertTrue("Blob length should be 3 larger",
+						blob.length() == (blobData.length + 3));
+			} finally {
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testUpdateLongBlob");
+			}
+		}
+	}
+
+	/**
+	 * 
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testUpdateLongBlobGT16M() throws Exception {
+		if (versionMeetsMinimum(4, 0)) {
+			try {
+				byte[] blobData = new byte[18 * 1024 * 1024]; // 18M blob
+
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testUpdateLongBlob");
+				this.stmt
+						.executeUpdate("CREATE TABLE testUpdateLongBlob(blobField LONGBLOB)");
+				this.stmt
+						.executeUpdate("INSERT INTO testUpdateLongBlob (blobField) VALUES (NULL)");
+
+				PreparedStatement pStmt = this.conn
+						.prepareStatement("UPDATE testUpdateLongBlob SET blobField=?");
+				pStmt.setBytes(1, blobData);
+				pStmt.executeUpdate();
+			} finally {
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testUpdateLongBlob");
+			}
+		}
+	}
+
+	/**
+	 * 
+	 * @throws Exception
+	 */
+	public void testUpdatableBlobsWithCharsets() throws Exception {
+		byte[] smallBlob = new byte[32];
+
+		for (byte i = 0; i < smallBlob.length; i++) {
+			smallBlob[i] = i;
+		}
+
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testUpdatableBlobsWithCharsets");
+			this.stmt
+					.executeUpdate("CREATE TABLE testUpdatableBlobsWithCharsets(pk INT NOT NULL PRIMARY KEY, field1 BLOB)");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("INSERT INTO testUpdatableBlobsWithCharsets (pk, field1) VALUES (1, ?)");
+			pStmt.setBinaryStream(1, new ByteArrayInputStream(smallBlob),
+					smallBlob.length);
+			pStmt.executeUpdate();
+
+			Statement updStmt = this.conn
+					.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+
+			this.rs = updStmt
+					.executeQuery("SELECT pk, field1 FROM testUpdatableBlobsWithCharsets");
+			System.out.println(this.rs);
+			this.rs.next();
+
+			for (byte i = 0; i < smallBlob.length; i++) {
+				smallBlob[i] = (byte) (i + 32);
+			}
+
+			this.rs.updateBinaryStream(2, new ByteArrayInputStream(smallBlob),
+					smallBlob.length);
+			this.rs.updateRow();
+
+			ResultSet newRs = this.stmt
+					.executeQuery("SELECT field1 FROM testUpdatableBlobsWithCharsets");
+
+			newRs.next();
+
+			byte[] updatedBlob = newRs.getBytes(1);
+
+			for (byte i = 0; i < smallBlob.length; i++) {
+				byte origValue = smallBlob[i];
+				byte newValue = updatedBlob[i];
+
+				assertTrue("Original byte at position " + i + ", " + origValue
+						+ " != new value, " + newValue, origValue == newValue);
+			}
+
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testUpdatableBlobsWithCharsets");
+		}
+	}
+
+	public void testBug5490() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5490");
+			this.stmt.executeUpdate("CREATE TABLE testBug5490"
+					+ "(pk INT NOT NULL PRIMARY KEY, blobField BLOB)");
+			String sql = "insert into testBug5490 values(?,?)";
+
+			int blobFileSize = 871;
+			File blobFile = newTempBinaryFile("Bug5490", blobFileSize);
+
+			PreparedStatement pStmt = this.conn.prepareStatement(sql,
+					ResultSet.TYPE_SCROLL_INSENSITIVE,
+					ResultSet.CONCUR_READ_ONLY);
+			pStmt.setInt(1, 2);
+			FileInputStream fis = new FileInputStream(blobFile);
+			pStmt.setBinaryStream(2, fis, blobFileSize);
+			pStmt.execute();
+			fis.close();
+			pStmt.close();
+
+			this.rs = this.stmt
+					.executeQuery("SELECT blobField FROM testBug5490");
+
+			this.rs.next();
+
+			byte[] returned = this.rs.getBytes(1);
+
+			assertEquals(blobFileSize, returned.length);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5490");
+		}
+	}
+
+	/**
+	 * Tests BUG#8096 where emulated locators corrupt binary data when using
+	 * server-side prepared statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug8096() throws Exception {
+		if (!isRunningOnJdk131()) {
+			int dataSize = 256;
+
+			Properties props = new Properties();
+			props.setProperty("emulateLocators", "true");
+			Connection locatorConn = getConnectionWithProps(props);
+
+			String createTable = "CREATE TABLE testBug8096 (ID VARCHAR(10) "
+					+ "PRIMARY KEY, DATA LONGBLOB)";
+			String select = "SELECT ID, 'DATA' AS BLOB_DATA FROM testBug8096 "
+					+ "WHERE ID = ?";
+			String insert = "INSERT INTO testBug8096 (ID, DATA) VALUES (?, '')";
+
+			String id = "1";
+			byte[] testData = new byte[dataSize];
+
+			for (int i = 0; i < testData.length; i++) {
+				testData[i] = (byte) i;
+			}
+
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8096");
+
+				this.stmt.executeUpdate(createTable);
+
+				PreparedStatement ps = locatorConn.prepareStatement(insert);
+				ps.setString(1, id);
+				ps.execute();
+
+				ps = locatorConn.prepareStatement(select);
+				ps.setString(1, id);
+
+				this.rs = ps.executeQuery();
+
+				if (this.rs.next()) {
+					Blob b = rs.getBlob("BLOB_DATA");
+					b.setBytes(1, testData);
+				}
+
+				this.rs.close();
+				ps.close();
+
+				ps = locatorConn.prepareStatement(select);
+				ps.setString(1, id);
+
+				this.rs = ps.executeQuery();
+
+				byte[] result = null;
+				if (this.rs.next()) {
+					Blob b = this.rs.getBlob("BLOB_DATA");
+
+					result = b.getBytes(1, dataSize - 1);
+				}
+
+				this.rs.close();
+				ps.close();
+
+				assertNotNull(result);
+
+				for (int i = 0; i < result.length && i < testData.length; i++) {
+					// Will print out all of the values that don't match.
+					// All negative values will instead be replaced with 63.
+					if (result[i] != testData[i]) {
+						assertEquals("At position " + i, testData[i], result[i]);
+					}
+				}
+
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8096");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#9040 - PreparedStatement.addBatch() doesn't work with
+	 * server-side prepared statements and streaming BINARY data.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug9040() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug9040");
+
+			this.stmt.executeUpdate("create table if not exists testBug9040 "
+					+ "(primary_key int not null primary key, "
+					+ "data mediumblob)");
+
+			this.pstmt = this.conn
+					.prepareStatement("replace into testBug9040 (primary_key, data) values(?,?)");
+
+			int primaryKey = 1;
+			byte[] data = "First Row".getBytes();
+			this.pstmt.setInt(1, primaryKey);
+			this.pstmt.setBinaryStream(2, new ByteArrayInputStream(data),
+					data.length);
+			this.pstmt.addBatch();
+
+			primaryKey = 2;
+			data = "Second Row".getBytes();
+			this.pstmt.setInt(1, primaryKey);
+			this.pstmt.setBinaryStream(2, new ByteArrayInputStream(data),
+					data.length);
+			this.pstmt.addBatch();
+
+			this.pstmt.executeBatch();
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug9040");
+
+			if (this.pstmt != null) {
+				this.pstmt.close();
+			}
+		}
+	}
+
+	public void testBug10850() throws Exception {
+		String tableName = "testBug10850";
+
+		createTable(tableName, "(field1 TEXT)");
+
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = this.conn.prepareStatement("INSERT INTO " +
+
+			tableName + " VALUES (?)");
+			pStmt.setCharacterStream(1, new StringReader(""), 0);
+			pStmt.executeUpdate();
+
+			assertEquals("0", getSingleIndexedValueWithQuery(1,
+					"SELECT LENGTH(field1) FROM " + tableName).toString());
+			this.stmt.executeUpdate("TRUNCATE TABLE " + tableName);
+
+			pStmt.clearParameters();
+			pStmt.setBinaryStream(1, new ByteArrayInputStream(new byte[0]), 0);
+			pStmt.executeUpdate();
+
+			assertEquals("0", getSingleIndexedValueWithQuery(1,
+					"SELECT LENGTH(field1) FROM " + tableName).toString());
+			this.stmt.executeUpdate("TRUNCATE TABLE " + tableName);
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/CachedRowsetTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/CachedRowsetTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/CachedRowsetTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,106 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.lang.reflect.Method;
+import java.sql.ResultSet;
+
+import javax.sql.RowSet;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Regression test cases for the ResultSet class.
+ * 
+ * @author Eric Herman
+ */
+public class CachedRowsetTest extends BaseTestCase {
+	/**
+	 * Creates a new CachedRowsetTest
+	 * 
+	 * @param name
+	 *            the name of the test to run
+	 */
+	public CachedRowsetTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(CachedRowsetTest.class);
+	}
+
+	/**
+	 * Tests fix for BUG#5188, CachedRowSet errors using PreparedStatement. Uses
+	 * Sun's "com.sun.rowset.CachedRowSetImpl"
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug5188() throws Exception {
+		String implClass = "com.sun.rowset.CachedRowSetImpl";
+		Class c;
+		Method populate;
+		try {
+			c = Class.forName(implClass);
+		} catch (ClassNotFoundException e) {
+			System.out.println("skipping testBug5188. Requires: " + implClass);
+			return;
+		}
+		populate = c.getMethod("populate", new Class[] { ResultSet.class });
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5188");
+			this.stmt.executeUpdate("CREATE TABLE testBug5188 "
+					+ "(ID int NOT NULL AUTO_INCREMENT, "
+					+ "datafield VARCHAR(64), " + "PRIMARY KEY(ID))");
+
+			this.stmt.executeUpdate("INSERT INTO testBug5188(datafield) "
+					+ "values('test data stuff !')");
+
+			String sql = "SELECT * FROM testBug5188 where ID = ?";
+			this.pstmt = this.conn.prepareStatement(sql);
+			this.pstmt.setString(1, "1");
+			this.rs = this.pstmt.executeQuery();
+
+			// create a CachedRowSet and populate it
+			RowSet cachedRowSet = (RowSet) c.newInstance();
+			// cachedRowSet.populate(rs);
+			populate.invoke(cachedRowSet, new Object[] { this.rs });
+
+			// scroll through CachedRowSet ...
+			assertTrue(cachedRowSet.next());
+			assertEquals("1", cachedRowSet.getString("ID"));
+			assertEquals("test data stuff !", cachedRowSet
+					.getString("datafield"));
+			assertFalse(cachedRowSet.next());
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5188");
+		}
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/CallableStatementRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/CallableStatementRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/CallableStatementRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,908 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+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.Properties;
+
+import com.mysql.jdbc.DatabaseMetaData;
+import com.mysql.jdbc.NonRegisteringDriver;
+import com.mysql.jdbc.SQLError;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests fixes for bugs in CallableStatement code.
+ * 
+ * @version $Id: CallableStatementRegressionTest.java,v 1.1.2.6 2004/12/09
+ *          15:57:26 mmatthew Exp $
+ */
+public class CallableStatementRegressionTest extends BaseTestCase {
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param name
+	 */
+	public CallableStatementRegressionTest(String name) {
+		super(name);
+
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 *            ignored
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(CallableStatementRegressionTest.class);
+	}
+
+	/**
+	 * Tests fix for BUG#3539 getProcedures() does not return any procedures in
+	 * result set
+	 * 
+	 * @throws Exception
+	 *             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");
+
+				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");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#3540 getProcedureColumns doesn't work with wildcards
+	 * for procedure name
+	 * 
+	 * @throws Exception
+	 *             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");
+
+				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("y".equals(this.rs.getString(4)));
+
+				assertTrue(!this.rs.next());
+			} finally {
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3540");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#7026 - DBMD.getProcedures() doesn't respect catalog
+	 * parameter
+	 * 
+	 * @throws Exception
+	 *             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");
+
+				//
+				// Should be found this time.
+				//
+				this.rs = this.conn.getMetaData().getProcedures(
+						this.conn.getCatalog(), null, "testBug7026");
+
+				assertTrue(this.rs.next());
+				assertTrue("testBug7026".equals(this.rs.getString(3)));
+
+				assertTrue(!this.rs.next());
+
+				//
+				// 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());
+
+				//
+				// 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());
+				assertTrue("testBug7026".equals(this.rs.getString(3)));
+
+				assertTrue(!this.rs.next());
+			} finally {
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug7026");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#9319 -- Stored procedures with same name in different
+	 * databases confuse the driver when it tries to determine parameter
+	 * counts/types.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug9319() throws Exception {
+		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();
+
+					db2Connection.createStatement().executeUpdate(
+							"CREATE DATABASE IF NOT EXISTS db_9319_2");
+					db2Connection.setCatalog("db_9319_2");
+
+					db2Connection.createStatement().executeUpdate(
+							"DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
+
+					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"
+
+											+ (doASelect ? "\nselect 2;"
+													: "\nSELECT 2 INTO p_administrador;")
+											+ "\nEND");
+
+					db1Connection.createStatement().executeUpdate(
+							"CREATE DATABASE IF NOT EXISTS db_9319_1");
+					db1Connection.setCatalog("db_9319_1");
+
+					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");
+
+					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.registerOutParameter(6, java.sql.Types.VARCHAR);
+
+					cstmt.execute();
+
+					if (doASelect) {
+						this.rs = cstmt.getResultSet();
+						assertTrue(this.rs.next());
+						assertEquals(2, this.rs.getInt(1));
+					} else {
+						assertEquals(2, cstmt.getInt(5));
+					}
+
+					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 = 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.execute();
+
+					if (doASelect) {
+						this.rs = cstmt.getResultSet();
+						assertTrue(this.rs.next());
+						assertEquals(1, this.rs.getInt(1));
+					} else {
+						assertEquals(1, cstmt.getInt(5));
+					}
+
+					String quoteChar = db2Connection.getMetaData()
+							.getIdentifierQuoteString();
+
+					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);
+
+					cstmt.execute();
+
+					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");
+					}
+				}
+			}
+		}
+	}
+
+	/*
+	 * public void testBug9319() throws Exception { boolean doASelect = false; //
+	 * 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();
+	 * 
+	 * db2Connection.createStatement().executeUpdate( "CREATE DATABASE IF NOT
+	 * EXISTS db_9319"); db2Connection.setCatalog("db_9319");
+	 * 
+	 * db2Connection.createStatement().executeUpdate( "DROP PROCEDURE IF EXISTS
+	 * COMPROVAR_USUARI");
+	 * 
+	 * 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" + (doASelect ? "\nselect 2;" : "\nSELECT 2 INTO
+	 * p_administrador;" ) + "\nEND");
+	 * 
+	 * this.stmt .executeUpdate("DROP PROCEDURE IF EXISTS COMPROVAR_USUARI");
+	 * this.stmt .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");
+	 * 
+	 * 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.registerOutParameter(6, java.sql.Types.VARCHAR);
+	 * 
+	 * cstmt.execute();
+	 * 
+	 * if (doASelect) { this.rs = cstmt.getResultSet();
+	 * assertTrue(this.rs.next()); assertEquals(2, this.rs.getInt(1)); } else {
+	 * assertEquals(2, cstmt.getInt(5)); }
+	 * 
+	 * cstmt = this.conn .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 = this.conn .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();
+	 * 
+	 * if (doASelect) { this.rs = cstmt.getResultSet();
+	 * assertTrue(this.rs.next()); assertEquals(1, this.rs.getInt(1)); } else {
+	 * assertEquals(1, cstmt.getInt(5)); }
+	 * 
+	 * String quoteChar =
+	 * db2Connection.getMetaData().getIdentifierQuoteString();
+	 * 
+	 * cstmt = db2Connection .prepareCall("{ call " + quoteChar +
+	 * this.conn.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);
+	 * 
+	 * cstmt.execute();
+	 * 
+	 * 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"); }
+	 * 
+	 * this.stmt .executeUpdate("DROP PROCEDURE IF EXISTS COMPROVAR_USUARI"); } } } }
+	 */
+
+	/**
+	 * Tests fix for BUG#9682 - Stored procedures with DECIMAL parameters with
+	 * storage specifications that contained "," in them would fail.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug9682() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			CallableStatement cStmt = null;
+
+			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");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix forBUG#10310 - Driver doesn't support {?=CALL(...)} for calling
+	 * stored functions. This involved adding support for function retrieval to
+	 * DatabaseMetaData.getProcedures() and getProcedureColumns() as well.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug10310() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			CallableStatement cStmt = null;
+
+			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());
+
+				assertEquals(-1, cStmt.executeUpdate());
+				assertEquals(2f, cStmt.getInt(1), .001);
+				assertEquals("java.lang.Integer", cStmt.getObject(1).getClass()
+						.getName());
+
+				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
+
+				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.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();
+				}
+
+				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
+	 * place for it now).
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug12417() throws Exception {
+		if (versionMeetsMinimum(5, 0) && isServerRunningOnWindows()) {
+			Connection ucCatalogConn = null;
+
+			try {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testBug12417");
+				this.stmt.executeUpdate("CREATE PROCEDURE testBug12417()\n"
+						+ "BEGIN\n" + "SELECT 1;" + "end\n");
+				ucCatalogConn = getConnectionWithProps(null);
+				ucCatalogConn.setCatalog(this.conn.getCatalog().toUpperCase());
+				ucCatalogConn.prepareCall("{call testBug12417()}");
+			} finally {
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3539");
+
+				if (ucCatalogConn != null) {
+					ucCatalogConn.close();
+				}
+			}
+		}
+	}
+
+	public void testBug15121() throws Exception {
+		if (false /* needs to be fixed on server */) {
+			if (versionMeetsMinimum(5, 0)) {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS p_testBug15121");
+
+				this.stmt.executeUpdate("CREATE PROCEDURE p_testBug15121()\n"
+						+ "BEGIN\n" + "SELECT * from idonotexist;\n" + "END");
+
+				Properties props = new Properties();
+				props.setProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY, "");
+
+				Connection noDbConn = null;
+
+				try {
+					noDbConn = getConnectionWithProps(props);
+
+					StringBuffer queryBuf = new StringBuffer("{call ");
+					String quotedId = this.conn.getMetaData()
+							.getIdentifierQuoteString();
+					queryBuf.append(quotedId);
+					queryBuf.append(this.conn.getCatalog());
+					queryBuf.append(quotedId);
+					queryBuf.append(".p_testBug15121()}");
+
+					noDbConn.prepareCall(queryBuf.toString()).execute();
+				} finally {
+					if (noDbConn != null) {
+						noDbConn.close();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#15464 - INOUT parameter does not store IN value.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+
+	public void testBug15464() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			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");
+
+				storedProc = this.conn
+						.prepareCall("{call testInOutParam(?, ?)}");
+
+				storedProc.setString(1, "abcd");
+				storedProc.setInt(2, 4);
+				storedProc.registerOutParameter(2, Types.INTEGER);
+
+				storedProc.execute();
+
+				assertEquals(5, storedProc.getInt(2));
+			} finally {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testInOutParam");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for 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() should
+	 * be, however.
+	 * 
+	 * @throws Exception
+	 *             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");
+
+			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));
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#21462 - JDBC (and ODBC) specifications allow no-parenthesis
+	 * CALL statements for procedures with no arguments, MySQL server does not.
+	 * 
+	 * @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");
+			}
+		}
+	}
+	
+	/** 
+	 * Tests fix for BUG#22024 - Newlines causing whitespace to span confuse
+	 * procedure parser when getting parameter metadata for stored procedures.
+	 * 
+	 * @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");
+			}
+		}
+	}
+	
+	/**
+	 * Tests 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 statement).
+	 * 
+	 * @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");
+
+			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();
+			}
+		}
+	}
+	
+	public void testHugeNumberOfParameters() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			this.stmt
+					.executeUpdate("DROP PROCEDURE IF EXISTS testHugeNumberOfParameters");
+
+			StringBuffer procDef = new StringBuffer(
+					"CREATE PROCEDURE testHugeNumberOfParameters(");
+
+			for (int i = 0; i < 274; i++) {
+				if (i != 0) {
+					procDef.append(",");
+				}
+
+				procDef.append(" OUT param_" + i + " VARCHAR(32)");
+			}
+
+			procDef.append(")\nBEGIN\nSELECT 1;\nEND");
+			this.stmt.executeUpdate(procDef.toString());
+
+			CallableStatement cStmt = null;
+
+			try {
+				cStmt = this.conn
+						.prepareCall("{call testHugeNumberOfParameters(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+								+
+
+								"?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,"
+								+ "?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)}");
+				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;
+
+			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();
+				}
+			}
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/ConnectionRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/ConnectionRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/ConnectionRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1794 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import testsuite.BaseTestCase;
+
+import com.mysql.jdbc.Driver;
+import com.mysql.jdbc.NonRegisteringDriver;
+import com.mysql.jdbc.ReplicationConnection;
+import com.mysql.jdbc.ReplicationDriver;
+
+/**
+ * Regression tests for Connections
+ * 
+ * @author Mark Matthews
+ * @version $Id: ConnectionRegressionTest.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class ConnectionRegressionTest extends BaseTestCase {
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param name
+	 *            the name of the testcase
+	 */
+	public ConnectionRegressionTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(ConnectionRegressionTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testBug1914() throws Exception {
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), BIGINT)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), BINARY)}"));
+		System.out
+				.println(this.conn.nativeSQL("{fn convert(foo(a,b,c), BIT)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), CHAR)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), DATE)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), DECIMAL)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), DOUBLE)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), FLOAT)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), INTEGER)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), LONGVARBINARY)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), LONGVARCHAR)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), TIME)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), TIMESTAMP)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), TINYINT)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), VARBINARY)}"));
+		System.out.println(this.conn
+				.nativeSQL("{fn convert(foo(a,b,c), VARCHAR)}"));
+	}
+
+	/**
+	 * Tests fix for BUG#3554 - Not specifying database in URL causes
+	 * MalformedURL exception.
+	 * 
+	 * @throws Exception
+	 *             if an error ocurrs.
+	 */
+	public void testBug3554() throws Exception {
+		try {
+			new NonRegisteringDriver().connect(
+					"jdbc:mysql://localhost:3306/?user=root&password=root",
+					new Properties());
+		} catch (SQLException sqlEx) {
+			assertTrue(sqlEx.getMessage().indexOf("Malformed") == -1);
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testBug3790() throws Exception {
+		String field2OldValue = "foo";
+		String field2NewValue = "bar";
+		int field1OldValue = 1;
+
+		Connection conn1 = null;
+		Connection conn2 = null;
+		Statement stmt1 = null;
+		Statement stmt2 = null;
+		ResultSet rs2 = null;
+
+		Properties props = new Properties();
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3790");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug3790 (field1 INT NOT NULL PRIMARY KEY, field2 VARCHAR(32)) TYPE=InnoDB");
+			this.stmt.executeUpdate("INSERT INTO testBug3790 VALUES ("
+					+ field1OldValue + ", '" + field2OldValue + "')");
+
+			conn1 = getConnectionWithProps(props); // creates a new connection
+			conn2 = getConnectionWithProps(props); // creates another new
+			// connection
+			conn1.setAutoCommit(false);
+			conn2.setAutoCommit(false);
+
+			stmt1 = conn1.createStatement();
+			stmt1.executeUpdate("UPDATE testBug3790 SET field2 = '"
+					+ field2NewValue + "' WHERE field1=" + field1OldValue);
+			conn1.commit();
+
+			stmt2 = conn2.createStatement();
+
+			rs2 = stmt2.executeQuery("SELECT field1, field2 FROM testBug3790");
+
+			assertTrue(rs2.next());
+			assertTrue(rs2.getInt(1) == field1OldValue);
+			assertTrue(rs2.getString(2).equals(field2NewValue));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3790");
+
+			if (rs2 != null) {
+				rs2.close();
+			}
+
+			if (stmt2 != null) {
+				stmt2.close();
+			}
+
+			if (stmt1 != null) {
+				stmt1.close();
+			}
+
+			if (conn1 != null) {
+				conn1.close();
+			}
+
+			if (conn2 != null) {
+				conn2.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests if the driver configures character sets correctly for 4.1.x
+	 * servers. Requires that the 'admin connection' is configured, as this test
+	 * needs to create/drop databases.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testCollation41() throws Exception {
+		if (versionMeetsMinimum(4, 1) && isAdminConnectionConfigured()) {
+			Map charsetsAndCollations = getCharacterSetsAndCollations();
+			charsetsAndCollations.remove("latin7"); // Maps to multiple Java
+			// charsets
+			charsetsAndCollations.remove("ucs2"); // can't be used as a
+			// connection charset
+
+			Iterator charsets = charsetsAndCollations.keySet().iterator();
+
+			while (charsets.hasNext()) {
+				Connection charsetConn = null;
+				Statement charsetStmt = null;
+
+				try {
+					String charsetName = charsets.next().toString();
+					String collationName = charsetsAndCollations.get(
+							charsetName).toString();
+					Properties props = new Properties();
+					props.put("characterEncoding", charsetName);
+
+					System.out.println("Testing character set " + charsetName);
+
+					charsetConn = getAdminConnectionWithProps(props);
+
+					charsetStmt = charsetConn.createStatement();
+
+					charsetStmt
+							.executeUpdate("DROP DATABASE IF EXISTS testCollation41");
+					charsetStmt
+							.executeUpdate("DROP TABLE IF EXISTS testCollation41");
+
+					charsetStmt
+							.executeUpdate("CREATE DATABASE testCollation41 DEFAULT CHARACTER SET "
+									+ charsetName);
+					charsetConn.setCatalog("testCollation41");
+
+					// We've switched catalogs, so we need to recreate the
+					// statement to pick this up...
+					charsetStmt = charsetConn.createStatement();
+
+					StringBuffer createTableCommand = new StringBuffer(
+							"CREATE TABLE testCollation41"
+									+ "(field1 VARCHAR(255), field2 INT)");
+
+					charsetStmt.executeUpdate(createTableCommand.toString());
+
+					charsetStmt
+							.executeUpdate("INSERT INTO testCollation41 VALUES ('abc', 0)");
+
+					int updateCount = charsetStmt
+							.executeUpdate("UPDATE testCollation41 SET field2=1 WHERE field1='abc'");
+					assertTrue(updateCount == 1);
+				} finally {
+					if (charsetStmt != null) {
+						charsetStmt
+								.executeUpdate("DROP TABLE IF EXISTS testCollation41");
+						charsetStmt
+								.executeUpdate("DROP DATABASE IF EXISTS testCollation41");
+						charsetStmt.close();
+					}
+
+					if (charsetConn != null) {
+						charsetConn.close();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests setReadOnly() being reset during failover
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testSetReadOnly() throws Exception {
+		Properties props = new Properties();
+		props.put("autoReconnect", "true");
+
+		String sepChar = "?";
+
+		if (BaseTestCase.dbUrl.indexOf("?") != -1) {
+			sepChar = "&";
+		}
+
+		Connection reconnectableConn = DriverManager.getConnection(
+				BaseTestCase.dbUrl + sepChar + "autoReconnect=true", props);
+
+		this.rs = reconnectableConn.createStatement().executeQuery(
+				"SELECT CONNECTION_ID()");
+		this.rs.next();
+
+		String connectionId = this.rs.getString(1);
+
+		reconnectableConn.setReadOnly(true);
+
+		boolean isReadOnly = reconnectableConn.isReadOnly();
+
+		Connection killConn = getConnectionWithProps(null);
+
+		killConn.createStatement().executeUpdate("KILL " + connectionId);
+		Thread.sleep(2000);
+
+		SQLException caughtException = null;
+
+		int numLoops = 8;
+
+		while (caughtException == null && numLoops > 0) {
+			numLoops--;
+
+			try {
+				reconnectableConn.createStatement().executeQuery("SELECT 1");
+			} catch (SQLException sqlEx) {
+				caughtException = sqlEx;
+			}
+		}
+
+		System.out
+				.println("Executing statement on reconnectable connection...");
+
+		this.rs = reconnectableConn.createStatement().executeQuery(
+				"SELECT CONNECTION_ID()");
+		this.rs.next();
+		assertTrue("Connection is not a reconnected-connection", !connectionId
+				.equals(this.rs.getString(1)));
+
+		try {
+			reconnectableConn.createStatement().executeQuery("SELECT 1");
+		} catch (SQLException sqlEx) {
+			; // ignore
+		}
+
+		reconnectableConn.createStatement().executeQuery("SELECT 1");
+
+		assertTrue(reconnectableConn.isReadOnly() == isReadOnly);
+	}
+
+	private Map getCharacterSetsAndCollations() throws Exception {
+		Map charsetsToLoad = new HashMap();
+
+		try {
+			this.rs = this.stmt.executeQuery("SHOW character set");
+
+			while (this.rs.next()) {
+				charsetsToLoad.put(this.rs.getString("Charset"), this.rs
+						.getString("Default collation"));
+			}
+
+			//
+			// These don't have mappings in Java...
+			//
+			charsetsToLoad.remove("swe7");
+			charsetsToLoad.remove("hp8");
+			charsetsToLoad.remove("dec8");
+			charsetsToLoad.remove("koi8u");
+			charsetsToLoad.remove("keybcs2");
+			charsetsToLoad.remove("geostd8");
+			charsetsToLoad.remove("armscii8");
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+			}
+		}
+
+		return charsetsToLoad;
+	}
+
+	/**
+	 * Tests fix for BUG#4334, port #'s not being picked up for
+	 * failover/autoreconnect.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testBug4334() throws Exception {
+		if (isAdminConnectionConfigured()) {
+			Connection adminConnection = null;
+
+			try {
+				adminConnection = getAdminConnection();
+
+				int bogusPortNumber = 65534;
+
+				NonRegisteringDriver driver = new NonRegisteringDriver();
+
+				Properties oldProps = driver.parseURL(BaseTestCase.dbUrl, null);
+
+				String host = driver.host(oldProps);
+				int port = driver.port(oldProps);
+				String database = oldProps
+						.getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY);
+				String user = oldProps
+						.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY);
+				String password = oldProps
+						.getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
+
+				StringBuffer newUrlToTestPortNum = new StringBuffer(
+						"jdbc:mysql://");
+
+				if (host != null) {
+					newUrlToTestPortNum.append(host);
+				}
+
+				newUrlToTestPortNum.append(":").append(port);
+				newUrlToTestPortNum.append(",");
+
+				if (host != null) {
+					newUrlToTestPortNum.append(host);
+				}
+
+				newUrlToTestPortNum.append(":").append(bogusPortNumber);
+				newUrlToTestPortNum.append("/");
+
+				if (database != null) {
+					newUrlToTestPortNum.append(database);
+				}
+
+				if ((user != null) || (password != null)) {
+					newUrlToTestPortNum.append("?");
+
+					if (user != null) {
+						newUrlToTestPortNum.append("user=").append(user);
+
+						if (password != null) {
+							newUrlToTestPortNum.append("&");
+						}
+					}
+
+					if (password != null) {
+						newUrlToTestPortNum.append("password=")
+								.append(password);
+					}
+				}
+
+				Properties autoReconnectProps = new Properties();
+				autoReconnectProps.put("autoReconnect", "true");
+
+				System.out.println(newUrlToTestPortNum);
+
+				//
+				// First test that port #'s are being correctly picked up
+				//
+				// We do this by looking at the error message that is returned
+				//
+				Connection portNumConn = DriverManager.getConnection(
+						newUrlToTestPortNum.toString(), autoReconnectProps);
+				Statement portNumStmt = portNumConn.createStatement();
+				this.rs = portNumStmt.executeQuery("SELECT connection_id()");
+				this.rs.next();
+
+				killConnection(adminConnection, this.rs.getString(1));
+
+				try {
+					portNumStmt.executeQuery("SELECT connection_id()");
+				} catch (SQLException sqlEx) {
+					// we expect this one
+				}
+
+				try {
+					portNumStmt.executeQuery("SELECT connection_id()");
+				} catch (SQLException sqlEx) {
+					assertTrue(sqlEx.getMessage().toLowerCase().indexOf(
+							"connection refused") != -1);
+				}
+
+				//
+				// Now make sure failover works
+				//
+				StringBuffer newUrlToTestFailover = new StringBuffer(
+						"jdbc:mysql://");
+
+				if (host != null) {
+					newUrlToTestFailover.append(host);
+				}
+
+				newUrlToTestFailover.append(":").append(port);
+				newUrlToTestFailover.append(",");
+
+				if (host != null) {
+					newUrlToTestFailover.append(host);
+				}
+
+				newUrlToTestFailover.append(":").append(bogusPortNumber);
+				newUrlToTestFailover.append("/");
+
+				if (database != null) {
+					newUrlToTestFailover.append(database);
+				}
+
+				if ((user != null) || (password != null)) {
+					newUrlToTestFailover.append("?");
+
+					if (user != null) {
+						newUrlToTestFailover.append("user=").append(user);
+
+						if (password != null) {
+							newUrlToTestFailover.append("&");
+						}
+					}
+
+					if (password != null) {
+						newUrlToTestFailover.append("password=").append(
+								password);
+					}
+				}
+
+				Connection failoverConn = DriverManager.getConnection(
+						newUrlToTestFailover.toString(), autoReconnectProps);
+				Statement failoverStmt = portNumConn.createStatement();
+				this.rs = failoverStmt.executeQuery("SELECT connection_id()");
+				this.rs.next();
+
+				killConnection(adminConnection, this.rs.getString(1));
+
+				try {
+					failoverStmt.executeQuery("SELECT connection_id()");
+				} catch (SQLException sqlEx) {
+					// we expect this one
+				}
+
+				failoverStmt.executeQuery("SELECT connection_id()");
+			} finally {
+				if (adminConnection != null) {
+					adminConnection.close();
+				}
+			}
+		}
+	}
+
+	private static void killConnection(Connection adminConn, String threadId)
+			throws SQLException {
+		adminConn.createStatement().execute("KILL " + threadId);
+	}
+
+	/**
+	 * Tests fix for BUG#6966, connections starting up failed-over (due to down
+	 * master) never retry master.
+	 * 
+	 * @throws Exception
+	 *             if the test fails...Note, test is timing-dependent, but
+	 *             should work in most cases.
+	 */
+	public void testBug6966() throws Exception {
+		Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null);
+		props.setProperty("autoReconnect", "true");
+
+		// Re-build the connection information
+		int firstIndexOfHost = BaseTestCase.dbUrl.indexOf("//") + 2;
+		int lastIndexOfHost = BaseTestCase.dbUrl.indexOf("/", firstIndexOfHost);
+
+		String hostPortPair = BaseTestCase.dbUrl.substring(firstIndexOfHost,
+				lastIndexOfHost);
+
+		StringTokenizer st = new StringTokenizer(hostPortPair, ":");
+
+		String host = null;
+		String port = null;
+
+		if (st.hasMoreTokens()) {
+			String possibleHostOrPort = st.nextToken();
+
+			if (Character.isDigit(possibleHostOrPort.charAt(0)) && 
+					(possibleHostOrPort.indexOf(".") == -1 /* IPV4 */)  &&
+					(possibleHostOrPort.indexOf("::") == -1 /* IPV6 */)) {
+				port = possibleHostOrPort;
+				host = "localhost";
+			} else {
+				host = possibleHostOrPort;
+			}
+		}
+
+		if (st.hasMoreTokens()) {
+			port = st.nextToken();
+		}
+
+		if (host == null) {
+			host = "";
+		}
+
+		if (port == null) {
+			port = "3306";
+		}
+
+		StringBuffer newHostBuf = new StringBuffer();
+		newHostBuf.append(host);
+		newHostBuf.append(":65532"); // make sure the master fails
+		newHostBuf.append(",");
+		newHostBuf.append(host);
+		if (port != null) {
+			newHostBuf.append(":");
+			newHostBuf.append(port);
+		}
+
+		props.remove("PORT");
+
+		props.setProperty("HOST", newHostBuf.toString());
+		props.setProperty("queriesBeforeRetryMaster", "50");
+		props.setProperty("maxReconnects", "1");
+
+		Connection failoverConnection = null;
+
+		try {
+			failoverConnection = getConnectionWithProps("jdbc:mysql://"
+					+ newHostBuf.toString() + "/", props);
+			failoverConnection.setAutoCommit(false);
+
+			String originalConnectionId = getSingleIndexedValueWithQuery(
+					failoverConnection, 1, "SELECT CONNECTION_ID()").toString();
+			
+			for (int i = 0; i < 49; i++) {
+				failoverConnection.createStatement().executeQuery("SELECT 1");
+			}
+
+			((com.mysql.jdbc.Connection)failoverConnection).clearHasTriedMaster();
+			
+			failoverConnection.setAutoCommit(true);
+
+			String newConnectionId = getSingleIndexedValueWithQuery(
+					failoverConnection, 1, "SELECT CONNECTION_ID()").toString();
+			
+			assertTrue(((com.mysql.jdbc.Connection)failoverConnection).hasTriedMaster());
+			
+			assertTrue(!newConnectionId.equals(originalConnectionId));
+
+			failoverConnection.createStatement().executeQuery("SELECT 1");
+		} finally {
+			if (failoverConnection != null) {
+				failoverConnection.close();
+			}
+		}
+	}
+
+	/**
+	 * Test fix for BUG#7952 -- Infinite recursion when 'falling back' to master
+	 * in failover configuration.
+	 * 
+	 * @throws Exception
+	 *             if the tests fails.
+	 */
+	public void testBug7952() throws Exception {
+		Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null);
+		props.setProperty("autoReconnect", "true");
+
+		// Re-build the connection information
+		int firstIndexOfHost = BaseTestCase.dbUrl.indexOf("//") + 2;
+		int lastIndexOfHost = BaseTestCase.dbUrl.indexOf("/", firstIndexOfHost);
+
+		String hostPortPair = BaseTestCase.dbUrl.substring(firstIndexOfHost,
+				lastIndexOfHost);
+
+		StringTokenizer st = new StringTokenizer(hostPortPair, ":");
+
+		String host = null;
+		String port = null;
+
+		if (st.hasMoreTokens()) {
+			String possibleHostOrPort = st.nextToken();
+
+			if (possibleHostOrPort.indexOf(".") == -1
+					&& Character.isDigit(possibleHostOrPort.charAt(0))) {
+				port = possibleHostOrPort;
+				host = "localhost";
+			} else {
+				host = possibleHostOrPort;
+			}
+		}
+
+		if (st.hasMoreTokens()) {
+			port = st.nextToken();
+		}
+
+		if (host == null) {
+			host = "";
+		}
+
+		if (port == null) {
+			port = "3306";
+		}
+
+		StringBuffer newHostBuf = new StringBuffer();
+		newHostBuf.append(host);
+		newHostBuf.append(":");
+		newHostBuf.append(port);
+		newHostBuf.append(",");
+		newHostBuf.append(host);
+		if (port != null) {
+			newHostBuf.append(":");
+			newHostBuf.append(port);
+		}
+
+		props.remove("PORT");
+
+		props.setProperty("HOST", newHostBuf.toString());
+		props.setProperty("queriesBeforeRetryMaster", "10");
+		props.setProperty("maxReconnects", "1");
+
+		Connection failoverConnection = null;
+		Connection killerConnection = getConnectionWithProps(null);
+
+		try {
+			failoverConnection = getConnectionWithProps("jdbc:mysql://"
+					+ newHostBuf + "/", props);
+			((com.mysql.jdbc.Connection) failoverConnection)
+					.setPreferSlaveDuringFailover(true);
+			failoverConnection.setAutoCommit(false);
+
+			String failoverConnectionId = getSingleIndexedValueWithQuery(
+					failoverConnection, 1, "SELECT CONNECTION_ID()").toString();
+
+			System.out.println("Connection id: " + failoverConnectionId);
+
+			killConnection(killerConnection, failoverConnectionId);
+
+			Thread.sleep(3000); // This can take some time....
+
+			try {
+				failoverConnection.createStatement().executeQuery("SELECT 1");
+			} catch (SQLException sqlEx) {
+				assertTrue("08S01".equals(sqlEx.getSQLState()));
+			}
+
+			((com.mysql.jdbc.Connection) failoverConnection)
+					.setPreferSlaveDuringFailover(false);
+			((com.mysql.jdbc.Connection) failoverConnection)
+					.setFailedOver(true);
+
+			failoverConnection.setAutoCommit(true);
+
+			String failedConnectionId = getSingleIndexedValueWithQuery(
+					failoverConnection, 1, "SELECT CONNECTION_ID()").toString();
+			System.out.println("Failed over connection id: "
+					+ failedConnectionId);
+
+			((com.mysql.jdbc.Connection) failoverConnection)
+					.setPreferSlaveDuringFailover(false);
+			((com.mysql.jdbc.Connection) failoverConnection)
+					.setFailedOver(true);
+
+			for (int i = 0; i < 30; i++) {
+				failoverConnection.setAutoCommit(true);
+				System.out.println(getSingleIndexedValueWithQuery(
+						failoverConnection, 1, "SELECT CONNECTION_ID()"));
+				// failoverConnection.createStatement().executeQuery("SELECT
+				// 1");
+				failoverConnection.setAutoCommit(true);
+			}
+
+			String fallbackConnectionId = getSingleIndexedValueWithQuery(
+					failoverConnection, 1, "SELECT CONNECTION_ID()").toString();
+			System.out.println("fallback connection id: "
+					+ fallbackConnectionId);
+
+			/*
+			 * long begin = System.currentTimeMillis();
+			 * 
+			 * failoverConnection.setAutoCommit(true);
+			 * 
+			 * long end = System.currentTimeMillis();
+			 * 
+			 * assertTrue("Probably didn't try failing back to the
+			 * master....check test", (end - begin) > 500);
+			 * 
+			 * failoverConnection.createStatement().executeQuery("SELECT 1");
+			 */
+		} finally {
+			if (failoverConnection != null) {
+				failoverConnection.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#7607 - MS932, SHIFT_JIS and Windows_31J not recog. as
+	 * aliases for sjis.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug7607() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			Connection ms932Conn = null, cp943Conn = null, shiftJisConn = null, windows31JConn = null;
+
+			try {
+				Properties props = new Properties();
+				props.setProperty("characterEncoding", "MS932");
+
+				ms932Conn = getConnectionWithProps(props);
+
+				this.rs = ms932Conn.createStatement().executeQuery(
+						"SHOW VARIABLES LIKE 'character_set_client'");
+				assertTrue(this.rs.next());
+				String encoding = this.rs.getString(2);
+				if (!versionMeetsMinimum(5, 0, 3)
+						&& !versionMeetsMinimum(4, 1, 11)) {
+					assertEquals("sjis", encoding.toLowerCase(Locale.ENGLISH));
+				} else {
+					assertEquals("cp932", encoding.toLowerCase(Locale.ENGLISH));
+				}
+
+				this.rs = ms932Conn.createStatement().executeQuery(
+						"SELECT 'abc'");
+				assertTrue(this.rs.next());
+
+				String charsetToCheck = "ms932";
+
+				if (versionMeetsMinimum(5, 0, 3)
+						|| versionMeetsMinimum(4, 1, 11)) {
+					charsetToCheck = "windows-31j";
+				}
+
+				assertEquals(charsetToCheck,
+						((com.mysql.jdbc.ResultSetMetaData) this.rs
+								.getMetaData()).getColumnCharacterSet(1)
+								.toLowerCase(Locale.ENGLISH));
+
+				try {
+					ms932Conn.createStatement().executeUpdate(
+							"drop table if exists testBug7607");
+					ms932Conn
+							.createStatement()
+							.executeUpdate(
+									"create table testBug7607 (sortCol int, col1 varchar(100) ) character set sjis");
+					ms932Conn.createStatement().executeUpdate(
+							"insert into testBug7607 values(1, 0x835C)"); // standard
+					// sjis
+					ms932Conn.createStatement().executeUpdate(
+							"insert into testBug7607 values(2, 0x878A)"); // NEC
+					// kanji
+
+					this.rs = ms932Conn
+							.createStatement()
+							.executeQuery(
+									"SELECT col1 FROM testBug7607 ORDER BY sortCol ASC");
+					assertTrue(this.rs.next());
+					String asString = this.rs.getString(1);
+					assertTrue("\u30bd".equals(asString));
+
+					// Can't be fixed unless server is fixed,
+					// this is fixed in 4.1.7.
+
+					assertTrue(this.rs.next());
+					asString = this.rs.getString(1);
+					assertEquals("\u3231", asString);
+				} finally {
+					ms932Conn.createStatement().executeUpdate(
+							"drop table if exists testBug7607");
+				}
+
+				props = new Properties();
+				props.setProperty("characterEncoding", "SHIFT_JIS");
+
+				shiftJisConn = getConnectionWithProps(props);
+
+				this.rs = shiftJisConn.createStatement().executeQuery(
+						"SHOW VARIABLES LIKE 'character_set_client'");
+				assertTrue(this.rs.next());
+				encoding = this.rs.getString(2);
+				assertTrue("sjis".equalsIgnoreCase(encoding));
+
+				this.rs = shiftJisConn.createStatement().executeQuery(
+						"SELECT 'abc'");
+				assertTrue(this.rs.next());
+
+				String charSetUC = ((com.mysql.jdbc.ResultSetMetaData) this.rs
+						.getMetaData()).getColumnCharacterSet(1).toUpperCase(
+						Locale.US);
+
+				if (isRunningOnJdk131()) {
+					assertEquals("WINDOWS-31J", charSetUC);
+				} else {
+					assertEquals("SHIFT_JIS", charSetUC);
+				}
+
+				props = new Properties();
+				props.setProperty("characterEncoding", "WINDOWS-31J");
+
+				windows31JConn = getConnectionWithProps(props);
+
+				this.rs = windows31JConn.createStatement().executeQuery(
+						"SHOW VARIABLES LIKE 'character_set_client'");
+				assertTrue(this.rs.next());
+				encoding = this.rs.getString(2);
+
+				if (!versionMeetsMinimum(5, 0, 3)
+						&& !versionMeetsMinimum(4, 1, 11)) {
+					assertEquals("sjis", encoding.toLowerCase(Locale.ENGLISH));
+				} else {
+					assertEquals("cp932", encoding.toLowerCase(Locale.ENGLISH));
+				}
+
+				this.rs = windows31JConn.createStatement().executeQuery(
+						"SELECT 'abc'");
+				assertTrue(this.rs.next());
+
+				if (!versionMeetsMinimum(4, 1, 11)) {
+					assertEquals("sjis".toLowerCase(Locale.ENGLISH),
+							((com.mysql.jdbc.ResultSetMetaData) this.rs
+									.getMetaData()).getColumnCharacterSet(1)
+									.toLowerCase(Locale.ENGLISH));
+				} else {
+					assertEquals("windows-31j".toLowerCase(Locale.ENGLISH),
+							((com.mysql.jdbc.ResultSetMetaData) this.rs
+									.getMetaData()).getColumnCharacterSet(1)
+									.toLowerCase(Locale.ENGLISH));
+				}
+
+				props = new Properties();
+				props.setProperty("characterEncoding", "CP943");
+
+				cp943Conn = getConnectionWithProps(props);
+
+				this.rs = cp943Conn.createStatement().executeQuery(
+						"SHOW VARIABLES LIKE 'character_set_client'");
+				assertTrue(this.rs.next());
+				encoding = this.rs.getString(2);
+				assertTrue("sjis".equalsIgnoreCase(encoding));
+
+				this.rs = cp943Conn.createStatement().executeQuery(
+						"SELECT 'abc'");
+				assertTrue(this.rs.next());
+
+				charSetUC = ((com.mysql.jdbc.ResultSetMetaData) this.rs
+						.getMetaData()).getColumnCharacterSet(1).toUpperCase(
+						Locale.US);
+
+				if (isRunningOnJdk131()) {
+					assertEquals("WINDOWS-31J", charSetUC);
+				} else {
+					assertEquals("CP943", charSetUC);
+				}
+
+			} finally {
+				if (ms932Conn != null) {
+					ms932Conn.close();
+				}
+
+				if (shiftJisConn != null) {
+					shiftJisConn.close();
+				}
+
+				if (windows31JConn != null) {
+					windows31JConn.close();
+				}
+
+				if (cp943Conn != null) {
+					cp943Conn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * In some case Connector/J's round-robin function doesn't work.
+	 * 
+	 * I had 2 mysqld, node1 "localhost:3306" and node2 "localhost:3307".
+	 * 
+	 * 1. node1 is up, node2 is up
+	 * 
+	 * 2. java-program connect to node1 by using properties
+	 * "autoRecconect=true","roundRobinLoadBalance=true","failOverReadOnly=false".
+	 * 
+	 * 3. node1 is down, node2 is up
+	 * 
+	 * 4. java-program execute a query and fail, but Connector/J's round-robin
+	 * fashion failover work and if java-program retry a query it can succeed
+	 * (connection is change to node2 by Connector/j)
+	 * 
+	 * 5. node1 is up, node2 is up
+	 * 
+	 * 6. node1 is up, node2 is down
+	 * 
+	 * 7. java-program execute a query, but this time Connector/J doesn't work
+	 * althought node1 is up and usable.
+	 * 
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug8643() throws Exception {
+		if (runMultiHostTests()) {
+			Properties defaultProps = getMasterSlaveProps();
+
+			defaultProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
+			defaultProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY);
+
+			defaultProps.put("autoReconnect", "true");
+			defaultProps.put("roundRobinLoadBalance", "true");
+			defaultProps.put("failOverReadOnly", "false");
+
+			Connection con = null;
+			try {
+				con = DriverManager.getConnection(getMasterSlaveUrl(),
+						defaultProps);
+				Statement stmt1 = con.createStatement();
+
+				ResultSet rs1 = stmt1
+						.executeQuery("show variables like 'port'");
+				rs1.next();
+
+				rs1 = stmt1.executeQuery("select connection_id()");
+				rs1.next();
+				String originalConnectionId = rs1.getString(1);
+				this.stmt.executeUpdate("kill " + originalConnectionId);
+
+				int numLoops = 8;
+
+				SQLException caughtException = null;
+
+				while (caughtException == null && numLoops > 0) {
+					numLoops--;
+
+					try {
+						rs1 = stmt1.executeQuery("show variables like 'port'");
+					} catch (SQLException sqlEx) {
+						caughtException = sqlEx;
+					}
+				}
+
+				assertNotNull(caughtException);
+
+				// failover and retry
+				rs1 = stmt1.executeQuery("show variables like 'port'");
+
+				rs1.next();
+				assertTrue(!((com.mysql.jdbc.Connection) con)
+						.isMasterConnection());
+
+				rs1 = stmt1.executeQuery("select connection_id()");
+				rs1.next();
+				String nextConnectionId = rs1.getString(1);
+				assertTrue(!nextConnectionId.equals(originalConnectionId));
+
+				this.stmt.executeUpdate("kill " + nextConnectionId);
+
+				numLoops = 8;
+
+				caughtException = null;
+
+				while (caughtException == null && numLoops > 0) {
+					numLoops--;
+
+					try {
+						rs1 = stmt1.executeQuery("show variables like 'port'");
+					} catch (SQLException sqlEx) {
+						caughtException = sqlEx;
+					}
+				}
+
+				assertNotNull(caughtException);
+
+				// failover and retry
+				rs1 = stmt1.executeQuery("show variables like 'port'");
+
+				rs1.next();
+				assertTrue(((com.mysql.jdbc.Connection) con)
+						.isMasterConnection());
+
+			} finally {
+				if (con != null) {
+					try {
+						con.close();
+					} catch (Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#9206, can not use 'UTF-8' for characterSetResults
+	 * configuration property.
+	 */
+	public void testBug9206() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("characterSetResults", "UTF-8");
+		getConnectionWithProps(props).close();
+	}
+
+	/**
+	 * These two charsets have different names depending on version of MySQL
+	 * server.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testNewCharsetsConfiguration() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("useUnicode", "true");
+		props.setProperty("characterEncoding", "EUC_KR");
+		getConnectionWithProps(props).close();
+
+		props = new Properties();
+		props.setProperty("useUnicode", "true");
+		props.setProperty("characterEncoding", "KOI8_R");
+		getConnectionWithProps(props).close();
+	}
+
+	/**
+	 * Tests fix for BUG#10144 - Memory leak in ServerPreparedStatement if
+	 * serverPrepare() fails.
+	 */
+
+	public void testBug10144() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			Properties props = new Properties();
+			props.setProperty("emulateUnsupportedPstmts", "false");
+			props.setProperty("useServerPrepStmts", "true");
+
+			Connection bareConn = getConnectionWithProps(props);
+
+			int currentOpenStatements = ((com.mysql.jdbc.Connection) bareConn)
+					.getActiveStatementCount();
+
+			try {
+				bareConn.prepareStatement("Boo!");
+				fail("Should not've been able to prepare that one!");
+			} catch (SQLException sqlEx) {
+				assertEquals(currentOpenStatements,
+						((com.mysql.jdbc.Connection) bareConn)
+								.getActiveStatementCount());
+			} finally {
+				if (bareConn != null) {
+					bareConn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#10496 - SQLException is thrown when using property
+	 * "characterSetResults"
+	 */
+	public void testBug10496() throws Exception {
+		if (versionMeetsMinimum(5, 0, 3)) {
+			Properties props = new Properties();
+			props.setProperty("useUnicode", "true");
+			props.setProperty("characterEncoding", "WINDOWS-31J");
+			props.setProperty("characterSetResults", "WINDOWS-31J");
+			getConnectionWithProps(props).close();
+
+			props = new Properties();
+			props.setProperty("useUnicode", "true");
+			props.setProperty("characterEncoding", "EUC_JP");
+			props.setProperty("characterSetResults", "EUC_JP");
+			getConnectionWithProps(props).close();
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11259, autoReconnect ping causes exception on
+	 * connection startup.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11259() throws Exception {
+		Connection dsConn = null;
+		try {
+			Properties props = new Properties();
+			props.setProperty("autoReconnect", "true");
+			dsConn = getConnectionWithProps(props);
+		} finally {
+			if (dsConn != null) {
+				dsConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11879 -- ReplicationConnection won't switch to slave,
+	 * throws "Catalog can't be null" exception.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug11879() throws Exception {
+		if (runMultiHostTests()) {
+			Connection replConn = null;
+
+			try {
+				replConn = getMasterSlaveReplicationConnection();
+				replConn.setReadOnly(true);
+				replConn.setReadOnly(false);
+			} finally {
+				if (replConn != null) {
+					replConn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11976 - maxPerformance.properties mis-spells
+	 * "elideSetAutoCommits".
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11976() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		Properties props = new Properties();
+		props.setProperty("useConfigs", "maxPerformance");
+
+		Connection maxPerfConn = getConnectionWithProps(props);
+		assertEquals(true, ((com.mysql.jdbc.Connection) maxPerfConn)
+				.getElideSetAutoCommits());
+	}
+
+	/**
+	 * Tests fix for BUG#12218, properties shared between master and slave with
+	 * replication connection.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug12218() throws Exception {
+		if (runMultiHostTests()) {
+			Connection replConn = null;
+
+			try {
+				replConn = getMasterSlaveReplicationConnection();
+				assertTrue(!((ReplicationConnection) replConn)
+						.getMasterConnection().hasSameProperties(
+								((ReplicationConnection) replConn)
+										.getSlavesConnection()));
+			} finally {
+				if (replConn != null) {
+					replConn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#12229 - explainSlowQueries hangs with server-side
+	 * prepared statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug12229() throws Exception {
+		createTable("testBug12229", "(`int_field` integer )");
+		this.stmt.executeUpdate("insert into testBug12229 values (123456),(1)");
+
+		Properties props = new Properties();
+		props.put("profileSQL", "true");
+		props.put("slowQueryThresholdMillis", "0");
+		props.put("logSlowQueries", "true");
+		props.put("explainSlowQueries", "true");
+		props.put("useServerPrepStmts", "true");
+
+		Connection explainConn = getConnectionWithProps(props);
+
+		this.pstmt = explainConn
+				.prepareStatement("SELECT `int_field` FROM `testBug12229` WHERE `int_field` = ?");
+		this.pstmt.setInt(1, 1);
+
+		this.rs = this.pstmt.executeQuery();
+		assertTrue(this.rs.next());
+
+		this.rs = this.pstmt.executeQuery();
+		assertTrue(this.rs.next());
+
+		this.rs = this.pstmt.executeQuery();
+		assertTrue(this.rs.next());
+	}
+
+	/**
+	 * Tests fix for BUG#12752 - Cp1251 incorrectly mapped to win1251 for
+	 * servers newer than 4.0.x.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug12752() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("characterEncoding", "Cp1251");
+		getConnectionWithProps(props).close();
+	}
+
+	/**
+	 * Tests fix for BUG#12753, sessionVariables=....=...., doesn't work as it's
+	 * tokenized incorrectly.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug12753() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			Properties props = new Properties();
+			props.setProperty("sessionVariables", "sql_mode=ansi");
+
+			Connection sessionConn = null;
+
+			try {
+				sessionConn = getConnectionWithProps(props);
+
+				String sqlMode = getMysqlVariable(sessionConn, "sql_mode");
+				assertTrue(sqlMode.indexOf("ANSI") != -1);
+			} finally {
+				if (sessionConn != null) {
+					sessionConn.close();
+					sessionConn = null;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#13048 - maxQuerySizeToLog is not respected.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug13048() throws Exception {
+
+		Connection profileConn = null;
+		PrintStream oldErr = System.err;
+
+		try {
+			ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+			System.setErr(new PrintStream(bOut));
+
+			Properties props = new Properties();
+			props.setProperty("profileSQL", "true");
+			props.setProperty("maxQuerySizeToLog", "2");
+			props.setProperty("logger", "com.mysql.jdbc.log.StandardLogger");
+
+			profileConn = getConnectionWithProps(props);
+
+			StringBuffer queryBuf = new StringBuffer("SELECT '");
+
+			for (int i = 0; i < 500; i++) {
+				queryBuf.append("a");
+			}
+
+			queryBuf.append("'");
+
+			this.rs = profileConn.createStatement().executeQuery(
+					queryBuf.toString());
+			this.rs.close();
+
+			String logString = new String(bOut.toString("ISO8859-1"));
+			assertTrue(logString.indexOf("... (truncated)") != -1);
+
+			bOut = new ByteArrayOutputStream();
+			System.setErr(new PrintStream(bOut));
+
+			this.rs = profileConn.prepareStatement(queryBuf.toString())
+					.executeQuery();
+			logString = new String(bOut.toString("ISO8859-1"));
+
+			assertTrue(logString.indexOf("... (truncated)") != -1);
+		} finally {
+			System.setErr(oldErr);
+
+			if (profileConn != null) {
+				profileConn.close();
+			}
+
+			if (this.rs != null) {
+				ResultSet toClose = this.rs;
+				this.rs = null;
+				toClose.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#13453 - can't use & or = in URL configuration values
+	 * (we now allow you to use www-form-encoding).
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug13453() throws Exception {
+		StringBuffer urlBuf = new StringBuffer(dbUrl);
+
+		if (dbUrl.indexOf('?') == -1) {
+			urlBuf.append('?');
+		} else {
+			urlBuf.append('&');
+		}
+
+		urlBuf.append("sessionVariables=@testBug13453='%25%26+%3D'");
+
+		Connection encodedConn = null;
+
+		try {
+			encodedConn = DriverManager.getConnection(urlBuf.toString(), null);
+
+			this.rs = encodedConn.createStatement().executeQuery(
+					"SELECT @testBug13453");
+			assertTrue(this.rs.next());
+			assertEquals("%& =", this.rs.getString(1));
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+
+			if (encodedConn != null) {
+				encodedConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#15065 - Usage advisor complains about unreferenced
+	 * columns, even though they've been referenced.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug15065() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		createTable("testBug15065", "(field1 int)");
+
+		this.stmt.executeUpdate("INSERT INTO testBug15065 VALUES (1)");
+
+		Connection advisorConn = null;
+		Statement advisorStmt = null;
+
+		try {
+			Properties props = new Properties();
+			props.setProperty("useUsageAdvisor", "true");
+			props.setProperty("logger", "com.mysql.jdbc.log.StandardLogger");
+
+			advisorConn = getConnectionWithProps(props);
+			advisorStmt = advisorConn.createStatement();
+
+			Method[] getMethods = ResultSet.class.getMethods();
+
+			PrintStream oldErr = System.err;
+
+			try {
+				ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+				System.setErr(new PrintStream(bOut));
+				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")) {
+							Class[] parameterTypes = getMethods[i]
+									.getParameterTypes();
+
+							if (parameterTypes.length == 1
+									&& parameterTypes[0] == Integer.TYPE) {
+								if (j == 0) {
+									this.rs = advisorStmt
+											.executeQuery("SELECT COUNT(*) FROM testBug15065");
+								} else {
+									this.rs = advisorConn
+											.prepareStatement(
+													"SELECT COUNT(*) FROM testBug15065")
+											.executeQuery();
+								}
+
+								this.rs.next();
+
+								try {
+
+									getMethods[i].invoke(this.rs,
+											new Object[] { new Integer(1) });
+								} catch (InvocationTargetException invokeEx) {
+									// we don't care about bad values, just that
+									// the
+									// column gets "touched"
+									if (!invokeEx
+											.getCause()
+											.getClass()
+											.isAssignableFrom(
+													java.sql.SQLException.class)
+											&& !invokeEx
+													.getCause()
+													.getClass()
+													.getName()
+													.equals(
+															"com.mysql.jdbc.NotImplemented")) {
+										throw invokeEx;
+									}
+								}
+
+								this.rs.close();
+								this.rs = null;
+							}
+						}
+					}
+				}
+
+				String logOut = bOut.toString("ISO8859-1");
+
+				if (logOut.indexOf(".Level") != -1) {
+					return; // we ignore for warnings
+				}
+
+				assertTrue("Usage advisor complained about columns:\n\n"
+						+ logOut, logOut.indexOf("columns") == -1);
+			} finally {
+				System.setErr(oldErr);
+			}
+		} finally {
+			if (advisorConn != null) {
+				advisorConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#15544, no "dos" character set in MySQL > 4.1.0
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug15544() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("characterEncoding", "Cp437");
+		Connection dosConn = null;
+
+		try {
+			dosConn = getConnectionWithProps(props);
+		} finally {
+			if (dosConn != null) {
+				dosConn.close();
+			}
+		}
+	}
+
+	public void testCSC5765() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		Properties props = new Properties();
+		props.setProperty("useUnicode", "true");
+		props.setProperty("characterEncoding", "utf8");
+		props.setProperty("characterSetResults", "utf8");
+		props.setProperty("connectionCollation", "utf8_bin");
+
+		Connection utf8Conn = null;
+
+		try {
+			utf8Conn = getConnectionWithProps(props);
+			this.rs = utf8Conn.createStatement().executeQuery(
+					"SHOW VARIABLES LIKE 'character_%'");
+			while (this.rs.next()) {
+				System.out.println(this.rs.getString(1) + " = "
+						+ this.rs.getString(2));
+			}
+
+			this.rs = utf8Conn.createStatement().executeQuery(
+					"SHOW VARIABLES LIKE 'collation_%'");
+			while (this.rs.next()) {
+				System.out.println(this.rs.getString(1) + " = "
+						+ this.rs.getString(2));
+			}
+		} finally {
+			if (utf8Conn != null) {
+				utf8Conn.close();
+			}
+		}
+	}
+
+	private Connection getMasterSlaveReplicationConnection()
+			throws SQLException {
+
+		Connection replConn = new ReplicationDriver().connect(
+				getMasterSlaveUrl(), getMasterSlaveProps());
+
+		return replConn;
+	}
+
+	protected String getMasterSlaveUrl() throws SQLException {
+		StringBuffer urlBuf = new StringBuffer("jdbc:mysql://");
+		Properties defaultProps = getPropertiesFromTestsuiteUrl();
+		String hostname = defaultProps
+				.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY);
+
+		int colonIndex = hostname.indexOf(":");
+
+		String portNumber = "3306";
+
+		if (colonIndex != -1 && !hostname.startsWith(":")) {
+			portNumber = hostname.substring(colonIndex + 1);
+			hostname = hostname.substring(0, colonIndex);
+		} else if (hostname.startsWith(":")) {
+			portNumber = hostname.substring(1);
+			hostname = "localhost";
+		} else {
+			portNumber = defaultProps
+					.getProperty(NonRegisteringDriver.PORT_PROPERTY_KEY);
+		}
+
+		for (int i = 0; i < 2; i++) {
+			urlBuf.append(hostname);
+			urlBuf.append(":");
+			urlBuf.append(portNumber);
+
+			if (i == 0) {
+				urlBuf.append(",");
+			}
+		}
+		urlBuf.append("/");
+
+		return urlBuf.toString();
+	}
+
+	protected Properties getMasterSlaveProps() throws SQLException {
+		Properties props = getPropertiesFromTestsuiteUrl();
+
+		props.remove(NonRegisteringDriver.HOST_PROPERTY_KEY);
+		props.remove(NonRegisteringDriver.PORT_PROPERTY_KEY);
+
+		return props;
+	}
+
+	/**
+	 * Tests fix for BUG#15570 - ReplicationConnection incorrectly copies state,
+	 * doesn't transfer connection context correctly when transitioning between
+	 * the same read-only states.
+	 * 
+	 * (note, this test will fail if the test user doesn't have permission to
+	 * "USE 'mysql'".
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug15570() throws Exception {
+		Connection replConn = null;
+
+		try {
+			replConn = getMasterSlaveReplicationConnection();
+
+			int masterConnectionId = Integer
+					.parseInt(getSingleIndexedValueWithQuery(replConn, 1,
+							"SELECT CONNECTION_ID()").toString());
+
+			replConn.setReadOnly(false);
+
+			assertEquals(masterConnectionId, Integer
+					.parseInt(getSingleIndexedValueWithQuery(replConn, 1,
+							"SELECT CONNECTION_ID()").toString()));
+
+			String currentCatalog = replConn.getCatalog();
+
+			replConn.setCatalog(currentCatalog);
+			assertEquals(currentCatalog, replConn.getCatalog());
+
+			replConn.setReadOnly(true);
+
+			int slaveConnectionId = Integer
+					.parseInt(getSingleIndexedValueWithQuery(replConn, 1,
+							"SELECT CONNECTION_ID()").toString());
+
+			// The following test is okay for now, as the chance
+			// of MySQL wrapping the connection id counter during our
+			// testsuite is very small.
+
+			assertTrue("Slave id " + slaveConnectionId
+					+ " is not newer than master id " + masterConnectionId,
+					slaveConnectionId > masterConnectionId);
+
+			assertEquals(currentCatalog, replConn.getCatalog());
+
+			String newCatalog = "mysql";
+
+			replConn.setCatalog(newCatalog);
+			assertEquals(newCatalog, replConn.getCatalog());
+
+			replConn.setReadOnly(true);
+			assertEquals(newCatalog, replConn.getCatalog());
+
+			replConn.setReadOnly(false);
+			assertEquals(masterConnectionId, Integer
+					.parseInt(getSingleIndexedValueWithQuery(replConn, 1,
+							"SELECT CONNECTION_ID()").toString()));
+		} finally {
+			if (replConn != null) {
+				replConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests bug where downed slave caused round robin load balance not to
+	 * cycle back to first host in the list.
+	 * 
+	 * @throws Exception
+	 *             if the test fails...Note, test is timing-dependent, but
+	 *             should work in most cases.
+	 */
+	public void testBug23281() throws Exception {
+		Properties props = new Driver().parseURL(BaseTestCase.dbUrl, null);
+		props.setProperty("autoReconnect", "false");
+		props.setProperty("roundRobinLoadBalance", "true");
+		props.setProperty("failoverReadOnly", "false");
+		props.setProperty("connectTimeout", "5000");
+		
+		// Re-build the connection information
+		int firstIndexOfHost = BaseTestCase.dbUrl.indexOf("//") + 2;
+		int lastIndexOfHost = BaseTestCase.dbUrl.indexOf("/", firstIndexOfHost);
+	
+		String hostPortPair = BaseTestCase.dbUrl.substring(firstIndexOfHost,
+				lastIndexOfHost);
+	
+		StringTokenizer st = new StringTokenizer(hostPortPair, ":");
+	
+		String host = null;
+		String port = null;
+	
+		if (st.hasMoreTokens()) {
+			String possibleHostOrPort = st.nextToken();
+	
+			if (Character.isDigit(possibleHostOrPort.charAt(0)) && 
+					(possibleHostOrPort.indexOf(".") == -1 /* IPV4 */)  &&
+					(possibleHostOrPort.indexOf("::") == -1 /* IPV6 */)) {
+				port = possibleHostOrPort;
+				host = "localhost";
+			} else {
+				host = possibleHostOrPort;
+			}
+		}
+	
+		if (st.hasMoreTokens()) {
+			port = st.nextToken();
+		}
+	
+		if (host == null) {
+			host = "";
+		}
+	
+		if (port == null) {
+			port = "3306";
+		}
+	
+		StringBuffer newHostBuf = new StringBuffer();
+		
+		newHostBuf.append(host);
+		if (port != null) {
+			newHostBuf.append(":");
+			newHostBuf.append(port);
+		}
+	
+		newHostBuf.append(",");
+		//newHostBuf.append(host);
+		newHostBuf.append("192.0.2.1"); // non-exsitent machine from RFC3330 test network
+		newHostBuf.append(":65532"); // make sure the slave fails
+		
+		props.remove("PORT");
+		props.remove("HOST");
+	
+		Connection failoverConnection = null;
+	
+		try {
+			failoverConnection = getConnectionWithProps("jdbc:mysql://"
+					+ newHostBuf.toString() + "/", props);
+		
+			String originalConnectionId = getSingleIndexedValueWithQuery(
+					failoverConnection, 1, "SELECT CONNECTION_ID()").toString();
+			
+			System.out.println(originalConnectionId);
+			
+			Connection nextConnection = getConnectionWithProps("jdbc:mysql://"
+					+ newHostBuf.toString() + "/", props);
+			
+			String nextId = getSingleIndexedValueWithQuery(
+					nextConnection, 1, "SELECT CONNECTION_ID()").toString();
+			
+			System.out.println(nextId);
+			
+		} finally {
+			if (failoverConnection != null) {
+				failoverConnection.close();
+			}
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/DataSourceRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/DataSourceRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/DataSourceRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,474 @@
+/*
+ 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 testsuite.regression;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Hashtable;
+import java.util.Properties;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.RefAddr;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.DataSource;
+import javax.sql.PooledConnection;
+
+import testsuite.BaseTestCase;
+import testsuite.simple.DataSourceTest;
+
+import com.mysql.jdbc.NonRegisteringDriver;
+import com.mysql.jdbc.integration.jboss.MysqlValidConnectionChecker;
+import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
+import com.mysql.jdbc.jdbc2.optional.MysqlDataSource;
+import com.mysql.jdbc.jdbc2.optional.MysqlDataSourceFactory;
+import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
+
+/**
+ * Tests fixes for bugs related to datasources.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: DataSourceRegressionTest.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class DataSourceRegressionTest extends BaseTestCase {
+
+	public final static String DS_DATABASE_PROP_NAME = "com.mysql.jdbc.test.ds.db";
+
+	public final static String DS_HOST_PROP_NAME = "com.mysql.jdbc.test.ds.host";
+
+	public final static String DS_PASSWORD_PROP_NAME = "com.mysql.jdbc.test.ds.password";
+
+	public final static String DS_PORT_PROP_NAME = "com.mysql.jdbc.test.ds.port";
+
+	public final static String DS_USER_PROP_NAME = "com.mysql.jdbc.test.ds.user";
+
+	private Context ctx;
+
+	private File tempDir;
+
+	/**
+	 * Creates a new DataSourceRegressionTest suite for the given test name
+	 * 
+	 * @param name
+	 *            the name of the testcase to run.
+	 */
+	public DataSourceRegressionTest(String name) {
+		super(name);
+
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(DataSourceTest.class);
+	}
+
+	/**
+	 * Sets up this test, calling registerDataSource() to bind a DataSource into
+	 * JNDI, using the FSContext JNDI provider from Sun
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		createJNDIContext();
+	}
+
+	/**
+	 * Un-binds the DataSource, and cleans up the filesystem
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void tearDown() throws Exception {
+		this.ctx.unbind(this.tempDir.getAbsolutePath() + "/test");
+		this.ctx.unbind(this.tempDir.getAbsolutePath() + "/testNoUrl");
+		this.ctx.close();
+		this.tempDir.delete();
+
+		super.tearDown();
+	}
+
+	/**
+	 * Tests fix for BUG#4808- Calling .close() twice on a PooledConnection
+	 * causes NPE.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testBug4808() throws Exception {
+		MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();
+		ds.setURL(BaseTestCase.dbUrl);
+		PooledConnection closeMeTwice = ds.getPooledConnection();
+		closeMeTwice.close();
+		closeMeTwice.close();
+
+	}
+
+	/**
+	 * Tests fix for Bug#3848, port # alone parsed incorrectly
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testBug3848() throws Exception {
+		String jndiName = "/testBug3848";
+
+		String databaseName = System.getProperty(DS_DATABASE_PROP_NAME);
+		String userName = System.getProperty(DS_USER_PROP_NAME);
+		String password = System.getProperty(DS_PASSWORD_PROP_NAME);
+		String port = System.getProperty(DS_PORT_PROP_NAME);
+
+		// Only run this test if at least one of the above are set
+		if ((databaseName != null) || (userName != null) || (password != null)
+				|| (port != null)) {
+			MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();
+
+			if (databaseName != null) {
+				ds.setDatabaseName(databaseName);
+			}
+
+			if (userName != null) {
+				ds.setUser(userName);
+			}
+
+			if (password != null) {
+				ds.setPassword(password);
+			}
+
+			if (port != null) {
+				ds.setPortNumber(Integer.parseInt(port));
+			}
+
+			bindDataSource(jndiName, ds);
+
+			ConnectionPoolDataSource boundDs = null;
+
+			try {
+				boundDs = (ConnectionPoolDataSource) lookupDatasourceInJNDI(jndiName);
+
+				assertTrue("Datasource not bound", boundDs != null);
+
+				Connection dsConn = null;
+
+				try {
+					dsConn = boundDs.getPooledConnection().getConnection();
+				} finally {
+					if (dsConn != null) {
+						dsConn.close();
+					}
+				}
+			} finally {
+				if (boundDs != null) {
+					this.ctx.unbind(jndiName);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests that we can get a connection from the DataSource bound in JNDI
+	 * during test setup
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testBug3920() throws Exception {
+		String jndiName = "/testBug3920";
+
+		String databaseName = System.getProperty(DS_DATABASE_PROP_NAME);
+		String userName = System.getProperty(DS_USER_PROP_NAME);
+		String password = System.getProperty(DS_PASSWORD_PROP_NAME);
+		String port = System.getProperty(DS_PORT_PROP_NAME);
+		String serverName = System.getProperty(DS_HOST_PROP_NAME);
+
+		// Only run this test if at least one of the above are set
+		if ((databaseName != null) || (serverName != null)
+				|| (userName != null) || (password != null) || (port != null)) {
+			MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();
+
+			if (databaseName != null) {
+				ds.setDatabaseName(databaseName);
+			}
+
+			if (userName != null) {
+				ds.setUser(userName);
+			}
+
+			if (password != null) {
+				ds.setPassword(password);
+			}
+
+			if (port != null) {
+				ds.setPortNumber(Integer.parseInt(port));
+			}
+
+			if (serverName != null) {
+				ds.setServerName(serverName);
+			}
+
+			bindDataSource(jndiName, ds);
+
+			ConnectionPoolDataSource boundDs = null;
+
+			try {
+				boundDs = (ConnectionPoolDataSource) lookupDatasourceInJNDI(jndiName);
+
+				assertTrue("Datasource not bound", boundDs != null);
+
+				Connection dsCon = null;
+				Statement dsStmt = null;
+
+				try {
+					dsCon = boundDs.getPooledConnection().getConnection();
+					dsStmt = dsCon.createStatement();
+					dsStmt.executeUpdate("DROP TABLE IF EXISTS testBug3920");
+					dsStmt
+							.executeUpdate("CREATE TABLE testBug3920 (field1 varchar(32))");
+
+					assertTrue(
+							"Connection can not be obtained from data source",
+							dsCon != null);
+				} finally {
+					dsStmt.executeUpdate("DROP TABLE IF EXISTS testBug3920");
+
+					dsStmt.close();
+					dsCon.close();
+				}
+			} finally {
+				if (boundDs != null) {
+					this.ctx.unbind(jndiName);
+				}
+			}
+		}
+	}
+
+	/** 
+	 * Tests fix for BUG#19169 - ConnectionProperties (and thus some
+	 * subclasses) are not serializable, even though some J2EE containers
+	 * expect them to be.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug19169() throws Exception {
+		MysqlDataSource toSerialize = new MysqlDataSource();
+		toSerialize.setZeroDateTimeBehavior("convertToNull");
+		
+		boolean testBooleanFlag = !toSerialize.getAllowLoadLocalInfile();
+		toSerialize.setAllowLoadLocalInfile(testBooleanFlag);
+		
+		int testIntFlag = toSerialize.getBlobSendChunkSize() + 1;
+		toSerialize.setBlobSendChunkSize(String.valueOf(testIntFlag));
+		
+		ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+		ObjectOutputStream objOut = new ObjectOutputStream(bOut);
+		objOut.writeObject(toSerialize);
+		objOut.flush();
+		
+		ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(bOut.toByteArray()));
+		
+		MysqlDataSource thawedDs = (MysqlDataSource)objIn.readObject();
+		
+		assertEquals("convertToNull", thawedDs.getZeroDateTimeBehavior());
+		assertEquals(testBooleanFlag, thawedDs.getAllowLoadLocalInfile());
+		assertEquals(testIntFlag, thawedDs.getBlobSendChunkSize());
+	}
+	
+	/**
+	 * Tests fix for BUG#20242 - MysqlValidConnectionChecker for JBoss doesn't
+	 * work with MySQLXADataSources.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug20242() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			try {
+				Class.forName("org.jboss.resource.adapter.jdbc.ValidConnectionChecker");
+			} catch (Exception ex) {
+				return; // class not available for testing
+			}
+			
+			MysqlXADataSource xaDs = new MysqlXADataSource();
+			xaDs.setUrl(dbUrl);
+			
+			MysqlValidConnectionChecker checker = new MysqlValidConnectionChecker();
+			assertNull(checker.isValidConnection(xaDs.getXAConnection().getConnection()));
+		}	
+	}
+	
+	private void bindDataSource(String name, DataSource ds) throws Exception {
+		this.ctx.bind(this.tempDir.getAbsolutePath() + name, ds);
+	}
+
+	/**
+	 * 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.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	private void createJNDIContext() throws Exception {
+		this.tempDir = File.createTempFile("jnditest", null);
+		this.tempDir.delete();
+		this.tempDir.mkdir();
+		this.tempDir.deleteOnExit();
+
+		MysqlConnectionPoolDataSource ds;
+		Hashtable env = new Hashtable();
+		env.put(Context.INITIAL_CONTEXT_FACTORY,
+				"com.sun.jndi.fscontext.RefFSContextFactory");
+		this.ctx = new InitialContext(env);
+		assertTrue("Naming Context not created", this.ctx != null);
+		ds = new MysqlConnectionPoolDataSource();
+		ds.setUrl(dbUrl); // from BaseTestCase
+		ds.setDatabaseName("test");
+		this.ctx.bind(this.tempDir.getAbsolutePath() + "/test", ds);
+	}
+
+	private DataSource lookupDatasourceInJNDI(String jndiName) throws Exception {
+		NameParser nameParser = this.ctx.getNameParser("");
+		Name datasourceName = nameParser.parse(this.tempDir.getAbsolutePath()
+				+ jndiName);
+		Object obj = this.ctx.lookup(datasourceName);
+		DataSource boundDs = null;
+
+		if (obj instanceof DataSource) {
+			boundDs = (DataSource) obj;
+		} else if (obj instanceof Reference) {
+			//
+			// For some reason, this comes back as a Reference
+			// instance under CruiseControl !?
+			//
+			Reference objAsRef = (Reference) obj;
+			ObjectFactory factory = (ObjectFactory) Class.forName(
+					objAsRef.getFactoryClassName()).newInstance();
+			boundDs = (DataSource) factory.getObjectInstance(objAsRef,
+					datasourceName, this.ctx, new Hashtable());
+		}
+
+		return boundDs;
+	}
+
+	public void testCSC4616() throws Exception {
+		MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();
+		ds.setURL(BaseTestCase.dbUrl);
+		PooledConnection pooledConn = ds.getPooledConnection();
+		Connection physConn = pooledConn.getConnection();
+		Statement physStatement = physConn.createStatement();
+
+		Method enableStreamingResultsMethodStmt = Class.forName(
+				"com.mysql.jdbc.jdbc2.optional.StatementWrapper").getMethod(
+				"enableStreamingResults", new Class[0]);
+		enableStreamingResultsMethodStmt.invoke(physStatement, new Class[0]);
+		this.rs = physStatement.executeQuery("SELECT 1");
+
+		try {
+			physConn.createStatement().executeQuery("SELECT 2");
+			fail("Should have caught a streaming exception here");
+		} catch (SQLException sqlEx) {
+			assertTrue(sqlEx.getMessage() != null
+					&& sqlEx.getMessage().indexOf("Streaming") != -1);
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+
+		PreparedStatement physPrepStmt = physConn.prepareStatement("SELECT 1");
+		Method enableStreamingResultsMethodPstmt = Class.forName(
+				"com.mysql.jdbc.jdbc2.optional.PreparedStatementWrapper")
+				.getMethod("enableStreamingResults", new Class[0]);
+		enableStreamingResultsMethodPstmt.invoke(physPrepStmt, new Class[0]);
+
+		this.rs = physPrepStmt.executeQuery();
+
+		try {
+			physConn.createStatement().executeQuery("SELECT 2");
+			fail("Should have caught a streaming exception here");
+		} catch (SQLException sqlEx) {
+			assertTrue(sqlEx.getMessage() != null
+					&& sqlEx.getMessage().indexOf("Streaming") != -1);
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#16791 - NullPointerException in MysqlDataSourceFactory
+	 * due to Reference containing RefAddrs with null content.
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug16791() throws Exception {
+		MysqlDataSource myDs = new MysqlDataSource();
+		myDs.setUrl(dbUrl);
+		Reference asRef = myDs.getReference();
+		System.out.println(asRef);
+		
+		removeFromRef(asRef, "port");
+		removeFromRef(asRef, NonRegisteringDriver.USER_PROPERTY_KEY);
+		removeFromRef(asRef, NonRegisteringDriver.PASSWORD_PROPERTY_KEY);
+		removeFromRef(asRef, "serverName");
+		removeFromRef(asRef, "databaseName");
+		
+		MysqlDataSource newDs = (MysqlDataSource)new MysqlDataSourceFactory().getObjectInstance(asRef, null, null, null);
+	}
+
+	private void removeFromRef(Reference ref, String key) {
+		int size = ref.size();
+		
+		for (int i = 0; i < size; i++) {
+			RefAddr refAddr = ref.get(i);
+			if (refAddr.getType().equals(key)) {
+				ref.remove(i);
+				break;
+			}
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/EscapeProcessorRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/EscapeProcessorRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/EscapeProcessorRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,87 @@
+/*
+ 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 testsuite.regression;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests regressions w/ the Escape Processor code.
+ * 
+ * @version $Id:$
+ * 
+ */
+public class EscapeProcessorRegressionTest extends BaseTestCase {
+
+	public EscapeProcessorRegressionTest(String name) {
+		super(name);
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		// TODO Auto-generated method stub
+
+	}
+
+	/**
+	 * Tests fix for BUG#11797 - Escape tokenizer doesn't respect stacked single
+	 * quotes for escapes.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11797() throws Exception {
+		assertEquals(
+				"select 'ESCAPED BY ''\\'' ON {tbl_name | * | *.* | db_name.*}'",
+				this.conn
+						.nativeSQL("select 'ESCAPED BY ''\\'' ON {tbl_name | * | *.* | db_name.*}'"));
+	}
+
+	/**
+	 * Tests fix for BUG#11498 - Escape processor didn't honor strings
+	 * demarcated with double quotes.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11498() throws Exception {
+		assertEquals(
+				"replace into t1 (id, f1, f4) VALUES(1,\"\",\"tko { zna gdje se sakrio\"),(2,\"a\",\"sedmi { kontinentio\"),(3,\"a\",\"a } cigov si ti?\")",
+				this.conn
+						.nativeSQL("replace into t1 (id, f1, f4) VALUES(1,\"\",\"tko { zna gdje se sakrio\"),(2,\"a\",\"sedmi { kontinentio\"),(3,\"a\",\"a } cigov si ti?\")"));
+
+	}
+
+	/**
+	 * Tests fix for BUG#14909 - escape processor replaces quote character in
+	 * quoted string with string delimiter.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug14909() throws Exception {
+		assertEquals("select '{\"','}'", this.conn
+				.nativeSQL("select '{\"','}'"));
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/MetaDataRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/MetaDataRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/MetaDataRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1692 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.HashMap;
+import java.util.Properties;
+
+import testsuite.BaseTestCase;
+
+import com.mysql.jdbc.Driver;
+import com.mysql.jdbc.SQLError;
+
+/**
+ * Regression tests for DatabaseMetaData
+ * 
+ * @author Mark Matthews
+ * @version $Id: MetaDataRegressionTest.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class MetaDataRegressionTest extends BaseTestCase {
+	/**
+	 * Creates a new MetaDataRegressionTest.
+	 * 
+	 * @param name
+	 *            the name of the test
+	 */
+	public MetaDataRegressionTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(MetaDataRegressionTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testBug2607() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2607");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug2607 (field1 INT PRIMARY KEY)");
+
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug2607");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			assertTrue(!rsmd.isAutoIncrement(1));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2607");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#2852, where RSMD is not returning correct (or matching)
+	 * types for TINYINT and SMALLINT.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug2852() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2852");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug2852 (field1 TINYINT, field2 SMALLINT)");
+			this.stmt.executeUpdate("INSERT INTO testBug2852 VALUES (1,1)");
+
+			this.rs = this.stmt.executeQuery("SELECT * from testBug2852");
+
+			assertTrue(this.rs.next());
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			assertTrue(rsmd.getColumnClassName(1).equals(
+					this.rs.getObject(1).getClass().getName()));
+			assertTrue("java.lang.Integer".equals(rsmd.getColumnClassName(1)));
+
+			assertTrue(rsmd.getColumnClassName(2).equals(
+					this.rs.getObject(2).getClass().getName()));
+			assertTrue("java.lang.Integer".equals(rsmd.getColumnClassName(2)));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2852");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#2855, where RSMD is not returning correct (or matching)
+	 * types for FLOAT.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug2855() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2855");
+			this.stmt.executeUpdate("CREATE TABLE testBug2855 (field1 FLOAT)");
+			this.stmt.executeUpdate("INSERT INTO testBug2855 VALUES (1)");
+
+			this.rs = this.stmt.executeQuery("SELECT * from testBug2855");
+
+			assertTrue(this.rs.next());
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			assertTrue(rsmd.getColumnClassName(1).equals(
+					this.rs.getObject(1).getClass().getName()));
+			assertTrue("java.lang.Float".equals(rsmd.getColumnClassName(1)));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2855");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#3570 -- inconsistent reporting of column type
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testBug3570() throws Exception {
+		String createTableQuery = " CREATE TABLE testBug3570(field_tinyint TINYINT"
+				+ ",field_smallint SMALLINT"
+				+ ",field_mediumint MEDIUMINT"
+				+ ",field_int INT"
+				+ ",field_integer INTEGER"
+				+ ",field_bigint BIGINT"
+				+ ",field_real REAL"
+				+ ",field_float FLOAT"
+				+ ",field_decimal DECIMAL"
+				+ ",field_numeric NUMERIC"
+				+ ",field_double DOUBLE"
+				+ ",field_char CHAR(3)"
+				+ ",field_varchar VARCHAR(255)"
+				+ ",field_date DATE"
+				+ ",field_time TIME"
+				+ ",field_year YEAR"
+				+ ",field_timestamp TIMESTAMP"
+				+ ",field_datetime DATETIME"
+				+ ",field_tinyblob TINYBLOB"
+				+ ",field_blob BLOB"
+				+ ",field_mediumblob MEDIUMBLOB"
+				+ ",field_longblob LONGBLOB"
+				+ ",field_tinytext TINYTEXT"
+				+ ",field_text TEXT"
+				+ ",field_mediumtext MEDIUMTEXT"
+				+ ",field_longtext LONGTEXT"
+				+ ",field_enum ENUM('1','2','3')"
+				+ ",field_set SET('1','2','3'))";
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3570");
+			this.stmt.executeUpdate(createTableQuery);
+
+			ResultSet dbmdRs = this.conn.getMetaData().getColumns(
+					this.conn.getCatalog(), null, "testBug3570", "%");
+
+			this.rs = this.stmt.executeQuery("SELECT * FROM testBug3570");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			while (dbmdRs.next()) {
+				String columnName = dbmdRs.getString(4);
+				int typeFromGetColumns = dbmdRs.getInt(5);
+				int typeFromRSMD = rsmd.getColumnType(this.rs
+						.findColumn(columnName));
+
+				//
+				// TODO: Server needs to send these types correctly....
+				//
+				if (!"field_tinyblob".equals(columnName)
+						&& !"field_tinytext".equals(columnName)) {
+					assertTrue(columnName + " -> type from DBMD.getColumns("
+							+ typeFromGetColumns
+							+ ") != type from RSMD.getColumnType("
+							+ typeFromRSMD + ")",
+							typeFromGetColumns == typeFromRSMD);
+				}
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3570");
+		}
+	}
+
+	/**
+	 * Tests char/varchar bug
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testCharVarchar() throws Exception {
+		try {
+			this.stmt.execute("DROP TABLE IF EXISTS charVarCharTest");
+			this.stmt.execute("CREATE TABLE charVarCharTest ("
+					+ "  TableName VARCHAR(64)," + "  FieldName VARCHAR(64),"
+					+ "  NextCounter INTEGER);");
+
+			String query = "SELECT TableName, FieldName, NextCounter FROM charVarCharTest";
+			this.rs = this.stmt.executeQuery(query);
+
+			ResultSetMetaData rsmeta = this.rs.getMetaData();
+
+			assertTrue(rsmeta.getColumnTypeName(1).equalsIgnoreCase("VARCHAR"));
+
+			// is "CHAR", expected "VARCHAR"
+			assertTrue(rsmeta.getColumnType(1) == 12);
+
+			// is 1 (java.sql.Types.CHAR), expected 12 (java.sql.Types.VARCHAR)
+		} finally {
+			this.stmt.execute("DROP TABLE IF EXISTS charVarCharTest");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#1673, where DatabaseMetaData.getColumns() is not
+	 * returning correct column ordinal info for non '%' column name patterns.
+	 * 
+	 * @throws Exception
+	 *             if the test fails for any reason
+	 */
+	public void testFixForBug1673() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1673");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug1673 (field_1 INT, field_2 INT)");
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+
+			int ordinalPosOfCol2Full = 0;
+
+			this.rs = dbmd.getColumns(this.conn.getCatalog(), null,
+					"testBug1673", null);
+
+			while (this.rs.next()) {
+				if (this.rs.getString(4).equals("field_2")) {
+					ordinalPosOfCol2Full = this.rs.getInt(17);
+				}
+			}
+
+			int ordinalPosOfCol2Scoped = 0;
+
+			this.rs = dbmd.getColumns(this.conn.getCatalog(), null,
+					"testBug1673", "field_2");
+
+			while (this.rs.next()) {
+				if (this.rs.getString(4).equals("field_2")) {
+					ordinalPosOfCol2Scoped = this.rs.getInt(17);
+				}
+			}
+
+			assertTrue("Ordinal position in full column list of '"
+					+ ordinalPosOfCol2Full
+					+ "' != ordinal position in pattern search, '"
+					+ ordinalPosOfCol2Scoped + "'.",
+					(ordinalPosOfCol2Full != 0)
+							&& (ordinalPosOfCol2Scoped != 0)
+							&& (ordinalPosOfCol2Scoped == ordinalPosOfCol2Full));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1673");
+		}
+	}
+
+	/**
+	 * Tests bug reported by OpenOffice team with getColumns and LONGBLOB
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testGetColumns() throws Exception {
+		try {
+			this.stmt
+					.execute("CREATE TABLE IF NOT EXISTS longblob_regress(field_1 longblob)");
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+			ResultSet dbmdRs = null;
+
+			try {
+				dbmdRs = dbmd.getColumns("", "", "longblob_regress", "%");
+
+				while (dbmdRs.next()) {
+					dbmdRs.getInt(7);
+				}
+			} finally {
+				if (dbmdRs != null) {
+					try {
+						dbmdRs.close();
+					} catch (SQLException ex) {
+						;
+					}
+				}
+			}
+		} finally {
+			this.stmt.execute("DROP TABLE IF EXISTS longblob_regress");
+		}
+	}
+
+	/**
+	 * Tests fix for Bug#
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testGetColumnsBug1099() throws Exception {
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testGetColumnsBug1099");
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+
+			this.rs = dbmd.getTypeInfo();
+
+			StringBuffer types = new StringBuffer();
+
+			HashMap alreadyDoneTypes = new HashMap();
+
+			while (this.rs.next()) {
+				String typeName = this.rs.getString("TYPE_NAME");
+				String createParams = this.rs.getString("CREATE_PARAMS");
+
+				if ((typeName.indexOf("BINARY") == -1)
+						&& !typeName.equals("LONG VARCHAR")) {
+					if (!alreadyDoneTypes.containsKey(typeName)) {
+						alreadyDoneTypes.put(typeName, null);
+
+						if (types.length() != 0) {
+							types.append(", \n");
+						}
+
+						int typeNameLength = typeName.length();
+						StringBuffer safeTypeName = new StringBuffer(
+								typeNameLength);
+
+						for (int i = 0; i < typeNameLength; i++) {
+							char c = typeName.charAt(i);
+
+							if (Character.isWhitespace(c)) {
+								safeTypeName.append("_");
+							} else {
+								safeTypeName.append(c);
+							}
+						}
+
+						types.append(safeTypeName.toString());
+						types.append("Column ");
+						types.append(typeName);
+
+						if (typeName.indexOf("CHAR") != -1) {
+							types.append(" (1)");
+						} else if (typeName.equalsIgnoreCase("enum")
+								|| typeName.equalsIgnoreCase("set")) {
+							types.append("('a', 'b', 'c')");
+						}
+					}
+				}
+			}
+
+			this.stmt.executeUpdate("CREATE TABLE testGetColumnsBug1099("
+					+ types.toString() + ")");
+
+			dbmd.getColumns(null, this.conn.getCatalog(),
+					"testGetColumnsBug1099", "%");
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testGetColumnsBug1099");
+		}
+	}
+
+	/**
+	 * Tests whether or not unsigned columns are reported correctly in
+	 * DBMD.getColumns
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetColumnsUnsigned() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testGetUnsignedCols");
+			this.stmt
+					.executeUpdate("CREATE TABLE testGetUnsignedCols (field1 BIGINT, field2 BIGINT UNSIGNED)");
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+
+			this.rs = dbmd.getColumns(this.conn.getCatalog(), null,
+					"testGetUnsignedCols", "%");
+
+			assertTrue(this.rs.next());
+			// This row doesn't have 'unsigned' attribute
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getString(6).toLowerCase().indexOf("unsigned") != -1);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testGetUnsignedCols");
+		}
+	}
+
+	/**
+	 * Tests whether bogus parameters break Driver.getPropertyInfo().
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testGetPropertyInfo() throws Exception {
+		new Driver().getPropertyInfo("", null);
+	}
+
+	/**
+	 * Tests whether ResultSetMetaData returns correct info for CHAR/VARCHAR
+	 * columns.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testIsCaseSensitive() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testIsCaseSensitive");
+			this.stmt
+					.executeUpdate("CREATE TABLE testIsCaseSensitive (bin_char CHAR(1) BINARY, bin_varchar VARCHAR(64) BINARY, ci_char CHAR(1), ci_varchar VARCHAR(64))");
+			this.rs = this.stmt
+					.executeQuery("SELECT bin_char, bin_varchar, ci_char, ci_varchar FROM testIsCaseSensitive");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+			assertTrue(rsmd.isCaseSensitive(1));
+			assertTrue(rsmd.isCaseSensitive(2));
+			assertTrue(!rsmd.isCaseSensitive(3));
+			assertTrue(!rsmd.isCaseSensitive(4));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testIsCaseSensitive");
+		}
+
+		if (versionMeetsMinimum(4, 1)) {
+			try {
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testIsCaseSensitiveCs");
+				this.stmt
+						.executeUpdate("CREATE TABLE testIsCaseSensitiveCs ("
+								+ "bin_char CHAR(1) CHARACTER SET latin1 COLLATE latin1_general_cs,"
+								+ "bin_varchar VARCHAR(64) CHARACTER SET latin1 COLLATE latin1_general_cs,"
+								+ "ci_char CHAR(1) CHARACTER SET latin1 COLLATE latin1_general_ci,"
+								+ "ci_varchar VARCHAR(64) CHARACTER SET latin1 COLLATE latin1_general_ci, "
+								+ "bin_tinytext TINYTEXT CHARACTER SET latin1 COLLATE latin1_general_cs,"
+								+ "bin_text TEXT CHARACTER SET latin1 COLLATE latin1_general_cs,"
+								+ "bin_med_text MEDIUMTEXT CHARACTER SET latin1 COLLATE latin1_general_cs,"
+								+ "bin_long_text LONGTEXT CHARACTER SET latin1 COLLATE latin1_general_cs,"
+								+ "ci_tinytext TINYTEXT CHARACTER SET latin1 COLLATE latin1_general_ci,"
+								+ "ci_text TEXT CHARACTER SET latin1 COLLATE latin1_general_ci,"
+								+ "ci_med_text MEDIUMTEXT CHARACTER SET latin1 COLLATE latin1_general_ci,"
+								+ "ci_long_text LONGTEXT CHARACTER SET latin1 COLLATE latin1_general_ci)");
+
+				this.rs = this.stmt
+						.executeQuery("SELECT bin_char, bin_varchar, ci_char, ci_varchar, bin_tinytext, bin_text, bin_med_text, bin_long_text, ci_tinytext, ci_text, ci_med_text, ci_long_text FROM testIsCaseSensitiveCs");
+
+				ResultSetMetaData rsmd = this.rs.getMetaData();
+				assertTrue(rsmd.isCaseSensitive(1));
+				assertTrue(rsmd.isCaseSensitive(2));
+				assertTrue(!rsmd.isCaseSensitive(3));
+				assertTrue(!rsmd.isCaseSensitive(4));
+
+				assertTrue(rsmd.isCaseSensitive(5));
+				assertTrue(rsmd.isCaseSensitive(6));
+				assertTrue(rsmd.isCaseSensitive(7));
+				assertTrue(rsmd.isCaseSensitive(8));
+
+				assertTrue(!rsmd.isCaseSensitive(9));
+				assertTrue(!rsmd.isCaseSensitive(10));
+				assertTrue(!rsmd.isCaseSensitive(11));
+				assertTrue(!rsmd.isCaseSensitive(12));
+			} finally {
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testIsCaseSensitiveCs");
+			}
+		}
+	}
+
+	/**
+	 * Tests whether or not DatabaseMetaData.getColumns() returns the correct
+	 * java.sql.Types info.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testLongText() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testLongText");
+			this.stmt
+					.executeUpdate("CREATE TABLE testLongText (field1 LONGTEXT)");
+
+			this.rs = this.conn.getMetaData().getColumns(
+					this.conn.getCatalog(), null, "testLongText", "%");
+
+			assertTrue(this.rs.next());
+
+			assertTrue(this.rs.getInt("DATA_TYPE") == java.sql.Types.LONGVARCHAR);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testLongText");
+		}
+	}
+
+	/**
+	 * Tests for types being returned correctly
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testTypes() throws Exception {
+		try {
+			this.stmt.execute("DROP TABLE IF EXISTS typesRegressTest");
+			this.stmt.execute("CREATE TABLE typesRegressTest ("
+					+ "varcharField VARCHAR(32)," + "charField CHAR(2),"
+					+ "enumField ENUM('1','2'),"
+					+ "setField  SET('1','2','3')," + "tinyblobField TINYBLOB,"
+					+ "mediumBlobField MEDIUMBLOB," + "longblobField LONGBLOB,"
+					+ "blobField BLOB)");
+
+			this.rs = this.stmt.executeQuery("SELECT * from typesRegressTest");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			int numCols = rsmd.getColumnCount();
+
+			for (int i = 0; i < numCols; i++) {
+				String columnName = rsmd.getColumnName(i + 1);
+				String columnTypeName = rsmd.getColumnTypeName(i + 1);
+				System.out.println(columnName + " -> " + columnTypeName);
+			}
+		} finally {
+			this.stmt.execute("DROP TABLE IF EXISTS typesRegressTest");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#4742, 'DOUBLE' mapped twice in getTypeInfo().
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug4742() throws Exception {
+		HashMap clashMap = new HashMap();
+
+		this.rs = this.conn.getMetaData().getTypeInfo();
+
+		while (this.rs.next()) {
+			String name = this.rs.getString(1);
+			assertTrue("Type represented twice in type info, '" + name + "'.",
+					!clashMap.containsKey(name));
+			clashMap.put(name, name);
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#4138, getColumns() returns incorrect JDBC type for
+	 * unsigned columns.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug4138() throws Exception {
+		try {
+			String[] typesToTest = new String[] { "TINYINT", "SMALLINT",
+					"MEDIUMINT", "INTEGER", "BIGINT", "FLOAT", "DOUBLE",
+					"DECIMAL" };
+
+			short[] jdbcMapping = new short[] { Types.TINYINT, Types.SMALLINT,
+					Types.INTEGER, Types.INTEGER, Types.BIGINT, Types.REAL,
+					Types.DOUBLE, Types.DECIMAL };
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4138");
+
+			StringBuffer createBuf = new StringBuffer();
+
+			createBuf.append("CREATE TABLE testBug4138 (");
+
+			boolean firstColumn = true;
+
+			for (int i = 0; i < typesToTest.length; i++) {
+				if (!firstColumn) {
+					createBuf.append(", ");
+				} else {
+					firstColumn = false;
+				}
+
+				createBuf.append("field");
+				createBuf.append((i + 1));
+				createBuf.append(" ");
+				createBuf.append(typesToTest[i]);
+				createBuf.append(" UNSIGNED");
+			}
+			createBuf.append(")");
+			this.stmt.executeUpdate(createBuf.toString());
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+			this.rs = dbmd.getColumns(this.conn.getCatalog(), null,
+					"testBug4138", "field%");
+
+			assertTrue(this.rs.next());
+
+			for (int i = 0; i < typesToTest.length; i++) {
+				assertTrue(
+						"JDBC Data Type of "
+								+ this.rs.getShort("DATA_TYPE")
+								+ " for MySQL type '"
+								+ this.rs.getString("TYPE_NAME")
+								+ "' from 'DATA_TYPE' column does not match expected value of "
+								+ jdbcMapping[i] + ".",
+						jdbcMapping[i] == this.rs.getShort("DATA_TYPE"));
+				this.rs.next();
+			}
+
+			this.rs.close();
+
+			StringBuffer queryBuf = new StringBuffer("SELECT ");
+			firstColumn = true;
+
+			for (int i = 0; i < typesToTest.length; i++) {
+				if (!firstColumn) {
+					queryBuf.append(", ");
+				} else {
+					firstColumn = false;
+				}
+
+				queryBuf.append("field");
+				queryBuf.append((i + 1));
+			}
+
+			queryBuf.append(" FROM testBug4138");
+
+			this.rs = this.stmt.executeQuery(queryBuf.toString());
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			for (int i = 0; i < typesToTest.length; i++) {
+
+				assertTrue(jdbcMapping[i] == rsmd.getColumnType(i + 1));
+				String desiredTypeName = typesToTest[i] + " unsigned";
+
+				assertTrue(rsmd.getColumnTypeName((i + 1)) + " != "
+						+ desiredTypeName, desiredTypeName
+						.equalsIgnoreCase(rsmd.getColumnTypeName(i + 1)));
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4138");
+		}
+	}
+
+	/**
+	 * Here for housekeeping only, the test is actually in testBug4138().
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug4860() throws Exception {
+		testBug4138();
+	}
+
+	/**
+	 * Tests fix for BUG#4880 - RSMD.getPrecision() returns '0' for non-numeric
+	 * types.
+	 * 
+	 * Why-oh-why is this not in the spec, nor the api-docs, but in some
+	 * 'optional' book, _and_ it is a variance from both ODBC and the ANSI SQL
+	 * standard :p
+	 * 
+	 * (from the CTS testsuite)....
+	 * 
+	 * The getPrecision(int colindex) method returns an integer value
+	 * representing the number of decimal digits for number types,maximum length
+	 * in characters for character types,maximum length in bytes for JDBC binary
+	 * datatypes.
+	 * 
+	 * (See Section 27.3 of JDBC 2.0 API Reference & Tutorial 2nd edition)
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+
+	public void testBug4880() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4880");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug4880 (field1 VARCHAR(80), field2 TINYBLOB, field3 BLOB, field4 MEDIUMBLOB, field5 LONGBLOB)");
+			this.rs = this.stmt
+					.executeQuery("SELECT field1, field2, field3, field4, field5 FROM testBug4880");
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			assertEquals(80, rsmd.getPrecision(1));
+			assertEquals(Types.VARCHAR, rsmd.getColumnType(1));
+			assertEquals(80, rsmd.getColumnDisplaySize(1));
+
+			assertEquals(255, rsmd.getPrecision(2));
+			assertEquals(Types.VARBINARY, rsmd.getColumnType(2));
+			assertTrue("TINYBLOB".equalsIgnoreCase(rsmd.getColumnTypeName(2)));
+			assertEquals(255, rsmd.getColumnDisplaySize(2));
+
+			assertEquals(65535, rsmd.getPrecision(3));
+			assertEquals(Types.LONGVARBINARY, rsmd.getColumnType(3));
+			assertTrue("BLOB".equalsIgnoreCase(rsmd.getColumnTypeName(3)));
+			assertEquals(65535, rsmd.getColumnDisplaySize(3));
+
+			assertEquals(16777215, rsmd.getPrecision(4));
+			assertEquals(Types.LONGVARBINARY, rsmd.getColumnType(4));
+			assertTrue("MEDIUMBLOB".equalsIgnoreCase(rsmd.getColumnTypeName(4)));
+			assertEquals(16777215, rsmd.getColumnDisplaySize(4));
+
+			if (versionMeetsMinimum(4, 1)) {
+				// Server doesn't send us enough information to detect LONGBLOB
+				// type
+				assertEquals(Integer.MAX_VALUE, rsmd.getPrecision(5));
+				assertEquals(Types.LONGVARBINARY, rsmd.getColumnType(5));
+				assertTrue("LONGBLOB".equalsIgnoreCase(rsmd
+						.getColumnTypeName(5)));
+				assertEquals(Integer.MAX_VALUE, rsmd.getColumnDisplaySize(5));
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4880");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#6399, ResultSetMetaData.getDisplaySize() is wrong for
+	 * multi-byte charsets.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug6399() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6399");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBug6399 (field1 CHAR(3) CHARACTER SET UTF8, field2 CHAR(3) CHARACTER SET LATIN1, field3 CHAR(3) CHARACTER SET SJIS)");
+				this.stmt
+						.executeUpdate("INSERT INTO testBug6399 VALUES ('a', 'a', 'a')");
+
+				this.rs = this.stmt
+						.executeQuery("SELECT field1, field2, field3 FROM testBug6399");
+				ResultSetMetaData rsmd = this.rs.getMetaData();
+
+				assertTrue(3 == rsmd.getColumnDisplaySize(1));
+				assertTrue(3 == rsmd.getColumnDisplaySize(2));
+				assertTrue(3 == rsmd.getColumnDisplaySize(3));
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6399");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#7081, DatabaseMetaData.getIndexInfo() ignoring 'unique'
+	 * parameters.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug7081() throws Exception {
+		String tableName = "testBug7081";
+
+		try {
+			createTable(tableName, "(field1 INT, INDEX(field1))");
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+			this.rs = dbmd.getIndexInfo(this.conn.getCatalog(), null,
+					tableName, true, false);
+			assertTrue(!this.rs.next()); // there should be no rows that meet
+			// this requirement
+
+			this.rs = dbmd.getIndexInfo(this.conn.getCatalog(), null,
+					tableName, false, false);
+			assertTrue(this.rs.next()); // there should be one row that meets
+			// this requirement
+			assertTrue(!this.rs.next());
+
+		} finally {
+			dropTable(tableName);
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#7033 - PreparedStatements don't encode Big5 (and other
+	 * multibyte) character sets correctly in static SQL strings.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug7033() throws Exception {
+		if (false) { // disabled for now
+			Connection big5Conn = null;
+			Statement big5Stmt = null;
+			PreparedStatement big5PrepStmt = null;
+
+			String testString = "\u5957 \u9910";
+
+			try {
+				Properties props = new Properties();
+				props.setProperty("useUnicode", "true");
+				props.setProperty("characterEncoding", "Big5");
+
+				big5Conn = getConnectionWithProps(props);
+				big5Stmt = big5Conn.createStatement();
+
+				byte[] foobar = testString.getBytes("Big5");
+				System.out.println(foobar);
+
+				this.rs = big5Stmt.executeQuery("select 1 as '\u5957 \u9910'");
+				String retrString = this.rs.getMetaData().getColumnName(1);
+				assertTrue(testString.equals(retrString));
+
+				big5PrepStmt = big5Conn
+						.prepareStatement("select 1 as '\u5957 \u9910'");
+				this.rs = big5PrepStmt.executeQuery();
+				retrString = this.rs.getMetaData().getColumnName(1);
+				assertTrue(testString.equals(retrString));
+			} finally {
+				if (this.rs != null) {
+					this.rs.close();
+					this.rs = null;
+				}
+
+				if (big5Stmt != null) {
+					big5Stmt.close();
+
+				}
+
+				if (big5PrepStmt != null) {
+					big5PrepStmt.close();
+				}
+
+				if (big5Conn != null) {
+					big5Conn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for Bug#8812, DBMD.getIndexInfo() returning inverted values for
+	 * 'NON_UNIQUE' column.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug8812() throws Exception {
+		String tableName = "testBug8812";
+
+		try {
+			createTable(tableName,
+					"(field1 INT, field2 INT, INDEX(field1), UNIQUE INDEX(field2))");
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+			this.rs = dbmd.getIndexInfo(this.conn.getCatalog(), null,
+					tableName, true, false);
+			assertTrue(this.rs.next()); // there should be one row that meets
+			// this requirement
+			assertEquals(this.rs.getBoolean("NON_UNIQUE"), false);
+
+			this.rs = dbmd.getIndexInfo(this.conn.getCatalog(), null,
+					tableName, false, false);
+			assertTrue(this.rs.next()); // there should be two rows that meets
+			// this requirement
+			assertEquals(this.rs.getBoolean("NON_UNIQUE"), false);
+			assertTrue(this.rs.next());
+			assertEquals(this.rs.getBoolean("NON_UNIQUE"), true);
+
+		} finally {
+			dropTable(tableName);
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#8800 - supportsMixedCase*Identifiers() returns wrong
+	 * value on servers running on case-sensitive filesystems.
+	 */
+
+	public void testBug8800() throws Exception {
+		assertEquals(((com.mysql.jdbc.Connection) this.conn)
+				.lowerCaseTableNames(), !this.conn.getMetaData()
+				.supportsMixedCaseIdentifiers());
+		assertEquals(((com.mysql.jdbc.Connection) this.conn)
+				.lowerCaseTableNames(), !this.conn.getMetaData()
+				.supportsMixedCaseQuotedIdentifiers());
+
+	}
+
+	/**
+	 * Tests fix for BUG#8792 - DBMD.supportsResultSetConcurrency() not
+	 * returning true for forward-only/read-only result sets (we obviously
+	 * support this).
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug8792() throws Exception {
+		DatabaseMetaData dbmd = this.conn.getMetaData();
+
+		assertTrue(dbmd.supportsResultSetConcurrency(
+				ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY));
+
+		assertTrue(dbmd.supportsResultSetConcurrency(
+				ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE));
+
+		assertTrue(dbmd.supportsResultSetConcurrency(
+				ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY));
+
+		assertTrue(dbmd.supportsResultSetConcurrency(
+				ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE));
+
+		assertTrue(!dbmd.supportsResultSetConcurrency(
+				ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY));
+
+		assertTrue(!dbmd.supportsResultSetConcurrency(
+				ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE));
+
+		// Check error conditions
+		try {
+			dbmd.supportsResultSetConcurrency(ResultSet.TYPE_FORWARD_ONLY,
+					Integer.MIN_VALUE);
+			fail("Exception should've been raised for bogus concurrency value");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+					.getSQLState()));
+		}
+
+		try {
+			assertTrue(dbmd.supportsResultSetConcurrency(
+					ResultSet.TYPE_SCROLL_INSENSITIVE, Integer.MIN_VALUE));
+			fail("Exception should've been raised for bogus concurrency value");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+					.getSQLState()));
+		}
+
+		try {
+			assertTrue(dbmd.supportsResultSetConcurrency(Integer.MIN_VALUE,
+					Integer.MIN_VALUE));
+			fail("Exception should've been raised for bogus concurrency value");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+					.getSQLState()));
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#8803, 'DATA_TYPE' column from
+	 * DBMD.getBestRowIdentifier() causes ArrayIndexOutOfBoundsException when
+	 * accessed (and in fact, didn't return any value).
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug8803() throws Exception {
+		String tableName = "testBug8803";
+		createTable(tableName, "(field1 INT NOT NULL PRIMARY KEY)");
+		DatabaseMetaData metadata = this.conn.getMetaData();
+		try {
+			this.rs = metadata.getBestRowIdentifier(this.conn.getCatalog(),
+					null, tableName, DatabaseMetaData.bestRowNotPseudo, true);
+
+			assertTrue(this.rs.next());
+
+			this.rs.getInt("DATA_TYPE"); // **** Fails here *****
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+
+				this.rs = null;
+			}
+		}
+
+	}
+
+	/**
+	 * Tests fix for BUG#9320 - PreparedStatement.getMetaData() inserts blank
+	 * row in database under certain conditions when not using server-side
+	 * prepared statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug9320() throws Exception {
+		createTable("testBug9320", "(field1 int)");
+
+		testAbsenceOfMetadataForQuery("INSERT INTO testBug9320 VALUES (?)");
+		testAbsenceOfMetadataForQuery("UPDATE testBug9320 SET field1=?");
+		testAbsenceOfMetadataForQuery("DELETE FROM testBug9320 WHERE field1=?");
+	}
+
+	/**
+	 * Tests fix for BUG#9778, DBMD.getTables() shouldn't return tables if views
+	 * are asked for, even if the database version doesn't support views.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug9778() throws Exception {
+		String tableName = "testBug9778";
+
+		try {
+			createTable(tableName, "(field1 int)");
+			this.rs = this.conn.getMetaData().getTables(null, null, tableName,
+					new String[] { "VIEW" });
+			assertEquals(false, this.rs.next());
+
+			this.rs = this.conn.getMetaData().getTables(null, null, tableName,
+					new String[] { "TABLE" });
+			assertEquals(true, this.rs.next());
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#9769 - Should accept null for procedureNamePattern,
+	 * even though it isn't JDBC compliant, for legacy's sake.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug9769() throws Exception {
+		boolean defaultPatternConfig = ((com.mysql.jdbc.Connection) this.conn)
+				.getNullNamePatternMatchesAll();
+
+		// We're going to change this in 3.2.x, so make that test here, so we
+		// catch it.
+
+		if (this.conn.getMetaData().getDriverMajorVersion() == 3
+				&& this.conn.getMetaData().getDriverMinorVersion() >= 2) {
+			assertEquals(false, defaultPatternConfig);
+		} else {
+			assertEquals(true, defaultPatternConfig);
+		}
+
+		try {
+			this.conn.getMetaData().getProcedures(this.conn.getCatalog(), "%",
+					null);
+
+			if (!defaultPatternConfig) {
+				// we shouldn't have gotten here
+				fail("Exception should've been thrown");
+			}
+		} catch (SQLException sqlEx) {
+			if (!defaultPatternConfig) {
+				assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx
+						.getSQLState());
+			} else {
+				throw sqlEx; // we shouldn't have gotten an exception here
+			}
+		}
+
+		// FIXME: TO test for 3.1.9
+		// getColumns();
+		// getTablePrivileges();
+		// getTables();
+
+	}
+
+	/**
+	 * Tests fix for BUG#9917 - Should accept null for catalog in DBMD methods,
+	 * even though it's not JDBC-compliant for legacy's sake.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug9917() throws Exception {
+		String tableName = "testBug9917";
+		boolean defaultCatalogConfig = ((com.mysql.jdbc.Connection) this.conn)
+				.getNullCatalogMeansCurrent();
+
+		// We're going to change this in 3.2.x, so make that test here, so we
+		// catch it.
+
+		if (this.conn.getMetaData().getDriverMajorVersion() == 3
+				&& this.conn.getMetaData().getDriverMinorVersion() >= 2) {
+			assertEquals(false, defaultCatalogConfig);
+		} else {
+			assertEquals(true, defaultCatalogConfig);
+		}
+
+		try {
+			createTable(tableName, "(field1 int)");
+			String currentCatalog = this.conn.getCatalog();
+
+			try {
+				this.rs = this.conn.getMetaData().getTables(null, null,
+						tableName, new String[] { "TABLE" });
+
+				if (!defaultCatalogConfig) {
+					// we shouldn't have gotten here
+					fail("Exception should've been thrown");
+				}
+
+				assertEquals(true, this.rs.next());
+				assertEquals(currentCatalog, this.rs.getString("TABLE_CAT"));
+
+				// FIXME: Methods to test for 3.1.9
+				//
+				// getBestRowIdentifier()
+				// getColumns()
+				// getCrossReference()
+				// getExportedKeys()
+				// getImportedKeys()
+				// getIndexInfo()
+				// getPrimaryKeys()
+				// getProcedures()
+
+			} catch (SQLException sqlEx) {
+				if (!defaultCatalogConfig) {
+					assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx
+							.getSQLState());
+				} else {
+					throw sqlEx; // we shouldn't have gotten an exception
+					// here
+				}
+			}
+
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11575 -- DBMD.storesLower/Mixed/UpperIdentifiers()
+	 * reports incorrect values for servers deployed on Windows.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11575() throws Exception {
+		DatabaseMetaData dbmd = this.conn.getMetaData();
+
+		if (isServerRunningOnWindows()) {
+			assertEquals(true, dbmd.storesLowerCaseIdentifiers());
+			assertEquals(true, dbmd.storesLowerCaseQuotedIdentifiers());
+			assertEquals(false, dbmd.storesMixedCaseIdentifiers());
+			assertEquals(false, dbmd.storesMixedCaseQuotedIdentifiers());
+			assertEquals(false, dbmd.storesUpperCaseIdentifiers());
+			assertEquals(true, dbmd.storesUpperCaseQuotedIdentifiers());
+		} else {
+			assertEquals(false, dbmd.storesLowerCaseIdentifiers());
+			assertEquals(false, dbmd.storesLowerCaseQuotedIdentifiers());
+			assertEquals(true, dbmd.storesMixedCaseIdentifiers());
+			assertEquals(true, dbmd.storesMixedCaseQuotedIdentifiers());
+			assertEquals(false, dbmd.storesUpperCaseIdentifiers());
+			assertEquals(true, dbmd.storesUpperCaseQuotedIdentifiers());
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11781, foreign key information that is quoted is parsed
+	 * incorrectly.
+	 */
+	public void testBug11781() throws Exception {
+
+		if (versionMeetsMinimum(5, 1)) {
+			if (!versionMeetsMinimum(5, 1, 12)) {
+				// server bug prevents this test from functioning
+				
+				return;
+			}
+		}
+		
+		createTable(
+				"`app tab`",
+				"( C1 int(11) NULL, INDEX NEWINX (C1), INDEX NEWINX2 (C1)) ENGINE = InnoDB CHECKSUM = 0 COMMENT = 'InnoDB free: 3072 kB; (`C1`) REFER`test/app tab`(`C1`)' PACK_KEYS = 0");
+
+		this.stmt
+				.executeUpdate("ALTER TABLE `app tab` ADD CONSTRAINT APPFK FOREIGN KEY (C1) REFERENCES `app tab` (C1)");
+
+		/*
+		 * this.rs = this.conn.getMetaData().getCrossReference(
+		 * this.conn.getCatalog(), null, "app tab", this.conn.getCatalog(),
+		 * null, "app tab");
+		 */
+		this.rs = ((com.mysql.jdbc.DatabaseMetaData) this.conn.getMetaData())
+				.extractForeignKeyFromCreateTable(this.conn.getCatalog(),
+						"app tab");
+		assertTrue("must return a row", this.rs.next());
+
+		String catalog = this.conn.getCatalog();
+
+		assertEquals("comment; APPFK(`C1`) REFER `" + catalog
+				+ "`/ `app tab` (`C1`)", this.rs.getString(3));
+
+		this.rs.close();
+
+		this.rs = this.conn.getMetaData().getImportedKeys(
+				this.conn.getCatalog(), null, "app tab");
+
+		assertTrue(this.rs.next());
+
+		this.rs = this.conn.getMetaData().getExportedKeys(
+				this.conn.getCatalog(), null, "app tab");
+
+		assertTrue(this.rs.next());
+	}
+
+	/**
+	 * Tests fix for BUG#12970 - java.sql.Types.OTHER returned for binary and
+	 * varbinary columns.
+	 * 
+	 */
+	public void testBug12970() throws Exception {
+		if (versionMeetsMinimum(5, 0, 8)) {
+			String tableName = "testBug12970";
+
+			createTable(tableName,
+					"(binary_field BINARY(32), varbinary_field VARBINARY(64))");
+
+			try {
+				this.rs = this.conn.getMetaData().getColumns(
+						this.conn.getCatalog(), null, tableName, "%");
+				assertTrue(this.rs.next());
+				assertEquals(Types.BINARY, this.rs.getInt("DATA_TYPE"));
+				assertEquals(32, this.rs.getInt("COLUMN_SIZE"));
+				assertTrue(this.rs.next());
+				assertEquals(Types.VARBINARY, this.rs.getInt("DATA_TYPE"));
+				assertEquals(64, this.rs.getInt("COLUMN_SIZE"));
+				this.rs.close();
+
+				this.rs = this.stmt
+						.executeQuery("SELECT binary_field, varbinary_field FROM "
+								+ tableName);
+				ResultSetMetaData rsmd = this.rs.getMetaData();
+				assertEquals(Types.BINARY, rsmd.getColumnType(1));
+				assertEquals(32, rsmd.getPrecision(1));
+				assertEquals(Types.VARBINARY, rsmd.getColumnType(2));
+				assertEquals(64, rsmd.getPrecision(2));
+				this.rs.close();
+			} finally {
+				if (this.rs != null) {
+					this.rs.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#12975 - OpenOffice expects DBMD.supportsIEF() 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.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug12975() throws Exception {
+		assertEquals(false, this.conn.getMetaData()
+				.supportsIntegrityEnhancementFacility());
+
+		Connection overrideConn = null;
+
+		try {
+			Properties props = new Properties();
+
+			props.setProperty("overrideSupportsIntegrityEnhancementFacility",
+					"true");
+
+			overrideConn = getConnectionWithProps(props);
+			assertEquals(true, overrideConn.getMetaData()
+					.supportsIntegrityEnhancementFacility());
+		} finally {
+			if (overrideConn != null) {
+				overrideConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#13277 - RSMD for generated keys has NPEs when a
+	 * connection is referenced.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug13277() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		createTable("testBug13277",
+				"(field1 INT NOT NULL PRIMARY KEY AUTO_INCREMENT, field2 VARCHAR(32))");
+
+		try {
+			this.stmt.executeUpdate(
+					"INSERT INTO testBug13277 (field2) VALUES ('abcdefg')",
+					Statement.RETURN_GENERATED_KEYS);
+
+			this.rs = this.stmt.getGeneratedKeys();
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+			checkRsmdForBug13277(rsmd);
+			this.rs.close();
+
+			for (int i = 0; i < 5; i++) {
+				this.stmt
+						.addBatch("INSERT INTO testBug13277 (field2) VALUES ('abcdefg')");
+			}
+
+			this.stmt.executeBatch();
+
+			this.rs = this.stmt.getGeneratedKeys();
+
+			rsmd = this.rs.getMetaData();
+			checkRsmdForBug13277(rsmd);
+			this.rs.close();
+
+			this.pstmt = this.conn.prepareStatement(
+					"INSERT INTO testBug13277 (field2) VALUES ('abcdefg')",
+					Statement.RETURN_GENERATED_KEYS);
+			this.pstmt.executeUpdate();
+
+			this.rs = this.pstmt.getGeneratedKeys();
+
+			rsmd = this.rs.getMetaData();
+			checkRsmdForBug13277(rsmd);
+			this.rs.close();
+
+			this.pstmt.addBatch();
+			this.pstmt.addBatch();
+
+			this.pstmt.executeUpdate();
+
+			this.rs = this.pstmt.getGeneratedKeys();
+
+			rsmd = this.rs.getMetaData();
+			checkRsmdForBug13277(rsmd);
+			this.rs.close();
+
+		} finally {
+			if (this.pstmt != null) {
+				this.pstmt.close();
+				this.pstmt = null;
+			}
+
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests BUG13601 (which doesn't seem to be present in 3.1.11, but we'll
+	 * leave it in here for regression's-sake).
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug13601() throws Exception {
+
+		if (versionMeetsMinimum(5, 0)) {
+			createTable("testBug13601",
+					"(field1 BIGINT NOT NULL, field2 BIT default 0 NOT NULL) ENGINE=MyISAM");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field1, field2 FROM testBug13601 WHERE 1=-1");
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+			assertEquals(Types.BIT, rsmd.getColumnType(2));
+			assertEquals(Boolean.class.getName(), rsmd.getColumnClassName(2));
+
+			this.rs = this.conn.prepareStatement(
+					"SELECT field1, field2 FROM testBug13601 WHERE 1=-1")
+					.executeQuery();
+			rsmd = this.rs.getMetaData();
+			assertEquals(Types.BIT, rsmd.getColumnType(2));
+			assertEquals(Boolean.class.getName(), rsmd.getColumnClassName(2));
+
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#14815 - DBMD.getColumns() doesn't return TABLE_NAME
+	 * correctly.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug14815() throws Exception {
+		try {
+			createTable("testBug14815_1", "(field_1_1 int)");
+			createTable("testBug14815_2", "(field_2_1 int)");
+
+			boolean lcTableNames = this.conn.getMetaData()
+					.storesLowerCaseIdentifiers();
+
+			String tableName1 = lcTableNames ? "testbug14815_1"
+					: "testBug14815_1";
+			String tableName2 = lcTableNames ? "testbug14815_2"
+					: "testBug14815_2";
+
+			this.rs = this.conn.getMetaData().getColumns(
+					this.conn.getCatalog(), null, "testBug14815%", "%");
+
+			assertTrue(this.rs.next());
+			assertEquals(tableName1, this.rs.getString("TABLE_NAME"));
+			assertEquals("field_1_1", this.rs.getString("COLUMN_NAME"));
+
+			assertTrue(this.rs.next());
+			assertEquals(tableName2, this.rs.getString("TABLE_NAME"));
+			assertEquals("field_2_1", this.rs.getString("COLUMN_NAME"));
+
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#15854 - DBMD.getColumns() returns wrong type for BIT.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug15854() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			createTable("testBug15854", "(field1 BIT)");
+			try {
+				this.rs = this.conn.getMetaData().getColumns(
+						this.conn.getCatalog(), null, "testBug15854", "field1");
+				assertTrue(this.rs.next());
+				assertEquals(Types.BIT, this.rs.getInt("DATA_TYPE"));
+			} finally {
+				if (this.rs != null) {
+					ResultSet toClose = this.rs;
+					this.rs = null;
+					toClose.close();
+				}
+			}
+
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#16277 - Invalid classname returned for
+	 * RSMD.getColumnClassName() for BIGINT type.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug16277() throws Exception {
+		createTable("testBug16277", "(field1 BIGINT, field2 BIGINT UNSIGNED)");
+		ResultSetMetaData rsmd = this.stmt.executeQuery(
+				"SELECT field1, field2 FROM testBug16277").getMetaData();
+		assertEquals("java.lang.Long", rsmd.getColumnClassName(1));
+		assertEquals("java.math.BigInteger", rsmd.getColumnClassName(2));
+	}
+
+	/**
+	 * Tests fix for BUG#18554 - Aliased column names where length of name > 251
+	 * are corrupted.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug18554() throws Exception {
+		testBug18554(249);
+		testBug18554(250);
+		testBug18554(251);
+		testBug18554(252);
+		testBug18554(253);
+		testBug18554(254);
+		testBug18554(255);
+	}
+
+	private void testBug18554(int columnNameLength) throws Exception {
+		StringBuffer buf = new StringBuffer(columnNameLength + 2);
+
+		for (int i = 0; i < columnNameLength; i++) {
+			buf.append((char) ((Math.random() * 26) + 65));
+		}
+
+		String colName = buf.toString();
+		this.rs = this.stmt.executeQuery("select curtime() as `" + colName
+				+ "`");
+		ResultSetMetaData meta = this.rs.getMetaData();
+
+		assertEquals(colName, meta.getColumnLabel(1));
+
+	}
+
+	private void checkRsmdForBug13277(ResultSetMetaData rsmd)
+			throws SQLException {
+		assertEquals(17, rsmd.getColumnDisplaySize(1));
+
+		if (versionMeetsMinimum(4, 1)) {
+			assertEquals(false, rsmd.isDefinitelyWritable(1));
+			assertEquals(true, rsmd.isReadOnly(1));
+			assertEquals(false, rsmd.isWritable(1));
+		}
+	}
+
+	public void testSupportsCorrelatedSubqueries() throws Exception {
+		DatabaseMetaData dbmd = this.conn.getMetaData();
+
+		assertEquals(versionMeetsMinimum(4, 1), dbmd
+				.supportsCorrelatedSubqueries());
+	}
+
+	public void testSupportesGroupByUnrelated() throws Exception {
+		DatabaseMetaData dbmd = this.conn.getMetaData();
+
+		assertEquals(true, dbmd.supportsGroupByUnrelated());
+	}
+	
+	/**
+	 * 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 
+	 * information_schema, thus leading to a truncation exception that 
+	 * isn't present when not using information_schema.
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug21544() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+		
+		createTable("testBug21544",
+                "(foo_id INT NOT NULL, stuff LONGTEXT"
+                + ", PRIMARY KEY (foo_id)) TYPE=INNODB");
+		
+		Connection infoSchemConn = null;
+		
+		Properties props = new Properties();
+		props.setProperty("useInformationSchema", "true");
+		props.setProperty("jdbcCompliantTruncation", "false");
+		
+		infoSchemConn = getConnectionWithProps(props);
+		
+		try {
+	        this.rs = infoSchemConn.getMetaData().getColumns(null, null, 
+	        		"testBug21544",
+	                null);
+	        
+	        while (rs.next()) {
+	        	rs.getInt("COLUMN_SIZE");   
+	        }
+	    } finally {
+            if (infoSchemConn != null) {
+            	infoSchemConn.close();
+            }
+            
+            closeMemberJDBCResources();
+        }
+	}
+
+	/** 
+	 * Tests fix for BUG#22613 - DBMD.getColumns() does not return expected
+	 * COLUMN_SIZE for the SET type (fixed to be consistent with the ODBC driver)
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug22613() throws Exception {
+		
+		createTable("bug22613", "( s set('a','bc','def','ghij') default NULL, t enum('a', 'ab', 'cdef'))");
+
+		try {
+			checkMetadataForBug22613(this.conn);
+			
+			if (versionMeetsMinimum(5, 0)) {
+				Connection infoSchemConn = null;
+			
+				try {
+					Properties props = new Properties();
+					props.setProperty("useInformationSchema", "true");
+					
+					infoSchemConn = getConnectionWithProps(props);
+					
+					checkMetadataForBug22613(infoSchemConn);
+				} finally {
+					if (infoSchemConn != null) {
+						infoSchemConn.close();
+					}
+				}
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	private void checkMetadataForBug22613(Connection c) throws Exception {
+		String maxValue = "a,bc,def,ghij";
+		
+		try {
+			DatabaseMetaData meta = c.getMetaData();
+			this.rs = meta.getColumns(null, this.conn.getCatalog(), "bug22613", "s");
+			this.rs.first();
+
+			assertEquals(maxValue.length(), rs.getInt("COLUMN_SIZE"));
+			
+			this.rs = meta.getColumns(null, c.getCatalog(), "bug22613", "t");
+			this.rs.first();
+
+			assertEquals(4, rs.getInt("COLUMN_SIZE"));			
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+
+	private void testAbsenceOfMetadataForQuery(String query) throws Exception {
+		try {
+			this.pstmt = this.conn.prepareStatement(query);
+			ResultSetMetaData rsmd = this.pstmt.getMetaData();
+
+			assertNull(rsmd);
+
+			this.pstmt = ((com.mysql.jdbc.Connection) this.conn)
+					.clientPrepareStatement(query);
+			rsmd = this.pstmt.getMetaData();
+
+			assertNull(rsmd);
+		} finally {
+			if (this.pstmt != null) {
+				this.pstmt.close();
+			}
+		}
+	}
+
+	public void testRSMDToStringFromDBMD() throws Exception {
+		try {		
+			this.rs = this.conn.getMetaData().getTypeInfo();
+			
+			this.rs.getMetaData().toString(); // used to cause NPE
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	public void testCharacterSetForDBMD() throws Exception {
+		String tableName = "\u00e9\u0074\u00e9";
+		createTable(tableName, "(field1 int)");
+		this.rs = this.conn.getMetaData().getTables(this.conn.getCatalog(), 
+				null, tableName, new String[] {"TABLE"});
+		assertEquals(true, this.rs.next());
+		System.out.println(this.rs.getString("TABLE_NAME"));
+		System.out.println(new String(this.rs.getBytes("TABLE_NAME"), "UTF-8"));
+	}
+
+	/**
+	 * Tests fix for BUG#18258 - Nonexistent catalog/database causes SQLException
+	 * to be raised, rather than returning empty result set.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug18258() throws Exception {
+		String bogusDatabaseName = "abcdefghijklmnopqrstuvwxyz";
+		this.conn.getMetaData().getTables(bogusDatabaseName, "%", "%", new String[] {"TABLE", "VIEW"});
+		this.conn.getMetaData().getColumns(bogusDatabaseName, "%", "%", "%");
+		this.conn.getMetaData().getProcedures(bogusDatabaseName, "%", "%");
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/MicroPerformanceRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/MicroPerformanceRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/MicroPerformanceRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,504 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.Time;
+import java.sql.Timestamp;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Microperformance benchmarks to track increase/decrease in performance of core
+ * methods in the driver over time.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: MicroPerformanceRegressionTest.java,v 1.1.2.1 2005/05/13
+ *          18:58:38 mmatthews Exp $
+ */
+public class MicroPerformanceRegressionTest extends BaseTestCase {
+
+	private double scaleFactor = 1.0;
+
+	private final static int ORIGINAL_LOOP_TIME_MS = 2300;
+
+	private final static double LEEWAY = 3.0;
+
+	private final static Map BASELINE_TIMES = new HashMap();
+
+	static {
+		BASELINE_TIMES.put("ResultSet.getInt()", new Double(0.00661));
+		BASELINE_TIMES.put("ResultSet.getDouble()", new Double(0.00671));
+		BASELINE_TIMES.put("ResultSet.getTime()", new Double(0.02033));
+		BASELINE_TIMES.put("ResultSet.getTimestamp()", new Double(0.02363));
+		BASELINE_TIMES.put("ResultSet.getDate()", new Double(0.02223));
+		BASELINE_TIMES.put("ResultSet.getString()", new Double(0.00982));
+		BASELINE_TIMES.put("ResultSet.getObject() on a string", new Double(
+				0.00861));
+		BASELINE_TIMES
+				.put("Connection.prepareStatement()", new Double(0.18547));
+		BASELINE_TIMES.put("PreparedStatement.setInt()", new Double(0.0011));
+		BASELINE_TIMES
+				.put("PreparedStatement.setDouble()", new Double(0.00671));
+		BASELINE_TIMES.put("PreparedStatement.setTime()", new Double(0.0642));
+		BASELINE_TIMES.put("PreparedStatement.setTimestamp()", new Double(
+				0.03184));
+		BASELINE_TIMES.put("PreparedStatement.setDate()", new Double(0.12248));
+		BASELINE_TIMES
+				.put("PreparedStatement.setString()", new Double(0.01512));
+		BASELINE_TIMES.put("PreparedStatement.setObject() on a string",
+				new Double(0.01923));
+		BASELINE_TIMES.put("single selects", new Double(46));
+		BASELINE_TIMES.put("5 standalone queries", new Double(146));
+		BASELINE_TIMES.put("total time all queries", new Double(190));
+	}
+
+	public MicroPerformanceRegressionTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(MicroPerformanceRegressionTest.class);
+	}
+
+	/**
+	 * Tests result set accessors performance.
+	 * 
+	 * @throws Exception
+	 *             if the performance of these methods does not meet
+	 *             expectations.
+	 */
+	public void testResultSetAccessors() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS marktest");
+			this.stmt
+					.executeUpdate("CREATE TABLE marktest(intField INT, floatField DOUBLE, timeField TIME, datetimeField DATETIME, stringField VARCHAR(64))");
+			this.stmt
+					.executeUpdate("INSERT INTO marktest VALUES (123456789, 12345.6789, NOW(), NOW(), 'abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@')");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT intField, floatField, timeField, datetimeField, stringField FROM marktest");
+
+			this.rs.next();
+
+			int numLoops = 100000;
+
+			long start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				this.rs.getInt(1);
+			}
+
+			double getIntAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("ResultSet.getInt()", getIntAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				this.rs.getDouble(2);
+			}
+
+			double getDoubleAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("ResultSet.getDouble()", getDoubleAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				this.rs.getTime(3);
+			}
+
+			double getTimeAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("ResultSet.getTime()", getTimeAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				this.rs.getTimestamp(4);
+			}
+
+			double getTimestampAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("ResultSet.getTimestamp()", getTimestampAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				this.rs.getDate(4);
+			}
+
+			double getDateAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("ResultSet.getDate()", getDateAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				this.rs.getString(5);
+			}
+
+			double getStringAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("ResultSet.getString()", getStringAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				this.rs.getObject(5);
+			}
+
+			double getStringObjAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("ResultSet.getObject() on a string", getStringObjAvgMs);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS marktest");
+		}
+	}
+
+	public void testPreparedStatementTimes() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS marktest");
+			this.stmt
+					.executeUpdate("CREATE TABLE marktest(intField INT, floatField DOUBLE, timeField TIME, datetimeField DATETIME, stringField VARCHAR(64))");
+			this.stmt
+					.executeUpdate("INSERT INTO marktest VALUES (123456789, 12345.6789, NOW(), NOW(), 'abcdefghijklmnopqrstuvABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@')");
+
+			long start = System.currentTimeMillis();
+
+			long blockStart = System.currentTimeMillis();
+			long lastBlock = 0;
+
+			int numLoops = 100000;
+
+			int numPrepares = 100000;
+
+			if (versionMeetsMinimum(4, 1)) {
+				numPrepares = 10000; // we don't need to do so many for
+				// server-side prep statements...
+			}
+
+			for (int i = 0; i < numPrepares; i++) {
+				if (i % 1000 == 0) {
+
+					long blockEnd = System.currentTimeMillis();
+
+					long totalTime = blockEnd - blockStart;
+
+					blockStart = blockEnd;
+
+					StringBuffer messageBuf = new StringBuffer();
+
+					messageBuf.append(i
+							+ " prepares, the last 1000 prepares took "
+							+ totalTime + " ms");
+
+					if (lastBlock == 0) {
+						lastBlock = totalTime;
+						messageBuf.append(".");
+					} else {
+						double diff = (double) totalTime / (double) lastBlock;
+
+						messageBuf.append(", difference is " + diff + " x");
+
+						lastBlock = totalTime;
+					}
+
+					System.out.println(messageBuf.toString());
+
+				}
+
+				PreparedStatement pStmt = this.conn
+						.prepareStatement("INSERT INTO test.marktest VALUES (?, ?, ?, ?, ?)");
+				pStmt.close();
+			}
+
+			double getPrepareStmtAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numPrepares;
+
+			// checkTime("Connection.prepareStatement()", getPrepareStmtAvgMs);
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("INSERT INTO marktest VALUES (?, ?, ?, ?, ?)");
+
+			System.out.println(pStmt.toString());
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				pStmt.setInt(1, 1);
+			}
+
+			System.out.println(pStmt.toString());
+
+			double setIntAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("PreparedStatement.setInt()", setIntAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				pStmt.setDouble(2, 1234567890.1234);
+			}
+
+			double setDoubleAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("PreparedStatement.setDouble()", setDoubleAvgMs);
+
+			start = System.currentTimeMillis();
+
+			Time tm = new Time(start);
+
+			for (int i = 0; i < numLoops; i++) {
+				pStmt.setTime(3, tm);
+			}
+
+			double setTimeAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("PreparedStatement.setTime()", setTimeAvgMs);
+
+			start = System.currentTimeMillis();
+
+			Timestamp ts = new Timestamp(start);
+
+			for (int i = 0; i < numLoops; i++) {
+				pStmt.setTimestamp(4, ts);
+			}
+
+			double setTimestampAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("PreparedStatement.setTimestamp()", setTimestampAvgMs);
+
+			start = System.currentTimeMillis();
+
+			Date dt = new Date(start);
+
+			for (int i = 0; i < numLoops; i++) {
+				pStmt.setDate(4, dt);
+			}
+
+			double setDateAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("PreparedStatement.setDate()", setDateAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				pStmt
+						.setString(5,
+								"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@");
+			}
+
+			double setStringAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("PreparedStatement.setString()", setStringAvgMs);
+
+			start = System.currentTimeMillis();
+
+			for (int i = 0; i < numLoops; i++) {
+				pStmt
+						.setObject(5,
+								"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789!@");
+			}
+
+			double setStringObjAvgMs = (double) (System.currentTimeMillis() - start)
+					/ numLoops;
+
+			checkTime("PreparedStatement.setObject() on a string",
+					setStringObjAvgMs);
+
+			start = System.currentTimeMillis();
+
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS marktest");
+		}
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+
+		System.out.println("Calculating performance scaling factor...");
+		// Run this simple test to get some sort of performance scaling factor,
+		// compared to
+		// the development environment. This should help reduce false-positives
+		// on this test.
+		int numLoops = 10000;
+
+		long start = System.currentTimeMillis();
+
+		for (int j = 0; j < 2000; j++) {
+			StringBuffer buf = new StringBuffer(numLoops);
+
+			for (int i = 0; i < numLoops; i++) {
+				buf.append('a');
+			}
+		}
+
+		long elapsedTime = System.currentTimeMillis() - start;
+
+		System.out.println("Elapsed time for factor: " + elapsedTime);
+
+		this.scaleFactor = (double) elapsedTime
+				/ (double) ORIGINAL_LOOP_TIME_MS;
+
+		System.out
+				.println("Performance scaling factor is: " + this.scaleFactor);
+	}
+
+	private void checkTime(String testType, double avgExecTimeMs)
+			throws Exception {
+		
+		double adjustForVendor = 1.0D;
+
+		if (isRunningOnJRockit()) {
+			adjustForVendor = 4.0D;
+		}
+
+		Double baselineExecTimeMs = (Double) BASELINE_TIMES.get(testType);
+
+		if (baselineExecTimeMs == null) {
+			throw new Exception("No baseline time recorded for test '"
+					+ testType + "'");
+		}
+
+		double acceptableTime = LEEWAY * baselineExecTimeMs.doubleValue()
+				* this.scaleFactor * adjustForVendor;
+
+		assertTrue("Average execution time of " + avgExecTimeMs
+				+ " ms. exceeded baseline * leeway of " + acceptableTime
+				+ " ms.", (avgExecTimeMs <= acceptableTime));
+	}
+
+	public void testBug6359() throws Exception {
+		if (runLongTests()) {
+			int numRows = 550000;
+			int numSelects = 100000;
+
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6359");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBug6359 (pk_field INT PRIMARY KEY NOT NULL AUTO_INCREMENT, field1 INT, field2 INT, field3 INT, field4 INT, field5 INT, field6 INT, field7 INT, field8 INT, field9 INT,  INDEX (field1))");
+
+				PreparedStatement pStmt = this.conn
+						.prepareStatement("INSERT INTO testBug6359 (field1, field2, field3, field4, field5, field6, field7, field8, field9) VALUES (?, 1, 2, 3, 4, 5, 6, 7, 8)");
+
+				logDebug("Loading " + numRows + " rows...");
+
+				for (int i = 0; i < numRows; i++) {
+					pStmt.setInt(1, i);
+					pStmt.executeUpdate();
+
+					if ((i % 10000) == 0) {
+						logDebug(i + " rows loaded so far");
+					}
+				}
+
+				logDebug("Finished loading rows");
+
+				long begin = System.currentTimeMillis();
+
+				long beginSingleQuery = System.currentTimeMillis();
+
+				for (int i = 0; i < numSelects; i++) {
+					this.rs = this.stmt
+							.executeQuery("SELECT pk_field FROM testBug6359 WHERE field1 BETWEEN 1 AND 5");
+				}
+
+				long endSingleQuery = System.currentTimeMillis();
+
+				double secondsSingleQuery = ((double) endSingleQuery - (double) beginSingleQuery) / 1000;
+
+				logDebug("time to execute " + numSelects + " single queries: "
+						+ secondsSingleQuery + " seconds");
+
+				checkTime("single selects", secondsSingleQuery);
+
+				PreparedStatement pStmt2 = this.conn
+						.prepareStatement("SELECT field2, field3, field4, field5 FROM testBug6359 WHERE pk_field=?");
+
+				long beginFiveQueries = System.currentTimeMillis();
+
+				for (int i = 0; i < numSelects; i++) {
+
+					for (int j = 0; j < 5; j++) {
+						pStmt2.setInt(1, j);
+						pStmt2.executeQuery();
+					}
+				}
+
+				long endFiveQueries = System.currentTimeMillis();
+
+				double secondsFiveQueries = ((double) endFiveQueries - (double) beginFiveQueries) / 1000;
+
+				logDebug("time to execute " + numSelects
+						+ " 5 standalone queries: " + secondsFiveQueries
+						+ " seconds");
+
+				checkTime("5 standalone queries", secondsFiveQueries);
+
+				long end = System.currentTimeMillis();
+
+				double seconds = ((double) end - (double) begin) / 1000;
+
+				logDebug("time to execute " + numSelects + " selects: "
+						+ seconds + " seconds");
+
+				checkTime("total time all queries", seconds);
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6359");
+			}
+		}
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/NumbersRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/NumbersRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/NumbersRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,309 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests various number-handling issues that have arrisen in the JDBC driver at
+ * one time or another.
+ * 
+ * @author Mark Matthews
+ */
+public class NumbersRegressionTest extends BaseTestCase {
+	/**
+	 * Constructor for NumbersRegressionTest.
+	 * 
+	 * @param name
+	 *            the test name
+	 */
+	public NumbersRegressionTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases
+	 * 
+	 * @param args
+	 *            command-line args
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(NumbersRegressionTest.class);
+	}
+
+	/**
+	 * Tests that BIGINT retrieval works correctly
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testBigInt() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS bigIntRegression");
+			this.stmt
+					.executeUpdate("CREATE TABLE bigIntRegression ( val BIGINT NOT NULL)");
+			this.stmt
+					.executeUpdate("INSERT INTO bigIntRegression VALUES (6692730313872877584)");
+			this.rs = this.stmt
+					.executeQuery("SELECT val FROM bigIntRegression");
+
+			while (this.rs.next()) {
+				// check retrieval
+				long retrieveAsLong = this.rs.getLong(1);
+				assertTrue(retrieveAsLong == 6692730313872877584L);
+			}
+
+			this.rs.close();
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS bigIntRegression");
+
+			String bigIntAsString = "6692730313872877584";
+
+			long parsedBigIntAsLong = Long.parseLong(bigIntAsString);
+
+			// check JDK parsing
+			assertTrue(bigIntAsString
+					.equals(String.valueOf(parsedBigIntAsLong)));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS bigIntRegression");
+		}
+	}
+
+	/**
+	 * Tests correct type assignment for MySQL FLOAT and REAL datatypes.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testFloatsAndReals() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS floatsAndReals");
+			this.stmt
+					.executeUpdate("CREATE TABLE IF NOT EXISTS floatsAndReals(floatCol FLOAT, realCol REAL, doubleCol DOUBLE)");
+			this.stmt
+					.executeUpdate("INSERT INTO floatsAndReals VALUES (0, 0, 0)");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT floatCol, realCol, doubleCol FROM floatsAndReals");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			this.rs.next();
+
+			assertTrue(rsmd.getColumnClassName(1).equals("java.lang.Float"));
+			assertTrue(this.rs.getObject(1).getClass().getName().equals(
+					"java.lang.Float"));
+
+			assertTrue(rsmd.getColumnClassName(2).equals("java.lang.Double"));
+			assertTrue(this.rs.getObject(2).getClass().getName().equals(
+					"java.lang.Double"));
+
+			assertTrue(rsmd.getColumnClassName(3).equals("java.lang.Double"));
+			assertTrue(this.rs.getObject(3).getClass().getName().equals(
+					"java.lang.Double"));
+
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS floatsAndReals");
+		}
+	}
+
+	/**
+	 * Tests that ResultSetMetaData precision and scale methods work correctly
+	 * for all numeric types.
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testPrecisionAndScale() throws Exception {
+		testPrecisionForType("TINYINT", 8, -1, false);
+		testPrecisionForType("TINYINT", 8, -1, true);
+		testPrecisionForType("SMALLINT", 8, -1, false);
+		testPrecisionForType("SMALLINT", 8, -1, true);
+		testPrecisionForType("MEDIUMINT", 8, -1, false);
+		testPrecisionForType("MEDIUMINT", 8, -1, true);
+		testPrecisionForType("INT", 8, -1, false);
+		testPrecisionForType("INT", 8, -1, true);
+		testPrecisionForType("BIGINT", 8, -1, false);
+		testPrecisionForType("BIGINT", 8, -1, true);
+
+		testPrecisionForType("FLOAT", 8, 4, false);
+		testPrecisionForType("FLOAT", 8, 4, true);
+		testPrecisionForType("DOUBLE", 8, 4, false);
+		testPrecisionForType("DOUBLE", 8, 4, true);
+
+		testPrecisionForType("DECIMAL", 8, 4, false);
+		testPrecisionForType("DECIMAL", 8, 4, true);
+
+		testPrecisionForType("DECIMAL", 9, 0, false);
+		testPrecisionForType("DECIMAL", 9, 0, true);
+	}
+
+	private void testPrecisionForType(String typeName, int m, int d,
+			boolean unsigned) throws Exception {
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS precisionAndScaleRegression");
+
+			StringBuffer createStatement = new StringBuffer(
+					"CREATE TABLE precisionAndScaleRegression ( val ");
+			createStatement.append(typeName);
+			createStatement.append("(");
+			createStatement.append(m);
+
+			if (d != -1) {
+				createStatement.append(",");
+				createStatement.append(d);
+			}
+
+			createStatement.append(")");
+
+			if (unsigned) {
+				createStatement.append(" UNSIGNED ");
+			}
+
+			createStatement.append(" NOT NULL)");
+
+			this.stmt.executeUpdate(createStatement.toString());
+
+			this.rs = this.stmt
+					.executeQuery("SELECT val FROM precisionAndScaleRegression");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+			assertTrue("Precision returned incorrectly for type " + typeName
+					+ ", " + m + " != rsmd.getPrecision() = "
+					+ rsmd.getPrecision(1), rsmd.getPrecision(1) == m);
+
+			if (d != -1) {
+				assertTrue("Scale returned incorrectly for type " + typeName
+						+ ", " + d + " != rsmd.getScale() = "
+						+ rsmd.getScale(1), rsmd.getScale(1) == d);
+			}
+		} finally {
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (Exception ex) {
+					// ignore
+				}
+			}
+
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS precisionAndScaleRegression");
+		}
+	}
+
+	public void testIntShouldReturnLong() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testIntRetLong");
+			this.stmt.executeUpdate("CREATE TABLE testIntRetLong(field1 INT)");
+			this.stmt.executeUpdate("INSERT INTO testIntRetLong VALUES (1)");
+
+			this.rs = this.stmt.executeQuery("SELECT * FROM testIntRetLong");
+			this.rs.next();
+
+			assertTrue(this.rs.getObject(1).getClass().equals(
+					java.lang.Integer.class));
+		} finally {
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (SQLException sqlEx) {
+					// ignore
+				}
+
+				this.rs = null;
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testIntRetLong");
+
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#5729, UNSIGNED BIGINT returned incorrectly
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug5729() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			String valueAsString = "1095923280000";
+
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5729");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBug5729(field1 BIGINT UNSIGNED)");
+				this.stmt.executeUpdate("INSERT INTO testBug5729 VALUES ("
+						+ valueAsString + ")");
+
+				this.rs = this.conn.prepareStatement(
+						"SELECT * FROM testBug5729").executeQuery();
+				this.rs.next();
+
+				assertTrue(this.rs.getObject(1).toString()
+						.equals(valueAsString));
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5729");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#8484 - ResultSet.getBigDecimal() throws exception when
+	 * rounding would need to occur to set scale.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 * @deprecated
+	 */
+	public void testBug8484() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8484");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug8484 (field1 DECIMAL(16, 8), field2 varchar(32))");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug8484 VALUES (12345678.12345678, '')");
+			this.rs = this.stmt
+					.executeQuery("SELECT field1, field2 FROM testBug8484");
+			this.rs.next();
+			assertEquals("12345678.123", this.rs.getBigDecimal(1, 3).toString());
+			assertEquals("0.000", this.rs.getBigDecimal(2, 3).toString());
+
+			this.pstmt = this.conn
+					.prepareStatement("SELECT field1, field2 FROM testBug8484");
+			this.rs = this.pstmt.executeQuery();
+			this.rs.next();
+			assertEquals("12345678.123", this.rs.getBigDecimal(1, 3).toString());
+			assertEquals("0.000", this.rs.getBigDecimal(2, 3).toString());
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8484");
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/PooledConnectionRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/PooledConnectionRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/PooledConnectionRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,410 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+ 
+ */
+package testsuite.regression;
+
+import java.io.BufferedInputStream;
+import java.io.FileInputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+
+import javax.sql.ConnectionEvent;
+import javax.sql.ConnectionEventListener;
+import javax.sql.ConnectionPoolDataSource;
+import javax.sql.PooledConnection;
+
+import junit.framework.Test;
+import junit.framework.TestSuite;
+import testsuite.BaseTestCase;
+
+import com.mysql.jdbc.PacketTooBigException;
+import com.mysql.jdbc.jdbc2.optional.ConnectionWrapper;
+import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
+
+/**
+ * Tests a PooledConnection implementation provided by a JDBC driver. Test case
+ * provided by Johnny Macchione from bug database record BUG#884. According to
+ * the JDBC 2.0 specification:
+ * 
+ * <p>
+ * "Each call to PooledConnection.getConnection() must return a newly
+ * constructed Connection object that exhibits the default Connection behavior.
+ * Only the most recent Connection object produced from a particular
+ * PooledConnection is open. An existing Connection object is automatically
+ * closed, if the getConnection() method of its associated Pooled-Connection is
+ * called again, before it has been explicitly closed by the application. This
+ * gives the application server a way to �take away� a Connection from the
+ * application if it wishes, and give it out to someone else. This capability
+ * will not likely be used frequently in practice."
+ * </p>
+ * 
+ * <p>
+ * "When the application calls Connection.close(), an event is triggered that
+ * tells the connection pool it can recycle the physical database connection. In
+ * other words, the event signals the connection pool that the PooledConnection
+ * object which originally produced the Connection object generating the event
+ * can be put back in the connection pool."
+ * </p>
+ * 
+ * <p>
+ * "A Connection-EventListener will also be notified when a fatal error occurs,
+ * so that it can make a note not to put a bad PooledConnection object back in
+ * the cache when the application finishes using it. When an error occurs, the
+ * ConnectionEventListener is notified by the JDBC driver, just before the
+ * driver throws an SQLException to the application to notify it of the same
+ * error. Note that automatic closing of a Connection object as discussed in the
+ * previous section does not generate a connection close event."
+ * </p>
+ * The JDBC 3.0 specification states the same in other words:
+ * 
+ * <p>
+ * "The Connection.close method closes the logical handle, but the physical
+ * connection is maintained. The connection pool manager is notified that the
+ * underlying PooledConnection object is now available for reuse. If the
+ * application attempts to reuse the logical handle, the Connection
+ * implementation throws an SQLException."
+ * </p>
+ * 
+ * <p>
+ * "For a given PooledConnection object, only the most recently produced logical
+ * Connection object will be valid. Any previously existing Connection object is
+ * automatically closed when the associated PooledConnection.getConnection
+ * method is called. Listeners (connection pool managers) are not notified in
+ * this case. This gives the application server a way to take a connection away
+ * from a client. This is an unlikely scenario but may be useful if the
+ * application server is trying to force an orderly shutdown."
+ * </p>
+ * 
+ * <p>
+ * "A connection pool manager shuts down a physical connection by calling the
+ * method PooledConnection.close. This method is typically called only in
+ * certain circumstances: when the application server is undergoing an orderly
+ * shutdown, when the connection cache is being reinitialized, or when the
+ * application server receives an event indicating that an unrecoverable error
+ * has occurred on the connection."
+ * </p>
+ * Even though the specification isn't clear about it, I think it is no use
+ * generating a close event when calling the method PooledConnection.close(),
+ * even if a logical Connection is open for this PooledConnection, bc the
+ * PooledConnection will obviously not be returned to the pool.
+ * 
+ * @author fcr
+ */
+public final class PooledConnectionRegressionTest extends BaseTestCase {
+	private ConnectionPoolDataSource cpds;
+
+	// Count nb of closeEvent.
+	private int closeEventCount;
+
+	// Count nb of connectionErrorEvent
+	private int connectionErrorEventCount;
+
+	/**
+	 * Creates a new instance of ProgressPooledConnectionTest
+	 * 
+	 * @param testname
+	 *            DOCUMENT ME!
+	 */
+	public PooledConnectionRegressionTest(String testname) {
+		super(testname);
+	}
+
+	/**
+	 * Set up test case before a test is run.
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+
+		// Reset event count.
+		this.closeEventCount = 0;
+		this.connectionErrorEventCount = 0;
+
+		MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();
+
+		ds.setURL(BaseTestCase.dbUrl);
+
+		this.cpds = ds;
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(PooledConnectionRegressionTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @return a test suite composed of this test case.
+	 */
+	public static Test suite() {
+		TestSuite suite = new TestSuite(PooledConnectionRegressionTest.class);
+
+		return suite;
+	}
+
+	/**
+	 * After the test is run.
+	 */
+	public void tearDown() {
+		this.cpds = null;
+	}
+
+	/**
+	 * Tests fix for BUG#7136 ... Statement.getConnection() returning physical
+	 * connection instead of logical connection.
+	 */
+	public void testBug7136() {
+		final ConnectionEventListener conListener = new ConnectionListener();
+		PooledConnection pc = null;
+		this.closeEventCount = 0;
+
+		try {
+			pc = this.cpds.getPooledConnection();
+
+			pc.addConnectionEventListener(conListener);
+
+			Connection conn = pc.getConnection();
+
+			Connection connFromStatement = conn.createStatement()
+					.getConnection();
+
+			// This should generate a close event.
+
+			connFromStatement.close();
+
+			assertEquals("One close event should've been registered", 1,
+					this.closeEventCount);
+
+			this.closeEventCount = 0;
+
+			conn = pc.getConnection();
+
+			Connection connFromPreparedStatement = conn.prepareStatement(
+					"SELECT 1").getConnection();
+
+			// This should generate a close event.
+
+			connFromPreparedStatement.close();
+
+			assertEquals("One close event should've been registered", 1,
+					this.closeEventCount);
+
+		} catch (SQLException ex) {
+			fail(ex.toString());
+		} finally {
+			if (pc != null) {
+				try {
+					pc.close();
+				} catch (SQLException ex) {
+					ex.printStackTrace();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Test the nb of closeEvents generated when a Connection is reclaimed. No
+	 * event should be generated in that case.
+	 */
+	public void testConnectionReclaim() {
+		final ConnectionEventListener conListener = new ConnectionListener();
+		PooledConnection pc = null;
+		final int NB_TESTS = 5;
+
+		try {
+			pc = this.cpds.getPooledConnection();
+
+			pc.addConnectionEventListener(conListener);
+
+			for (int i = 0; i < NB_TESTS; i++) {
+				Connection conn = pc.getConnection();
+
+				try {
+					// Try to reclaim connection.
+					System.out.println("Before connection reclaim.");
+
+					conn = pc.getConnection();
+
+					System.out.println("After connection reclaim.");
+				} finally {
+					if (conn != null) {
+						System.out.println("Before connection.close().");
+
+						// This should generate a close event.
+						conn.close();
+
+						System.out.println("After connection.close().");
+					}
+				}
+			}
+		} catch (SQLException ex) {
+			ex.printStackTrace();
+			fail(ex.toString());
+		} finally {
+			if (pc != null) {
+				try {
+					System.out.println("Before pooledConnection.close().");
+
+					// This should not generate a close event.
+					pc.close();
+
+					System.out.println("After pooledConnection.close().");
+				} catch (SQLException ex) {
+					ex.printStackTrace();
+					fail(ex.toString());
+				}
+			}
+		}
+
+		assertEquals("Wrong nb of CloseEvents: ", NB_TESTS,
+				this.closeEventCount);
+	}
+
+	/**
+	 * Tests that PacketTooLargeException doesn't clober the connection.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testPacketTooLargeException() throws Exception {
+		final ConnectionEventListener conListener = new ConnectionListener();
+		PooledConnection pc = null;
+
+		pc = this.cpds.getPooledConnection();
+
+		pc.addConnectionEventListener(conListener);
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testPacketTooLarge");
+			this.stmt
+					.executeUpdate("CREATE TABLE testPacketTooLarge(field1 LONGBLOB)");
+
+			Connection connFromPool = pc.getConnection();
+			PreparedStatement pstmtFromPool = ((ConnectionWrapper) connFromPool)
+					.clientPrepare("INSERT INTO testPacketTooLarge VALUES (?)");
+
+			this.rs = this.stmt
+					.executeQuery("SHOW VARIABLES LIKE 'max_allowed_packet'");
+			this.rs.next();
+
+			int maxAllowedPacket = this.rs.getInt(2);
+
+			int numChars = (int) (maxAllowedPacket * 1.2);
+
+			pstmtFromPool.setBinaryStream(1, new BufferedInputStream(
+					new FileInputStream(newTempBinaryFile(
+							"testPacketTooLargeException", numChars))),
+					numChars);
+
+			try {
+				pstmtFromPool.executeUpdate();
+				fail("Expecting PacketTooLargeException");
+			} catch (PacketTooBigException ptbe) {
+				// We're expecting this one...
+			}
+
+			// This should still work okay, even though the last query on the
+			// same
+			// connection didn't...
+			connFromPool.createStatement().executeQuery("SELECT 1");
+
+			assertTrue(this.connectionErrorEventCount == 0);
+			assertTrue(this.closeEventCount == 0);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testPacketTooLarge");
+		}
+	}
+
+	/**
+	 * Test the nb of closeEvents generated by a PooledConnection. A
+	 * JDBC-compliant driver should only generate 1 closeEvent each time
+	 * connection.close() is called.
+	 */
+	public void testCloseEvent() {
+		final ConnectionEventListener conListener = new ConnectionListener();
+		PooledConnection pc = null;
+		final int NB_TESTS = 5;
+
+		try {
+			pc = this.cpds.getPooledConnection();
+
+			pc.addConnectionEventListener(conListener);
+
+			for (int i = 0; i < NB_TESTS; i++) {
+				Connection pConn = pc.getConnection();
+
+				System.out.println("Before connection.close().");
+
+				// This should generate a close event.
+				pConn.close();
+
+				System.out.println("After connection.close().");
+			}
+		} catch (SQLException ex) {
+			fail(ex.toString());
+		} finally {
+			if (pc != null) {
+				try {
+					System.out.println("Before pooledConnection.close().");
+
+					// This should not generate a close event.
+					pc.close();
+
+					System.out.println("After pooledConnection.close().");
+				} catch (SQLException ex) {
+					ex.printStackTrace();
+				}
+			}
+		}
+		assertEquals("Wrong nb of CloseEvents: ", NB_TESTS,
+				this.closeEventCount);
+	}
+
+	/**
+	 * Listener for PooledConnection events.
+	 */
+	private final class ConnectionListener implements ConnectionEventListener {
+		/** */
+		public void connectionClosed(ConnectionEvent event) {
+			PooledConnectionRegressionTest.this.closeEventCount++;
+			System.out
+					.println(PooledConnectionRegressionTest.this.closeEventCount
+							+ " - Connection closed.");
+		}
+
+		/** */
+		public void connectionErrorOccurred(ConnectionEvent event) {
+			PooledConnectionRegressionTest.this.connectionErrorEventCount++;
+			System.out.println("Connection error: " + event.getSQLException());
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/ResultSetRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/ResultSetRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/ResultSetRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,3763 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.io.Reader;
+import java.math.BigDecimal;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.sql.Types;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import testsuite.BaseTestCase;
+
+import com.mysql.jdbc.MysqlDataTruncation;
+import com.mysql.jdbc.NotUpdatable;
+import com.mysql.jdbc.SQLError;
+
+/**
+ * Regression test cases for the ResultSet class.
+ * 
+ * @author Mark Matthews
+ */
+public class ResultSetRegressionTest extends BaseTestCase {
+	/**
+	 * Creates a new ResultSetRegressionTest
+	 * 
+	 * @param name
+	 *            the name of the test to run
+	 */
+	public ResultSetRegressionTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(ResultSetRegressionTest.class);
+	}
+
+	/**
+	 * Tests fix for BUG#???? -- Numeric types and server-side prepared
+	 * statements incorrectly detect nulls.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug2359() throws Exception {
+		try {
+			/*
+			 * this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2359");
+			 * this.stmt.executeUpdate("CREATE TABLE testBug2359 (field1 INT)
+			 * TYPE=InnoDB"); this.stmt.executeUpdate("INSERT INTO testBug2359
+			 * VALUES (null), (1)");
+			 * 
+			 * this.pstmt = this.conn.prepareStatement("SELECT field1 FROM
+			 * testBug2359 WHERE field1 IS NULL"); this.rs =
+			 * this.pstmt.executeQuery();
+			 * 
+			 * assertTrue(this.rs.next());
+			 * 
+			 * assertTrue(this.rs.getByte(1) == 0);
+			 * assertTrue(this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getShort(1) == 0);
+			 * assertTrue(this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getInt(1) == 0);
+			 * assertTrue(this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getLong(1) == 0);
+			 * assertTrue(this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getFloat(1) == 0);
+			 * assertTrue(this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getDouble(1) == 0);
+			 * assertTrue(this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getBigDecimal(1) == null);
+			 * assertTrue(this.rs.wasNull());
+			 * 
+			 * this.rs.close();
+			 * 
+			 * this.pstmt = this.conn.prepareStatement("SELECT max(field1) FROM
+			 * testBug2359 WHERE field1 IS NOT NULL"); this.rs =
+			 * this.pstmt.executeQuery(); assertTrue(this.rs.next());
+			 * 
+			 * assertTrue(this.rs.getByte(1) == 1);
+			 * assertTrue(!this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getShort(1) == 1);
+			 * assertTrue(!this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getInt(1) == 1);
+			 * assertTrue(!this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getLong(1) == 1);
+			 * assertTrue(!this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getFloat(1) == 1);
+			 * assertTrue(!this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getDouble(1) == 1);
+			 * assertTrue(!this.rs.wasNull());
+			 * 
+			 * assertTrue(this.rs.getBigDecimal(1) != null);
+			 * assertTrue(!this.rs.wasNull());
+			 * 
+			 */
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2359_1");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug2359_1 (id INT) TYPE=InnoDB");
+			this.stmt.executeUpdate("INSERT INTO testBug2359_1 VALUES (1)");
+
+			this.pstmt = this.conn
+					.prepareStatement("SELECT max(id) FROM testBug2359_1");
+			this.rs = this.pstmt.executeQuery();
+
+			if (this.rs.next()) {
+				assertTrue(this.rs.getInt(1) != 0);
+				this.rs.close();
+			}
+
+			this.rs.close();
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2359_1");
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2359");
+
+			this.rs.close();
+			this.pstmt.close();
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#2643, ClassCastException when using this.rs.absolute()
+	 * and server-side prepared statements.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug2623() throws Exception {
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = this.conn
+					.prepareStatement("SELECT NOW()",
+							ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_READ_ONLY);
+
+			this.rs = pStmt.executeQuery();
+
+			this.rs.absolute(1);
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+			}
+
+			this.rs = null;
+
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#2654, "Column 'column.table' not found" when "order by"
+	 * in query"
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug2654() throws Exception {
+		if (false) { // this is currently a server-level bug
+
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS foo");
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS bar");
+
+				this.stmt.executeUpdate("CREATE TABLE foo ("
+						+ "  id tinyint(3) default NULL,"
+						+ "  data varchar(255) default NULL"
+						+ ") TYPE=MyISAM DEFAULT CHARSET=latin1");
+				this.stmt
+						.executeUpdate("INSERT INTO foo VALUES (1,'male'),(2,'female')");
+
+				this.stmt.executeUpdate("CREATE TABLE bar ("
+						+ "id tinyint(3) unsigned default NULL,"
+						+ "data char(3) default '0'"
+						+ ") TYPE=MyISAM DEFAULT CHARSET=latin1");
+
+				this.stmt
+						.executeUpdate("INSERT INTO bar VALUES (1,'yes'),(2,'no')");
+
+				String statement = "select foo.id, foo.data, "
+						+ "bar.data from foo, bar" + "	where "
+						+ "foo.id = bar.id order by foo.id";
+
+				String column = "foo.data";
+
+				this.rs = this.stmt.executeQuery(statement);
+
+				ResultSetMetaData rsmd = this.rs.getMetaData();
+				System.out.println(rsmd.getTableName(1));
+				System.out.println(rsmd.getColumnName(1));
+
+				this.rs.next();
+
+				String fooData = this.rs.getString(column);
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS foo");
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS bar");
+			}
+		}
+	}
+
+	/**
+	 * Tests for fix to BUG#1130
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testClobTruncate() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testClobTruncate");
+			this.stmt
+					.executeUpdate("CREATE TABLE testClobTruncate (field1 TEXT)");
+			this.stmt
+					.executeUpdate("INSERT INTO testClobTruncate VALUES ('abcdefg')");
+
+			this.rs = this.stmt.executeQuery("SELECT * FROM testClobTruncate");
+			this.rs.next();
+
+			Clob clob = this.rs.getClob(1);
+			clob.truncate(3);
+
+			Reader reader = clob.getCharacterStream();
+			char[] buf = new char[8];
+			int charsRead = reader.read(buf);
+
+			String clobAsString = new String(buf, 0, charsRead);
+
+			assertTrue(clobAsString.equals("abc"));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testClobTruncate");
+		}
+	}
+
+	/**
+	 * Tests that streaming result sets are registered correctly.
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testClobberStreamingRS() throws Exception {
+		try {
+			Properties props = new Properties();
+			props.setProperty("clobberStreamingResults", "true");
+
+			Connection clobberConn = getConnectionWithProps(props);
+
+			Statement clobberStmt = clobberConn.createStatement();
+
+			clobberStmt.executeUpdate("DROP TABLE IF EXISTS StreamingClobber");
+			clobberStmt
+					.executeUpdate("CREATE TABLE StreamingClobber ( DUMMYID "
+							+ " INTEGER NOT NULL, DUMMYNAME VARCHAR(32),PRIMARY KEY (DUMMYID) )");
+			clobberStmt
+					.executeUpdate("INSERT INTO StreamingClobber (DUMMYID, DUMMYNAME) VALUES (0, NULL)");
+			clobberStmt
+					.executeUpdate("INSERT INTO StreamingClobber (DUMMYID, DUMMYNAME) VALUES (1, 'nro 1')");
+			clobberStmt
+					.executeUpdate("INSERT INTO StreamingClobber (DUMMYID, DUMMYNAME) VALUES (2, 'nro 2')");
+			clobberStmt
+					.executeUpdate("INSERT INTO StreamingClobber (DUMMYID, DUMMYNAME) VALUES (3, 'nro 3')");
+
+			Statement streamStmt = null;
+
+			try {
+				streamStmt = clobberConn.createStatement(
+						java.sql.ResultSet.TYPE_FORWARD_ONLY,
+						java.sql.ResultSet.CONCUR_READ_ONLY);
+				streamStmt.setFetchSize(Integer.MIN_VALUE);
+
+				this.rs = streamStmt.executeQuery("SELECT DUMMYID, DUMMYNAME "
+						+ "FROM StreamingClobber ORDER BY DUMMYID");
+
+				this.rs.next();
+
+				// This should proceed normally, after the driver
+				// clears the input stream
+				clobberStmt.executeQuery("SHOW VARIABLES");
+				this.rs.close();
+			} finally {
+				if (streamStmt != null) {
+					streamStmt.close();
+				}
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS StreamingClobber");
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testEmptyResultSetGet() throws Exception {
+		try {
+			this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'foo'");
+			System.out.println(this.rs.getInt(1));
+		} catch (SQLException sqlEx) {
+			assertTrue("Correct exception not thrown",
+					SQLError.SQL_STATE_GENERAL_ERROR
+							.equals(sqlEx.getSQLState()));
+		}
+	}
+
+	/**
+	 * Checks fix for BUG#1592 -- cross-database updatable result sets are not
+	 * checked for updatability correctly.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testFixForBug1592() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			Statement updatableStmt = this.conn
+					.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+
+			try {
+				updatableStmt.execute("SELECT * FROM mysql.user");
+
+				this.rs = updatableStmt.getResultSet();
+			} catch (SQLException sqlEx) {
+				String message = sqlEx.getMessage();
+
+				if ((message != null) && (message.indexOf("denied") != -1)) {
+					System.err
+							.println("WARN: Can't complete testFixForBug1592(), access to"
+									+ " 'mysql' database not allowed");
+				} else {
+					throw sqlEx;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#2006, where 2 columns with same name in a result set
+	 * are returned via findColumn() in the wrong order...The JDBC spec states,
+	 * that the _first_ matching column should be returned.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testFixForBug2006() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testFixForBug2006_1");
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testFixForBug2006_2");
+			this.stmt
+					.executeUpdate("CREATE TABLE testFixForBug2006_1 (key_field INT NOT NULL)");
+			this.stmt
+					.executeUpdate("CREATE TABLE testFixForBug2006_2 (key_field INT NULL)");
+			this.stmt
+					.executeUpdate("INSERT INTO testFixForBug2006_1 VALUES (1)");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT testFixForBug2006_1.key_field, testFixForBug2006_2.key_field FROM testFixForBug2006_1 LEFT JOIN testFixForBug2006_2 USING(key_field)");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			assertTrue(rsmd.getColumnName(1).equals(rsmd.getColumnName(2)));
+			assertTrue(rsmd.isNullable(this.rs.findColumn("key_field")) == ResultSetMetaData.columnNoNulls);
+			assertTrue(rsmd.isNullable(2) == ResultSetMetaData.columnNullable);
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getObject(1) != null);
+			assertTrue(this.rs.getObject(2) == null);
+		} finally {
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (SQLException sqlEx) {
+					// ignore
+				}
+
+				this.rs = null;
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testFixForBug2006_1");
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testFixForBug2006_2");
+		}
+	}
+
+	/**
+	 * Tests that ResultSet.getLong() does not truncate values.
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testGetLongBug() throws Exception {
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS getLongBug");
+		this.stmt
+				.executeUpdate("CREATE TABLE IF NOT EXISTS getLongBug (int_col int, bigint_col bigint)");
+
+		int intVal = 123456;
+		long longVal1 = 123456789012345678L;
+		long longVal2 = -2079305757640172711L;
+		this.stmt.executeUpdate("INSERT INTO getLongBug "
+				+ "(int_col, bigint_col) " + "VALUES (" + intVal + ", "
+				+ longVal1 + "), " + "(" + intVal + ", " + longVal2 + ")");
+
+		try {
+			this.rs = this.stmt
+					.executeQuery("SELECT int_col, bigint_col FROM getLongBug ORDER BY bigint_col DESC");
+			this.rs.next();
+			assertTrue(
+					"Values not decoded correctly",
+					((this.rs.getInt(1) == intVal) && (this.rs.getLong(2) == longVal1)));
+			this.rs.next();
+			assertTrue(
+					"Values not decoded correctly",
+					((this.rs.getInt(1) == intVal) && (this.rs.getLong(2) == longVal2)));
+		} finally {
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (Exception ex) {
+					// ignore
+				}
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS getLongBug");
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testGetTimestampWithDate() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testGetTimestamp");
+			this.stmt.executeUpdate("CREATE TABLE testGetTimestamp (d date)");
+			this.stmt
+					.executeUpdate("INSERT INTO testGetTimestamp values (now())");
+
+			this.rs = this.stmt.executeQuery("SELECT * FROM testGetTimestamp");
+			this.rs.next();
+			System.out.println(this.rs.getTimestamp(1));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testGetTimestamp");
+		}
+	}
+
+	/**
+	 * Tests a bug where ResultSet.isBefireFirst() would return true when the
+	 * result set was empty (which is incorrect)
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testIsBeforeFirstOnEmpty() throws Exception {
+		try {
+			// Query with valid rows: isBeforeFirst() correctly returns True
+			this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'version'");
+			assertTrue("Non-empty search should return true", this.rs
+					.isBeforeFirst());
+
+			// Query with empty result: isBeforeFirst() falsely returns True
+			// Sun's documentation says it should return false
+			this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'garbage'");
+			assertTrue("Empty search should return false ", !this.rs
+					.isBeforeFirst());
+		} finally {
+			this.rs.close();
+		}
+	}
+
+	/**
+	 * Tests a bug where ResultSet.isBefireFirst() would return true when the
+	 * result set was empty (which is incorrect)
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testMetaDataIsWritable() throws Exception {
+		try {
+			// Query with valid rows
+			this.rs = this.stmt.executeQuery("SHOW VARIABLES LIKE 'version'");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			int numColumns = rsmd.getColumnCount();
+
+			for (int i = 1; i <= numColumns; i++) {
+				assertTrue("rsmd.isWritable() should != rsmd.isReadOnly()",
+						rsmd.isWritable(i) != rsmd.isReadOnly(i));
+			}
+		} finally {
+			this.rs.close();
+		}
+	}
+
+	/**
+	 * Tests fix for bug # 496
+	 * 
+	 * @throws Exception
+	 *             if an error happens.
+	 */
+	public void testNextAndPrevious() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testNextAndPrevious");
+			this.stmt
+					.executeUpdate("CREATE TABLE testNextAndPrevious (field1 int)");
+			this.stmt
+					.executeUpdate("INSERT INTO testNextAndPrevious VALUES (1)");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT * from testNextAndPrevious");
+
+			System.out.println("Currently at row " + this.rs.getRow());
+			this.rs.next();
+			System.out.println("Value at row " + this.rs.getRow() + " is "
+					+ this.rs.getString(1));
+
+			this.rs.previous();
+
+			try {
+				System.out.println("Value at row " + this.rs.getRow() + " is "
+						+ this.rs.getString(1));
+				fail("Should not be able to retrieve values with invalid cursor");
+			} catch (SQLException sqlEx) {
+				assertTrue(sqlEx.getMessage().startsWith("Before start"));
+			}
+
+			this.rs.next();
+
+			this.rs.next();
+
+			try {
+				System.out.println("Value at row " + this.rs.getRow() + " is "
+						+ this.rs.getString(1));
+				fail("Should not be able to retrieve values with invalid cursor");
+			} catch (SQLException sqlEx) {
+				assertTrue(sqlEx.getMessage().startsWith("After end"));
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testNextAndPrevious");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#1630 (not updatable exception turning into NPE on
+	 * second updateFoo() method call.
+	 * 
+	 * @throws Exception
+	 *             if an unexpected exception is thrown.
+	 */
+	public void testNotUpdatable() throws Exception {
+		this.rs = null;
+
+		try {
+			String sQuery = "SHOW VARIABLES";
+			this.pstmt = this.conn
+					.prepareStatement(sQuery, ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+
+			this.rs = this.pstmt.executeQuery();
+
+			if (this.rs.next()) {
+				this.rs.absolute(1);
+
+				try {
+					this.rs.updateInt(1, 1);
+				} catch (SQLException sqlEx) {
+					assertTrue(sqlEx instanceof NotUpdatable);
+				}
+
+				try {
+					this.rs.updateString(1, "1");
+				} catch (SQLException sqlEx) {
+					assertTrue(sqlEx instanceof NotUpdatable);
+				}
+			}
+		} finally {
+			if (this.pstmt != null) {
+				try {
+					this.pstmt.close();
+				} catch (Exception e) {
+					// ignore
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests that streaming result sets are registered correctly.
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testStreamingRegBug() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS StreamingRegBug");
+			this.stmt
+					.executeUpdate("CREATE TABLE StreamingRegBug ( DUMMYID "
+							+ " INTEGER NOT NULL, DUMMYNAME VARCHAR(32),PRIMARY KEY (DUMMYID) )");
+			this.stmt
+					.executeUpdate("INSERT INTO StreamingRegBug (DUMMYID, DUMMYNAME) VALUES (0, NULL)");
+			this.stmt
+					.executeUpdate("INSERT INTO StreamingRegBug (DUMMYID, DUMMYNAME) VALUES (1, 'nro 1')");
+			this.stmt
+					.executeUpdate("INSERT INTO StreamingRegBug (DUMMYID, DUMMYNAME) VALUES (2, 'nro 2')");
+			this.stmt
+					.executeUpdate("INSERT INTO StreamingRegBug (DUMMYID, DUMMYNAME) VALUES (3, 'nro 3')");
+
+			PreparedStatement streamStmt = null;
+
+			try {
+				streamStmt = this.conn.prepareStatement(
+						"SELECT DUMMYID, DUMMYNAME "
+								+ "FROM StreamingRegBug ORDER BY DUMMYID",
+						java.sql.ResultSet.TYPE_FORWARD_ONLY,
+						java.sql.ResultSet.CONCUR_READ_ONLY);
+				streamStmt.setFetchSize(Integer.MIN_VALUE);
+
+				this.rs = streamStmt.executeQuery();
+
+				while (this.rs.next()) {
+					this.rs.getString(1);
+				}
+
+				this.rs.close(); // error occurs here
+			} catch (SQLException sqlEx) {
+
+			} finally {
+				if (streamStmt != null) {
+					try {
+						streamStmt.close();
+					} catch (SQLException exWhileClose) {
+						exWhileClose.printStackTrace();
+					}
+				}
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS StreamingRegBug");
+		}
+	}
+
+	/**
+	 * Tests that result sets can be updated when all parameters are correctly
+	 * set.
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testUpdatability() throws Exception {
+		this.rs = null;
+
+		this.stmt.execute("DROP TABLE IF EXISTS updatabilityBug");
+		this.stmt.execute("CREATE TABLE IF NOT EXISTS updatabilityBug ("
+				+ " id int(10) unsigned NOT NULL auto_increment,"
+				+ " field1 varchar(32) NOT NULL default '',"
+				+ " field2 varchar(128) NOT NULL default '',"
+				+ " field3 varchar(128) default NULL,"
+				+ " field4 varchar(128) default NULL,"
+				+ " field5 varchar(64) default NULL,"
+				+ " field6 int(10) unsigned default NULL,"
+				+ " field7 varchar(64) default NULL," + " PRIMARY KEY  (id)"
+				+ ") TYPE=InnoDB;");
+		this.stmt.executeUpdate("insert into updatabilityBug (id) values (1)");
+
+		try {
+			String sQuery = " SELECT * FROM updatabilityBug WHERE id = ? ";
+			this.pstmt = this.conn
+					.prepareStatement(sQuery, ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+			this.conn.setAutoCommit(false);
+			this.pstmt.setInt(1, 1);
+			this.rs = this.pstmt.executeQuery();
+
+			if (this.rs.next()) {
+				this.rs.absolute(1);
+				this.rs.updateInt("id", 1);
+				this.rs.updateString("field1", "1");
+				this.rs.updateString("field2", "1");
+				this.rs.updateString("field3", "1");
+				this.rs.updateString("field4", "1");
+				this.rs.updateString("field5", "1");
+				this.rs.updateInt("field6", 1);
+				this.rs.updateString("field7", "1");
+				this.rs.updateRow();
+			}
+
+			this.conn.commit();
+			this.conn.setAutoCommit(true);
+		} finally {
+			if (this.pstmt != null) {
+				try {
+					this.pstmt.close();
+				} catch (Exception e) {
+					// ignore
+				}
+			}
+
+			this.stmt.execute("DROP TABLE IF EXISTS updatabilityBug");
+		}
+	}
+
+	/**
+	 * Test fixes for BUG#1071
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testUpdatabilityAndEscaping() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("useUnicode", "true");
+		props.setProperty("characterEncoding", "big5");
+
+		Connection updConn = getConnectionWithProps(props);
+		Statement updStmt = updConn.createStatement(
+				ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
+
+		try {
+			updStmt
+					.executeUpdate("DROP TABLE IF EXISTS testUpdatesWithEscaping");
+			updStmt
+					.executeUpdate("CREATE TABLE testUpdatesWithEscaping (field1 INT PRIMARY KEY, field2 VARCHAR(64))");
+			updStmt
+					.executeUpdate("INSERT INTO testUpdatesWithEscaping VALUES (1, null)");
+
+			String stringToUpdate = "\" \\ '";
+
+			this.rs = updStmt
+					.executeQuery("SELECT * from testUpdatesWithEscaping");
+
+			this.rs.next();
+			this.rs.updateString(2, stringToUpdate);
+			this.rs.updateRow();
+
+			assertTrue(stringToUpdate.equals(this.rs.getString(2)));
+		} finally {
+			updStmt
+					.executeUpdate("DROP TABLE IF EXISTS testUpdatesWithEscaping");
+			updStmt.close();
+			updConn.close();
+		}
+	}
+
+	/**
+	 * Tests the fix for BUG#661 ... refreshRow() fails when primary key values
+	 * have escaped data in them.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testUpdatabilityWithQuotes() throws Exception {
+		Statement updStmt = null;
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testUpdWithQuotes");
+			this.stmt
+					.executeUpdate("CREATE TABLE testUpdWithQuotes (keyField CHAR(32) PRIMARY KEY NOT NULL, field2 int)");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("INSERT INTO testUpdWithQuotes VALUES (?, ?)");
+			pStmt.setString(1, "Abe's");
+			pStmt.setInt(2, 1);
+			pStmt.executeUpdate();
+
+			updStmt = this.conn
+					.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+
+			this.rs = updStmt.executeQuery("SELECT * FROM testUpdWithQuotes");
+			this.rs.next();
+			this.rs.updateInt(2, 2);
+			this.rs.updateRow();
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testUpdWithQuotes");
+
+			if (this.rs != null) {
+				this.rs.close();
+			}
+
+			this.rs = null;
+
+			if (updStmt != null) {
+				updStmt.close();
+			}
+
+			updStmt = null;
+		}
+	}
+
+	/**
+	 * Checks whether or not ResultSet.updateClob() is implemented
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testUpdateClob() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		Statement updatableStmt = this.conn.createStatement(
+				ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testUpdateClob");
+			this.stmt
+					.executeUpdate("CREATE TABLE testUpdateClob(intField INT NOT NULL PRIMARY KEY, clobField TEXT)");
+			this.stmt
+					.executeUpdate("INSERT INTO testUpdateClob VALUES (1, 'foo')");
+
+			this.rs = updatableStmt
+					.executeQuery("SELECT intField, clobField FROM testUpdateClob");
+			this.rs.next();
+
+			Clob clob = this.rs.getClob(2);
+
+			clob.setString(1, "bar");
+
+			this.rs.updateClob(2, clob);
+			this.rs.updateRow();
+
+			this.rs.moveToInsertRow();
+
+			clob.setString(1, "baz");
+			this.rs.updateInt(1, 2);
+			this.rs.updateClob(2, clob);
+			this.rs.insertRow();
+
+			clob.setString(1, "bat");
+			this.rs.updateInt(1, 3);
+			this.rs.updateClob(2, clob);
+			this.rs.insertRow();
+
+			this.rs.close();
+
+			this.rs = this.stmt
+					.executeQuery("SELECT intField, clobField FROM testUpdateClob ORDER BY intField");
+
+			this.rs.next();
+			assertTrue((this.rs.getInt(1) == 1)
+					&& this.rs.getString(2).equals("bar"));
+
+			this.rs.next();
+			assertTrue((this.rs.getInt(1) == 2)
+					&& this.rs.getString(2).equals("baz"));
+
+			this.rs.next();
+			assertTrue((this.rs.getInt(1) == 3)
+					&& this.rs.getString(2).equals("bat"));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testUpdateClob");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#4482, ResultSet.getObject() returns wrong type for
+	 * strings when using prepared statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug4482() throws Exception {
+		this.rs = this.conn.prepareStatement("SELECT 'abcdef'").executeQuery();
+		assertTrue(this.rs.next());
+		assertTrue(this.rs.getObject(1) instanceof String);
+	}
+
+	/**
+	 * Test fix for BUG#4689 - WasNull not getting set correctly for binary
+	 * result sets.
+	 */
+	public void testBug4689() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4689");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug4689 (tinyintField tinyint, tinyintFieldNull tinyint, "
+							+ "intField int, intFieldNull int, "
+							+ "bigintField bigint, bigintFieldNull bigint, "
+							+ "shortField smallint, shortFieldNull smallint, "
+							+ "doubleField double, doubleFieldNull double)");
+
+			this.stmt.executeUpdate("INSERT INTO testBug4689 VALUES (1, null, "
+					+ "1, null, " + "1, null, " + "1, null, " + "1, null)");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("SELECT tinyintField, tinyintFieldNull,"
+							+ "intField, intFieldNull, "
+							+ "bigintField, bigintFieldNull, "
+							+ "shortField, shortFieldNull, "
+							+ "doubleField, doubleFieldNull FROM testBug4689");
+			this.rs = pStmt.executeQuery();
+			assertTrue(this.rs.next());
+
+			assertTrue(this.rs.getByte(1) == 1);
+			assertTrue(this.rs.wasNull() == false);
+			assertTrue(this.rs.getByte(2) == 0);
+			assertTrue(this.rs.wasNull() == true);
+
+			assertTrue(this.rs.getInt(3) == 1);
+			assertTrue(this.rs.wasNull() == false);
+			assertTrue(this.rs.getInt(4) == 0);
+			assertTrue(this.rs.wasNull() == true);
+
+			assertTrue(this.rs.getInt(5) == 1);
+			assertTrue(this.rs.wasNull() == false);
+			assertTrue(this.rs.getInt(6) == 0);
+			assertTrue(this.rs.wasNull() == true);
+
+			assertTrue(this.rs.getShort(7) == 1);
+			assertTrue(this.rs.wasNull() == false);
+			assertTrue(this.rs.getShort(8) == 0);
+			assertTrue(this.rs.wasNull() == true);
+
+			assertTrue(this.rs.getDouble(9) == 1);
+			assertTrue(this.rs.wasNull() == false);
+			assertTrue(this.rs.getDouble(10) == 0);
+			assertTrue(this.rs.wasNull() == true);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4689");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#5032 -- ResultSet.getObject() doesn't return type
+	 * Boolean for pseudo-bit types from prepared statements on 4.1.x.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug5032() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			PreparedStatement pStmt = null;
+
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5032");
+				this.stmt.executeUpdate("CREATE TABLE testBug5032(field1 BIT)");
+				this.stmt.executeUpdate("INSERT INTO testBug5032 VALUES (1)");
+
+				pStmt = this.conn
+						.prepareStatement("SELECT field1 FROM testBug5032");
+				this.rs = pStmt.executeQuery();
+				assertTrue(this.rs.next());
+				assertTrue(this.rs.getObject(1) instanceof Boolean);
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5032");
+
+				if (pStmt != null) {
+					pStmt.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#5069 -- ResultSet.getMetaData() should not return
+	 * incorrectly-initialized metadata if the result set has been closed, but
+	 * should instead throw a SQLException. Also tests fix for getRow() and
+	 * getWarnings() and traversal methods.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug5069() throws Exception {
+		try {
+			this.rs = this.stmt.executeQuery("SELECT 1");
+			this.rs.close();
+
+			try {
+				ResultSetMetaData md = this.rs.getMetaData();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.getRow();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.getWarnings();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.first();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.beforeFirst();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.last();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.afterLast();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.relative(0);
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.next();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.previous();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.isBeforeFirst();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.isFirst();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.isAfterLast();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.isLast();
+			} catch (NullPointerException npEx) {
+				fail("Should not catch NullPointerException here");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_GENERAL_ERROR.equals(sqlEx
+						.getSQLState()));
+			}
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests for BUG#5235, ClassCastException on all-zero date field when
+	 * zeroDatetimeBehavior is 'convertToNull'...however it appears that this
+	 * bug doesn't exist. This is a placeholder until we get more data from the
+	 * user on how they provoke this bug to happen.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug5235() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5235");
+			this.stmt.executeUpdate("CREATE TABLE testBug5235(field1 DATE)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug5235 (field1) VALUES ('0000-00-00')");
+
+			Properties props = new Properties();
+			props.setProperty("zeroDateTimeBehavior", "convertToNull");
+
+			Connection nullConn = getConnectionWithProps(props);
+
+			this.rs = nullConn.createStatement().executeQuery(
+					"SELECT field1 FROM testBug5235");
+			this.rs.next();
+			assertTrue(null == this.rs.getObject(1));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5235");
+		}
+	}
+
+	/**
+	 * Tests for BUG#5136, GEOMETRY types getting corrupted, turns out to be a
+	 * server bug.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug5136() throws Exception {
+		if (false) {
+			PreparedStatement toGeom = this.conn
+					.prepareStatement("select GeomFromText(?)");
+			PreparedStatement toText = this.conn
+					.prepareStatement("select AsText(?)");
+
+			String inText = "POINT(146.67596278 -36.54368233)";
+
+			// First assert that the problem is not at the server end
+			this.rs = this.stmt.executeQuery("select AsText(GeomFromText('"
+					+ inText + "'))");
+			this.rs.next();
+
+			String outText = this.rs.getString(1);
+			this.rs.close();
+			assertTrue(
+					"Server side only\n In: " + inText + "\nOut: " + outText,
+					inText.equals(outText));
+
+			// Now bring a binary geometry object to the client and send it back
+			toGeom.setString(1, inText);
+			this.rs = toGeom.executeQuery();
+			this.rs.next();
+
+			// Return a binary geometry object from the WKT
+			Object geom = this.rs.getObject(1);
+			this.rs.close();
+			toText.setObject(1, geom);
+			this.rs = toText.executeQuery();
+			this.rs.next();
+
+			// Return WKT from the binary geometry
+			outText = this.rs.getString(1);
+			this.rs.close();
+			assertTrue("Server to client and back\n In: " + inText + "\nOut: "
+					+ outText, inText.equals(outText));
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#5664, ResultSet.updateByte() when on insert row throws
+	 * ArrayOutOfBoundsException.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug5664() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5664");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug5664 (pkfield int PRIMARY KEY NOT NULL, field1 SMALLINT)");
+			this.stmt.executeUpdate("INSERT INTO testBug5664 VALUES (1, 1)");
+
+			Statement updatableStmt = this.conn
+					.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+
+			this.rs = updatableStmt
+					.executeQuery("SELECT pkfield, field1 FROM testBug5664");
+			this.rs.next();
+			this.rs.moveToInsertRow();
+			this.rs.updateInt(1, 2);
+			this.rs.updateByte(2, (byte) 2);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5664");
+		}
+	}
+
+	public void testBogusTimestampAsString() throws Exception {
+
+		this.rs = this.stmt.executeQuery("SELECT '2004-08-13 13:21:17.'");
+
+		this.rs.next();
+
+		// We're only checking for an exception being thrown here as the bug
+		this.rs.getTimestamp(1);
+
+	}
+
+	/**
+	 * Tests our ability to reject NaN and +/- INF in
+	 * PreparedStatement.setDouble();
+	 */
+	public void testBug5717() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5717");
+			this.stmt.executeUpdate("CREATE TABLE testBug5717 (field1 DOUBLE)");
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO testBug5717 VALUES (?)");
+
+			try {
+				this.pstmt.setDouble(1, Double.NEGATIVE_INFINITY);
+				fail("Exception should've been thrown");
+			} catch (Exception ex) {
+				// expected
+			}
+
+			try {
+				this.pstmt.setDouble(1, Double.POSITIVE_INFINITY);
+				fail("Exception should've been thrown");
+			} catch (Exception ex) {
+				// expected
+			}
+
+			try {
+				this.pstmt.setDouble(1, Double.NaN);
+				fail("Exception should've been thrown");
+			} catch (Exception ex) {
+				// expected
+			}
+		} finally {
+			if (this.pstmt != null) {
+				this.pstmt.close();
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5717");
+		}
+	}
+
+	/**
+	 * Tests fix for server issue that drops precision on aggregate operations
+	 * on DECIMAL types, because they come back as DOUBLEs.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug6537() throws Exception {
+		if (versionMeetsMinimum(4, 1, 0)) {
+			String tableName = "testBug6537";
+
+			try {
+				createTable(
+						tableName,
+						"(`id` int(11) NOT NULL default '0',"
+								+ "`value` decimal(10,2) NOT NULL default '0.00', `stringval` varchar(10),"
+								+ "PRIMARY KEY  (`id`)"
+								+ ") ENGINE=MyISAM DEFAULT CHARSET=latin1");
+				this.stmt
+						.executeUpdate("INSERT INTO "
+								+ tableName
+								+ "(id, value, stringval) VALUES (1, 100.00, '100.00'), (2, 200, '200')");
+
+				String sql = "SELECT SUM(value) as total FROM " + tableName
+						+ " WHERE id = ? ";
+				PreparedStatement pStmt = this.conn.prepareStatement(sql);
+				pStmt.setInt(1, 1);
+				this.rs = pStmt.executeQuery();
+				assertTrue(this.rs.next());
+
+				assertTrue("100.00".equals(this.rs.getBigDecimal("total")
+						.toString()));
+
+				sql = "SELECT stringval as total FROM " + tableName
+						+ " WHERE id = ? ";
+				pStmt = this.conn.prepareStatement(sql);
+				pStmt.setInt(1, 2);
+				this.rs = pStmt.executeQuery();
+				assertTrue(this.rs.next());
+
+				assertTrue("200.00".equals(this.rs.getBigDecimal("total", 2)
+						.toString()));
+
+			} finally {
+				dropTable(tableName);
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#6231, ResultSet.getTimestamp() on a column with TIME in
+	 * it fails.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug6231() throws Exception {
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6231");
+			this.stmt.executeUpdate("CREATE TABLE testBug6231 (field1 TIME)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug6231 VALUES ('09:16:00')");
+
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug6231");
+			this.rs.next();
+			long asMillis = this.rs.getTimestamp(1).getTime();
+			Calendar cal = Calendar.getInstance();
+
+			if (isRunningOnJdk131()) {
+				cal.setTime(new Date(asMillis));
+			} else {
+				cal.setTimeInMillis(asMillis);
+			}
+
+			assertEquals(9, cal.get(Calendar.HOUR));
+			assertEquals(16, cal.get(Calendar.MINUTE));
+			assertEquals(0, cal.get(Calendar.SECOND));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6231");
+		}
+	}
+
+	public void testBug6619() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6619");
+			this.stmt.executeUpdate("CREATE TABLE testBug6619 (field1 int)");
+			this.stmt.executeUpdate("INSERT INTO testBug6619 VALUES (1), (2)");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("SELECT SUM(field1) FROM testBug6619");
+
+			this.rs = pStmt.executeQuery();
+			this.rs.next();
+			System.out.println(this.rs.getString(1));
+
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6619");
+		}
+	}
+
+	public void testBug6743() throws Exception {
+		// 0x835C U+30BD # KATAKANA LETTER SO
+		String katakanaStr = "\u30BD";
+
+		Properties props = new Properties();
+
+		props.setProperty("useUnicode", "true");
+		props.setProperty("characterEncoding", "SJIS");
+
+		Connection sjisConn = null;
+		Statement sjisStmt = null;
+
+		try {
+			sjisConn = getConnectionWithProps(props);
+			sjisStmt = sjisConn.createStatement(
+					ResultSet.TYPE_SCROLL_INSENSITIVE,
+					ResultSet.CONCUR_UPDATABLE);
+
+			sjisStmt.executeUpdate("DROP TABLE IF EXISTS testBug6743");
+			StringBuffer queryBuf = new StringBuffer(
+					"CREATE TABLE testBug6743 (pkField INT NOT NULL PRIMARY KEY, field1 VARCHAR(32)");
+
+			if (versionMeetsMinimum(4, 1)) {
+				queryBuf.append(" CHARACTER SET SJIS");
+			}
+
+			queryBuf.append(")");
+			sjisStmt.executeUpdate(queryBuf.toString());
+			sjisStmt.executeUpdate("INSERT INTO testBug6743 VALUES (1, 'abc')");
+
+			this.rs = sjisStmt
+					.executeQuery("SELECT pkField, field1 FROM testBug6743");
+			this.rs.next();
+			this.rs.updateString(2, katakanaStr);
+			this.rs.updateRow();
+
+			String retrString = this.rs.getString(2);
+			assertTrue(katakanaStr.equals(retrString));
+
+			this.rs = sjisStmt
+					.executeQuery("SELECT pkField, field1 FROM testBug6743");
+			this.rs.next();
+
+			retrString = this.rs.getString(2);
+			assertTrue(katakanaStr.equals(retrString));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6743");
+
+			if (sjisStmt != null) {
+				sjisStmt.close();
+			}
+
+			if (sjisConn != null) {
+				sjisConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests for presence of BUG#6561, NPE thrown when dealing with 0 dates and
+	 * non-unpacked result sets.
+	 * 
+	 * @throws Exception
+	 *             if the test occurs.
+	 */
+	public void testBug6561() throws Exception {
+
+		try {
+			Properties props = new Properties();
+			props.setProperty("zeroDateTimeBehavior", "convertToNull");
+
+			Connection zeroConn = getConnectionWithProps(props);
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug6561");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug6561 (ofield int, field1 DATE, field2 integer, field3 integer)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug6561 (ofield, field1,field2,field3)	VALUES (1, 0,NULL,0)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug6561 (ofield, field1,field2,field3) VALUES (2, '2004-11-20',NULL,0)");
+
+			PreparedStatement ps = zeroConn
+					.prepareStatement("SELECT field1,field2,field3 FROM testBug6561 ORDER BY ofield");
+			this.rs = ps.executeQuery();
+
+			assertTrue(this.rs.next());
+			assertTrue(null == this.rs.getObject("field1"));
+			assertTrue(null == this.rs.getObject("field2"));
+			assertTrue(0 == this.rs.getInt("field3"));
+
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getString("field1").equals("2004-11-20"));
+			assertTrue(null == this.rs.getObject("field2"));
+			assertTrue(0 == this.rs.getInt("field3"));
+
+			ps.close();
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS test");
+		}
+	}
+
+	public void testBug7686() throws SQLException {
+		String tableName = "testBug7686";
+		createTable(tableName, "(id1 int(10) unsigned NOT NULL,"
+				+ " id2 DATETIME, "
+				+ " field1 varchar(128) NOT NULL default '',"
+				+ " PRIMARY KEY  (id1, id2)) TYPE=InnoDB;");
+
+		this.stmt.executeUpdate("insert into " + tableName
+				+ " (id1, id2, field1)"
+				+ " values (1, '2005-01-05 13:59:20', 'foo')");
+
+		String sQuery = " SELECT * FROM " + tableName
+				+ " WHERE id1 = ? AND id2 = ?";
+		this.pstmt = this.conn.prepareStatement(sQuery,
+				ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
+
+		this.conn.setAutoCommit(false);
+		this.pstmt.setInt(1, 1);
+		GregorianCalendar cal = new GregorianCalendar();
+		cal.clear();
+		cal.set(2005, 00, 05, 13, 59, 20);
+
+		Timestamp jan5before2pm = null;
+
+		if (isRunningOnJdk131()) {
+			jan5before2pm = new java.sql.Timestamp(cal.getTime().getTime());
+		} else {
+			jan5before2pm = new java.sql.Timestamp(cal.getTimeInMillis());
+		}
+
+		this.pstmt.setTimestamp(2, jan5before2pm);
+		this.rs = this.pstmt.executeQuery();
+		assertTrue(this.rs.next());
+		this.rs.absolute(1);
+		this.rs.updateString("field1", "bar");
+		this.rs.updateRow();
+		this.conn.commit();
+		this.conn.setAutoCommit(true);
+	}
+
+	/**
+	 * Tests fix for BUG#7715 - Timestamps converted incorrectly to strings with
+	 * SSPS and Upd. Result Sets.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug7715() throws Exception {
+		PreparedStatement pStmt = null;
+
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testConvertedBinaryTimestamp");
+			this.stmt
+					.executeUpdate("CREATE TABLE testConvertedBinaryTimestamp (field1 VARCHAR(32), field2 VARCHAR(32), field3 VARCHAR(32), field4 TIMESTAMP)");
+			this.stmt
+					.executeUpdate("INSERT INTO testConvertedBinaryTimestamp VALUES ('abc', 'def', 'ghi', NOW())");
+
+			pStmt = this.conn
+					.prepareStatement(
+							"SELECT field1, field2, field3, field4 FROM testConvertedBinaryTimestamp",
+							ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+
+			this.rs = pStmt.executeQuery();
+			assertTrue(this.rs.next());
+
+			this.rs.getObject(4); // fails if bug exists
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testConvertedBinaryTimestamp");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#8428 - getString() doesn't maintain format stored on
+	 * server.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug8428() throws Exception {
+		Connection noSyncConn = null;
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8428");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug8428 (field1 YEAR, field2 DATETIME)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug8428 VALUES ('1999', '2005-02-11 12:54:41')");
+
+			Properties props = new Properties();
+			props.setProperty("noDatetimeStringSync", "true");
+			props.setProperty("useUsageAdvisor", "true");
+			props.setProperty("yearIsDateType", "false"); // for 3.1.9+
+
+			noSyncConn = getConnectionWithProps(props);
+
+			this.rs = noSyncConn.createStatement().executeQuery(
+					"SELECT field1, field2 FROM testBug8428");
+			this.rs.next();
+			assertEquals("1999", this.rs.getString(1));
+			assertEquals("2005-02-11 12:54:41", this.rs.getString(2));
+
+			this.rs = noSyncConn.prepareStatement(
+					"SELECT field1, field2 FROM testBug8428").executeQuery();
+			this.rs.next();
+			assertEquals("1999", this.rs.getString(1));
+			assertEquals("2005-02-11 12:54:41", this.rs.getString(2));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8428");
+		}
+	}
+
+	/**
+	 * Tests fix for Bug#8868, DATE_FORMAT() queries returned as BLOBs from
+	 * getObject().
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug8868() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			createTable("testBug8868",
+					"(field1 DATE, field2 VARCHAR(32) CHARACTER SET BINARY)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug8868 VALUES (NOW(), 'abcd')");
+			try {
+				this.rs = this.stmt
+						.executeQuery("SELECT DATE_FORMAT(field1,'%b-%e %l:%i%p') as fmtddate, field2 FROM testBug8868");
+				this.rs.next();
+				assertEquals("java.lang.String", this.rs.getObject(1)
+						.getClass().getName());
+			} finally {
+				if (this.rs != null) {
+					this.rs.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#9098 - Server doesn't give us info to distinguish
+	 * between CURRENT_TIMESTAMP and 'CURRENT_TIMESTAMP' for default values.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug9098() throws Exception {
+		if (versionMeetsMinimum(4, 1, 10)) {
+			Statement updatableStmt = null;
+
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug9098");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBug9098(pkfield INT PRIMARY KEY NOT NULL AUTO_INCREMENT, \n"
+								+ "tsfield TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, tsfield2 TIMESTAMP NOT NULL DEFAULT '2005-12-25 12:20:52', charfield VARCHAR(4) NOT NULL DEFAULT 'abcd')");
+				updatableStmt = this.conn.createStatement(
+						ResultSet.TYPE_SCROLL_INSENSITIVE,
+						ResultSet.CONCUR_UPDATABLE);
+				this.rs = updatableStmt
+						.executeQuery("SELECT pkfield, tsfield, tsfield2, charfield FROM testBug9098");
+				this.rs.moveToInsertRow();
+				this.rs.insertRow();
+
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug9098");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for 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)
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug9236() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			try {
+				createTable(
+						"testBug9236",
+						"("
+								+ "field_1 int(18) NOT NULL auto_increment,"
+								+ "field_2 varchar(50) NOT NULL default '',"
+								+ "field_3 varchar(12) default NULL,"
+								+ "field_4 int(18) default NULL,"
+								+ "field_5 int(18) default NULL,"
+								+ "field_6 datetime default NULL,"
+								+ "field_7 varchar(30) default NULL,"
+								+ "field_8 varchar(50) default NULL,"
+								+ "field_9 datetime default NULL,"
+								+ "field_10 int(18) NOT NULL default '0',"
+								+ "field_11 int(18) default NULL,"
+								+ "field_12 datetime NOT NULL default '0000-00-00 00:00:00',"
+								+ "PRIMARY KEY  (field_1)," + "KEY (field_4),"
+								+ "KEY (field_2)," + "KEY (field_3),"
+								+ "KEY (field_7,field_1)," + "KEY (field_5),"
+								+ "KEY (field_6,field_10,field_9),"
+								+ "KEY (field_11,field_10),"
+								+ "KEY (field_12,field_10)"
+								+ ") ENGINE=InnoDB DEFAULT CHARSET=latin1");
+
+				this.stmt
+						.executeUpdate("INSERT INTO testBug9236 VALUES "
+								+ "(1,'0',NULL,-1,0,'0000-00-00 00:00:00','123456789','-1','2004-03-13 14:21:38',0,NULL,'2004-03-13 14:21:38'),"
+								+ "(2,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'1',NULL,0,NULL,'2004-07-13 14:29:52'),"
+								+ "(3,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'2',NULL,0,NULL,'2004-07-16 13:20:51'),"
+								+ "(4,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'3','2004-07-16 13:43:39',0,NULL,'2004-07-16 13:22:01'),"
+								+ "(5,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'4','2004-07-16 13:23:48',0,NULL,'2004-07-16 13:23:01'),"
+								+ "(6,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'5',NULL,0,NULL,'2004-07-16 14:41:07'),"
+								+ "(7,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'6',NULL,0,NULL,'2004-07-16 14:41:34'),"
+								+ "(8,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'7',NULL,0,NULL,'2004-07-16 14:41:54'),"
+								+ "(9,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'8',NULL,0,NULL,'2004-07-16 14:42:42'),"
+								+ "(10,'0','PI',1,0,'0000-00-00 00:00:00',NULL,'9',NULL,0,NULL,'2004-07-18 10:51:30'),"
+								+ "(11,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'10','2004-07-23 17:23:06',0,NULL,'2004-07-23 17:18:19'),"
+								+ "(12,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'11','2004-07-23 17:24:45',0,NULL,'2004-07-23 17:23:57'),"
+								+ "(13,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'12','2004-07-23 17:30:51',0,NULL,'2004-07-23 17:30:15'),"
+								+ "(14,'0',NULL,1,0,'0000-00-00 00:00:00',NULL,'13','2004-07-26 17:50:19',0,NULL,'2004-07-26 17:49:38'),"
+								+ "(15,'0','FRL',1,0,'0000-00-00 00:00:00',NULL,'1',NULL,0,NULL,'2004-08-19 18:29:18'),"
+								+ "(16,'0','FRL',1,0,'0000-00-00 00:00:00',NULL,'15',NULL,0,NULL,'2005-03-16 12:08:28')");
+
+				createTable("testBug9236_1",
+						"(field1 CHAR(2) CHARACTER SET BINARY)");
+				this.stmt
+						.executeUpdate("INSERT INTO testBug9236_1 VALUES ('ab')");
+				this.rs = this.stmt
+						.executeQuery("SELECT field1 FROM testBug9236_1");
+
+				ResultSetMetaData rsmd = this.rs.getMetaData();
+				assertEquals("[B", rsmd.getColumnClassName(1));
+				assertTrue(this.rs.next());
+				Object asObject = this.rs.getObject(1);
+				assertEquals("[B", asObject.getClass().getName());
+
+				this.rs = this.stmt
+						.executeQuery("select DATE_FORMAT(field_12, '%Y-%m-%d') as date, count(*) as count from testBug9236 where field_10 = 0 and field_3 = 'FRL' and field_12 >= '2005-03-02 00:00:00' and field_12 <= '2005-03-17 00:00:00' group by date");
+				rsmd = this.rs.getMetaData();
+				assertEquals("java.lang.String", rsmd.getColumnClassName(1));
+				this.rs.next();
+				asObject = this.rs.getObject(1);
+				assertEquals("java.lang.String", asObject.getClass().getName());
+
+				this.rs.close();
+
+				createTable("testBug8868_2",
+						"(field1 CHAR(4) CHARACTER SET BINARY)");
+				this.stmt
+						.executeUpdate("INSERT INTO testBug8868_2 VALUES ('abc')");
+				this.rs = this.stmt
+						.executeQuery("SELECT field1 FROM testBug8868_2");
+
+				rsmd = this.rs.getMetaData();
+				assertEquals("[B", rsmd.getColumnClassName(1));
+				this.rs.next();
+				asObject = this.rs.getObject(1);
+				assertEquals("[B", asObject.getClass().getName());
+			} finally {
+				if (this.rs != null) {
+					this.rs.close();
+					this.rs = null;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#9437, IF() returns type of [B or java.lang.String
+	 * depending on platform. Fixed earlier, but in here to catch if it ever
+	 * regresses.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug9437() throws Exception {
+		String tableName = "testBug9437";
+
+		if (versionMeetsMinimum(4, 1, 0)) {
+			try {
+				createTable(
+						tableName,
+						"("
+								+ "languageCode char(2) NOT NULL default '',"
+								+ "countryCode char(2) NOT NULL default '',"
+								+ "supported enum('no','yes') NOT NULL default 'no',"
+								+ "ordering int(11) default NULL,"
+								+ "createDate datetime NOT NULL default '1000-01-01 00:00:03',"
+								+ "modifyDate timestamp NOT NULL default CURRENT_TIMESTAMP on update"
+								+ " CURRENT_TIMESTAMP,"
+								+ "PRIMARY KEY  (languageCode,countryCode),"
+								+ "KEY languageCode (languageCode),"
+								+ "KEY countryCode (countryCode),"
+								+ "KEY ordering (ordering),"
+								+ "KEY modifyDate (modifyDate)"
+								+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8");
+
+				this.stmt.executeUpdate("INSERT INTO " + tableName
+						+ " (languageCode) VALUES ('en')");
+
+				String alias = "someLocale";
+				String sql = "select if ( languageCode = ?, ?, ? ) as " + alias
+						+ " from " + tableName;
+				this.pstmt = this.conn.prepareStatement(sql);
+
+				int count = 1;
+				this.pstmt.setObject(count++, "en");
+				this.pstmt.setObject(count++, "en_US");
+				this.pstmt.setObject(count++, "en_GB");
+
+				this.rs = this.pstmt.executeQuery();
+
+				assertTrue(this.rs.next());
+
+				Object object = this.rs.getObject(alias);
+
+				if (object != null) {
+					assertEquals("java.lang.String", object.getClass()
+							.getName());
+					assertEquals("en_US", object.toString());
+				}
+
+			} finally {
+				if (this.rs != null) {
+					this.rs.close();
+					this.rs = null;
+				}
+
+				if (this.pstmt != null) {
+					this.pstmt.close();
+					this.pstmt = null;
+				}
+			}
+		}
+	}
+
+	public void testBug9684() throws Exception {
+		if (versionMeetsMinimum(4, 1, 9)) {
+			String tableName = "testBug9684";
+
+			try {
+				createTable(tableName,
+						"(sourceText text character set utf8 collate utf8_bin)");
+				this.stmt.executeUpdate("INSERT INTO " + tableName
+						+ " VALUES ('abc')");
+				this.rs = this.stmt.executeQuery("SELECT sourceText FROM "
+						+ tableName);
+				assertTrue(this.rs.next());
+				assertEquals("java.lang.String", this.rs.getString(1)
+						.getClass().getName());
+				assertEquals("abc", this.rs.getString(1));
+			} finally {
+				if (this.rs != null) {
+					this.rs.close();
+					this.rs = null;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#10156 - Unsigned SMALLINT treated as signed
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug10156() throws Exception {
+		String tableName = "testBug10156";
+		try {
+			createTable(tableName, "(field1 smallint(5) unsigned, "
+					+ "field2 tinyint unsigned," + "field3 int unsigned)");
+			this.stmt.executeUpdate("INSERT INTO " + tableName
+					+ " VALUES (32768, 255, 4294967295)");
+			this.rs = this.conn.prepareStatement(
+					"SELECT field1, field2, field3 FROM " + tableName)
+					.executeQuery();
+			assertTrue(this.rs.next());
+			assertEquals(32768, this.rs.getInt(1));
+			assertEquals(255, this.rs.getInt(2));
+			assertEquals(4294967295L, this.rs.getLong(3));
+
+			assertEquals(String.valueOf(this.rs.getObject(1)), String
+					.valueOf(this.rs.getInt(1)));
+			assertEquals(String.valueOf(this.rs.getObject(2)), String
+					.valueOf(this.rs.getInt(2)));
+			assertEquals(String.valueOf(this.rs.getObject(3)), String
+					.valueOf(this.rs.getLong(3)));
+
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	public void testBug10212() throws Exception {
+		String tableName = "testBug10212";
+
+		try {
+			createTable(tableName, "(field1 YEAR(4))");
+			this.stmt.executeUpdate("INSERT INTO " + tableName
+					+ " VALUES (1974)");
+			this.rs = this.conn.prepareStatement(
+					"SELECT field1 FROM " + tableName).executeQuery();
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+			assertTrue(this.rs.next());
+			assertEquals("java.sql.Date", rsmd.getColumnClassName(1));
+			assertEquals("java.sql.Date", this.rs.getObject(1).getClass()
+					.getName());
+
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM " + tableName);
+
+			rsmd = this.rs.getMetaData();
+			assertTrue(this.rs.next());
+			assertEquals("java.sql.Date", rsmd.getColumnClassName(1));
+			assertEquals("java.sql.Date", this.rs.getObject(1).getClass()
+					.getName());
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11190 - ResultSet.moveToCurrentRow() fails to work when
+	 * preceeded with .moveToInsertRow().
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11190() throws Exception {
+
+		createTable("testBug11190", "(a CHAR(4) PRIMARY KEY, b VARCHAR(20))");
+		this.stmt
+				.executeUpdate("INSERT INTO testBug11190 VALUES('3000','L'),('3001','H'),('1050','B')");
+
+		Statement updStmt = null;
+
+		try {
+			updStmt = this.conn
+					.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+
+			this.rs = updStmt.executeQuery("select * from testBug11190");
+			assertTrue("must return a row", this.rs.next());
+			String savedValue = this.rs.getString(1);
+			this.rs.moveToInsertRow();
+			this.rs.updateString(1, "4000");
+			this.rs.updateString(2, "C");
+			this.rs.insertRow();
+
+			this.rs.moveToCurrentRow();
+			assertEquals(savedValue, this.rs.getString(1));
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+
+			if (updStmt != null) {
+				updStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#12104 - Geometry types not handled with server-side
+	 * prepared statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug12104() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			createTable("testBug12104", "(field1 GEOMETRY) ENGINE=MyISAM");
+
+			try {
+				this.stmt
+						.executeUpdate("INSERT INTO testBug12104 VALUES (GeomFromText('POINT(1 1)'))");
+				this.pstmt = this.conn
+						.prepareStatement("SELECT field1 FROM testBug12104");
+				this.rs = this.pstmt.executeQuery();
+				assertTrue(this.rs.next());
+				System.out.println(this.rs.getObject(1));
+			} finally {
+
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#13043 - when 'gatherPerfMetrics' is enabled for servers <
+	 * 4.1.0, a NPE is thrown from the constructor of ResultSet if the query
+	 * doesn't use any tables.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug13043() throws Exception {
+		if (!versionMeetsMinimum(4, 1)) {
+			Connection perfConn = null;
+
+			try {
+				Properties props = new Properties();
+				props.put("gatherPerfMetrics", "true"); // this property is
+				// reported as the cause
+				// of
+				// NullPointerException
+				props.put("reportMetricsIntervalMillis", "30000"); // this
+				// property
+				// is
+				// reported
+				// as the
+				// cause of
+				// NullPointerException
+				perfConn = getConnectionWithProps(props);
+				perfConn.createStatement().executeQuery("SELECT 1");
+			} finally {
+				if (perfConn != null) {
+					perfConn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#13374 - ResultSet.getStatement() on closed result set
+	 * returns NULL (as per JDBC 4.0 spec, but not backwards-compatible).
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+
+	public void testBug13374() throws Exception {
+		Statement retainStmt = null;
+		Connection retainConn = null;
+
+		try {
+			Properties props = new Properties();
+
+			props.setProperty("retainStatementAfterResultSetClose", "true");
+
+			retainConn = getConnectionWithProps(props);
+
+			retainStmt = retainConn.createStatement();
+
+			this.rs = retainStmt.executeQuery("SELECT 1");
+			this.rs.close();
+			assertNotNull(this.rs.getStatement());
+
+			this.rs = this.stmt.executeQuery("SELECT 1");
+			this.rs.close();
+
+			try {
+				this.rs.getStatement();
+			} catch (SQLException sqlEx) {
+				assertEquals(sqlEx.getSQLState(),
+						SQLError.SQL_STATE_GENERAL_ERROR);
+			}
+
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+
+			if (retainStmt != null) {
+				retainStmt.close();
+			}
+
+			if (retainConn != null) {
+				retainConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests bugfix for BUG#14562 - metadata/type for MEDIUMINT UNSIGNED is
+	 * incorrect.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug14562() throws Exception {
+		createTable("testBug14562",
+				"(row_order INT, signed_field MEDIUMINT, unsigned_field MEDIUMINT UNSIGNED)");
+
+		try {
+			this.stmt
+					.executeUpdate("INSERT INTO testBug14562 VALUES (1, -8388608, 0), (2, 8388607, 16777215)");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT signed_field, unsigned_field FROM testBug14562 ORDER BY row_order");
+			traverseResultSetBug14562();
+
+			this.rs = this.conn
+					.prepareStatement(
+							"SELECT signed_field, unsigned_field FROM testBug14562 ORDER BY row_order")
+					.executeQuery();
+			traverseResultSetBug14562();
+
+			if (versionMeetsMinimum(5, 0)) {
+				CallableStatement storedProc = null;
+
+				try {
+					this.stmt
+							.executeUpdate("DROP PROCEDURE IF EXISTS sp_testBug14562");
+					this.stmt
+							.executeUpdate("CREATE PROCEDURE sp_testBug14562() BEGIN SELECT signed_field, unsigned_field FROM testBug14562 ORDER BY row_order; END");
+					storedProc = this.conn
+							.prepareCall("{call sp_testBug14562()}");
+					storedProc.execute();
+					this.rs = storedProc.getResultSet();
+					traverseResultSetBug14562();
+
+					this.stmt
+							.executeUpdate("DROP PROCEDURE IF EXISTS sp_testBug14562_1");
+					this.stmt
+							.executeUpdate("CREATE PROCEDURE sp_testBug14562_1(OUT param_1 MEDIUMINT, OUT param_2 MEDIUMINT UNSIGNED) BEGIN SELECT signed_field, unsigned_field INTO param_1, param_2 FROM testBug14562 WHERE row_order=1; END");
+					storedProc = this.conn
+							.prepareCall("{call sp_testBug14562_1(?, ?)}");
+					storedProc.registerOutParameter(1, Types.INTEGER);
+					storedProc.registerOutParameter(2, Types.INTEGER);
+
+					storedProc.execute();
+
+					assertEquals("java.lang.Integer", storedProc.getObject(1)
+							.getClass().getName());
+					assertEquals("java.lang.Integer", storedProc.getObject(2)
+							.getClass().getName());
+
+				} finally {
+					if (storedProc != null) {
+						storedProc.close();
+					}
+
+					this.stmt
+							.executeUpdate("DROP PROCEDURE IF EXISTS sp_testBug14562");
+				}
+			}
+
+			this.rs = this.conn.getMetaData().getColumns(
+					this.conn.getCatalog(), null, "testBug14562", "%field");
+
+			assertTrue(this.rs.next());
+
+			assertEquals(Types.INTEGER, this.rs.getInt("DATA_TYPE"));
+			assertEquals("MEDIUMINT", this.rs.getString("TYPE_NAME")
+					.toUpperCase(Locale.US));
+
+			assertTrue(this.rs.next());
+
+			assertEquals(Types.INTEGER, this.rs.getInt("DATA_TYPE"));
+			assertEquals("MEDIUMINT UNSIGNED", this.rs.getString("TYPE_NAME")
+					.toUpperCase(Locale.US));
+
+			//
+			// The following test is harmless in the 3.1 driver, but
+			// is needed for the 5.0 driver, so we'll leave it here
+			//
+			if (versionMeetsMinimum(5, 0, 14)) {
+				Connection infoSchemConn = null;
+
+				try {
+					Properties props = new Properties();
+					props.setProperty("useInformationSchema", "true");
+
+					infoSchemConn = getConnectionWithProps(props);
+
+					this.rs = infoSchemConn.getMetaData().getColumns(
+							infoSchemConn.getCatalog(), null, "testBug14562",
+							"%field");
+
+					assertTrue(this.rs.next());
+
+					assertEquals(Types.INTEGER, this.rs.getInt("DATA_TYPE"));
+					assertEquals("MEDIUMINT", this.rs.getString("TYPE_NAME")
+							.toUpperCase(Locale.US));
+
+					assertTrue(this.rs.next());
+
+					assertEquals(Types.INTEGER, this.rs.getInt("DATA_TYPE"));
+					assertEquals("MEDIUMINT UNSIGNED", this.rs.getString(
+							"TYPE_NAME").toUpperCase(Locale.US));
+
+				} finally {
+					if (infoSchemConn != null) {
+						infoSchemConn.close();
+					}
+				}
+			}
+		} finally {
+
+		}
+	}
+
+	public void testBug14897() throws Exception {
+		createTable("table1", "(id int, name_id int)");
+		createTable("table2", "(id int)");
+		createTable(
+				"lang_table",
+				"(id int, en varchar(255) CHARACTER SET utf8, cz varchar(255) CHARACTER SET utf8)");
+
+		this.stmt.executeUpdate("insert into table1 values (0, 0)");
+		this.stmt.executeUpdate("insert into table2 values (0)");
+		this.stmt
+				.executeUpdate("insert into lang_table values (0, 'abcdef', 'ghijkl')");
+		this.rs = this.stmt
+				.executeQuery("select a.id, b.id, c.en, c.cz from table1 as a, table2 as b, lang_table as c where a.id = b.id and a.name_id = c.id");
+		assertTrue(this.rs.next());
+		this.rs.getString("c.cz");
+
+		this.rs = this.stmt
+				.executeQuery("select table1.*, table2.* FROM table1, table2");
+		this.rs.findColumn("table1.id");
+		this.rs.findColumn("table2.id");
+	}
+
+	/**
+	 * Tests fix for BUG#14609 - Exception thrown for new decimal type when
+	 * using updatable result sets.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug14609() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			createTable("testBug14609",
+					"(field1 int primary key, field2 decimal)");
+			this.stmt.executeUpdate("INSERT INTO testBug14609 VALUES (1, 1)");
+
+			PreparedStatement updatableStmt = this.conn.prepareStatement(
+					"SELECT field1, field2 FROM testBug14609",
+					ResultSet.TYPE_SCROLL_INSENSITIVE,
+					ResultSet.CONCUR_UPDATABLE);
+
+			try {
+				this.rs = updatableStmt.executeQuery();
+			} finally {
+				if (this.rs != null) {
+					ResultSet toClose = this.rs;
+					this.rs = null;
+					toClose.close();
+				}
+
+				if (updatableStmt != null) {
+					updatableStmt.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#16169 - ResultSet.getNativeShort() causes stack
+	 * overflow error via recurisve calls.
+	 * 
+	 * @throws Exception
+	 *             if the tests fails
+	 */
+	public void testBug16169() throws Exception {
+		createTable("testBug16169", "(field1 smallint)");
+
+		try {
+
+			this.stmt
+					.executeUpdate("INSERT INTO testBug16169 (field1) VALUES (0)");
+
+			this.pstmt = this.conn
+					.prepareStatement("SELECT * FROM testBug16169");
+			this.rs = this.pstmt.executeQuery();
+			assertTrue(this.rs.next());
+
+			assertEquals(0, ((Integer) rs.getObject("field1")).intValue());
+		} finally {
+			if (this.rs != null) {
+				ResultSet toCloseRs = this.rs;
+				this.rs = null;
+				toCloseRs.close();
+			}
+
+			if (this.pstmt != null) {
+				PreparedStatement toCloseStmt = this.pstmt;
+				this.pstmt = null;
+				toCloseStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#16841 - updatable result set doesn't return
+	 * AUTO_INCREMENT values for insertRow() when multiple column primary keys
+	 * are used.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug16841() throws Exception {
+
+		createTable("testBug16841", "(" + "CID int( 20 ) NOT NULL default '0',"
+				+ "OID int( 20 ) NOT NULL AUTO_INCREMENT ,"
+				+ "PatientID int( 20 ) default NULL ,"
+				+ "PRIMARY KEY ( CID , OID ) ," + "KEY OID ( OID ) ,"
+				+ "KEY Path ( CID, PatientID)" + ") TYPE = MYISAM");
+
+		String sSQLQuery = "SELECT * FROM testBug16841 WHERE 1 = 0";
+		Statement updStmt = null;
+
+		try {
+			updStmt = this.conn.createStatement(ResultSet.TYPE_FORWARD_ONLY,
+					ResultSet.CONCUR_UPDATABLE);
+
+			this.rs = updStmt.executeQuery(sSQLQuery);
+
+			this.rs.moveToInsertRow();
+
+			this.rs.updateInt("CID", 1);
+			this.rs.updateInt("PatientID", 1);
+
+			this.rs.insertRow();
+
+			this.rs.last();
+			assertEquals(1, this.rs.getInt("OID"));
+		} finally {
+			if (this.rs != null) {
+				ResultSet toClose = this.rs;
+				this.rs = null;
+				toClose.close();
+			}
+
+			if (updStmt != null) {
+				updStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#17450 - ResultSet.wasNull() not always reset correctly
+	 * for booleans when done via conversion for server-side prepared
+	 * statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug17450() throws Exception {
+		if (versionMeetsMinimum(4, 1, 0)) {
+			createTable("testBug17450", "(FOO VARCHAR(100), BAR CHAR NOT NULL)");
+
+			this.stmt
+					.execute("insert into testBug17450 (foo,bar) values ('foo',true)");
+			this.stmt
+					.execute("insert into testBug17450 (foo,bar) values (null,true)");
+
+			this.pstmt = this.conn
+					.prepareStatement("select * from testBug17450 where foo=?");
+			this.pstmt.setString(1, "foo");
+			this.rs = this.pstmt.executeQuery();
+			checkResult17450();
+
+			this.pstmt = this.conn
+					.prepareStatement("select * from testBug17450 where foo is null");
+			this.rs = this.pstmt.executeQuery();
+			checkResult17450();
+
+			this.rs = this.stmt
+					.executeQuery("select * from testBug17450 where foo='foo'");
+			checkResult17450();
+
+			this.rs = this.stmt
+					.executeQuery("select * from testBug17450 where foo is null");
+			checkResult17450();
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#19282 - ResultSet.wasNull() returns incorrect value
+	 * when extracting native string from server-side prepared statement
+	 * generated result set.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug19282() throws Exception {
+		createTable("testBug19282", "(field1 VARCHAR(32))");
+		try {
+			this.pstmt = this.conn
+					.prepareStatement("SELECT field1 FROM testBug19282");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug19282 VALUES ('abcdefg')");
+
+			this.rs = this.pstmt.executeQuery();
+			this.rs.next();
+			assertEquals(false, this.rs.wasNull());
+			this.rs.getString(1);
+			assertEquals(false, this.rs.wasNull());
+		} finally {
+			if (this.rs != null) {
+				ResultSet toClose = this.rs;
+				this.rs = null;
+				toClose.close();
+			}
+
+			if (this.pstmt != null) {
+				PreparedStatement toClose = this.pstmt;
+				this.pstmt = null;
+				toClose.close();
+			}
+		}
+	}
+
+	private void checkResult17450() throws Exception {
+		this.rs.next();
+		this.rs.getString(1);
+		boolean bar = this.rs.getBoolean(2);
+
+		assertEquals("field 2 should be true", true, bar);
+		assertFalse("wasNull should return false", this.rs.wasNull());
+	}
+
+	/**
+	 * Tests fix for BUG#
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug19568() throws Exception {
+		if (versionMeetsMinimum(4, 1, 0)) {
+			createTable("testBug19568", "(field1 BOOLEAN,"
+					+ (versionMeetsMinimum(5, 0, 0) ? "field2 BIT"
+							: "field2 BOOLEAN") + ")");
+
+			this.stmt
+					.executeUpdate("INSERT INTO testBug19568 VALUES (1,0), (0, 1)");
+
+			try {
+				this.pstmt = this.conn
+						.prepareStatement("SELECT field1, field2 FROM testBug19568 ORDER BY field1 DESC");
+				this.rs = this.pstmt.executeQuery();
+
+				checkResultsBug19568();
+
+				this.rs = this.stmt
+						.executeQuery("SELECT field1, field2 FROM testBug19568 ORDER BY field1 DESC");
+				checkResultsBug19568();
+			} finally {
+				closeMemberJDBCResources();
+			}
+		}
+	}
+
+	private void checkResultsBug19568() throws SQLException {
+		// Test all numerical getters, and make sure to alternate true/false
+		// across rows so we can catch
+		// false-positives if off-by-one errors exist in the column getters.
+
+		for (int i = 0; i < 2; i++) {
+			assertTrue(this.rs.next());
+
+			for (int j = 0; j < 2; j++) {
+				assertEquals((i == 1 && j == 1) || (i == 0 && j == 0), this.rs
+						.getBoolean(j + 1));
+				assertEquals(
+						((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0),
+						this.rs.getBigDecimal(j + 1).intValue());
+				assertEquals(
+						((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0),
+						this.rs.getByte(j + 1));
+				assertEquals(
+						((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0),
+						this.rs.getShort(j + 1));
+				assertEquals(
+						((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0),
+						this.rs.getInt(j + 1));
+				assertEquals(
+						((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0),
+						this.rs.getLong(j + 1));
+				assertEquals(
+						((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0),
+						this.rs.getFloat(j + 1), .1);
+				assertEquals(
+						((i == 1 && j == 1) || (i == 0 && j == 0) ? 1 : 0),
+						this.rs.getDouble(j + 1), .1);
+			}
+		}
+	}
+
+	public void testBug19724() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			// can't set this via session on 4.0 :(
+
+			createTable("test19724",
+					"(col1 INTEGER NOT NULL, col2 VARCHAR(255) NULL, PRIMARY KEY (col1))");
+
+			this.stmt
+					.execute("INSERT IGNORE INTO test19724 VALUES (0, 'Blah'),(1,'Boo')");
+
+			Connection ansiConn = null;
+			Statement updStmt = null;
+
+			Properties props = new Properties();
+			props.setProperty("sessionVariables", "sql_mode=ansi");
+
+			try {
+				ansiConn = getConnectionWithProps(props);
+				updStmt = ansiConn.createStatement(
+						ResultSet.TYPE_SCROLL_INSENSITIVE,
+						ResultSet.CONCUR_UPDATABLE);
+				this.rs = updStmt.executeQuery("SELECT * FROM test19724");
+
+				this.rs.beforeFirst();
+
+				this.rs.next();
+
+				this.rs.updateString("col2", "blah2");
+				this.rs.updateRow();
+			} finally {
+				closeMemberJDBCResources();
+
+				if (ansiConn != null) {
+					ansiConn.close();
+				}
+			}
+		}
+	}
+
+	private void traverseResultSetBug14562() throws SQLException {
+		assertTrue(this.rs.next());
+
+		ResultSetMetaData rsmd = this.rs.getMetaData();
+		assertEquals("MEDIUMINT", rsmd.getColumnTypeName(1));
+		assertEquals("MEDIUMINT UNSIGNED", rsmd.getColumnTypeName(2));
+
+		assertEquals(Types.INTEGER, rsmd.getColumnType(1));
+		assertEquals(Types.INTEGER, rsmd.getColumnType(2));
+
+		assertEquals("java.lang.Integer", rsmd.getColumnClassName(1));
+		assertEquals("java.lang.Integer", rsmd.getColumnClassName(2));
+
+		assertEquals(-8388608, this.rs.getInt(1));
+		assertEquals(0, this.rs.getInt(2));
+
+		assertEquals("java.lang.Integer", this.rs.getObject(1).getClass()
+				.getName());
+		assertEquals("java.lang.Integer", this.rs.getObject(2).getClass()
+				.getName());
+
+		assertTrue(this.rs.next());
+
+		assertEquals(8388607, this.rs.getInt(1));
+		assertEquals(16777215, this.rs.getInt(2));
+
+		assertEquals("java.lang.Integer", this.rs.getObject(1).getClass()
+				.getName());
+		assertEquals("java.lang.Integer", this.rs.getObject(2).getClass()
+				.getName());
+	}
+
+	/*
+	 * public void testBug16458() throws Exception { createTable("a", "(id
+	 * INTEGER NOT NULL, primary key (id)) Type=InnoDB"); createTable("b", "(id
+	 * INTEGER NOT NULL, primary key (id)) Type=InnoDB"); createTable("c", "(id
+	 * INTEGER NOT NULL, primary key (id)) Type=InnoDB");
+	 * 
+	 * createTable( "problem_table", "(id int(11) NOT NULL auto_increment," +
+	 * "a_id int(11) NOT NULL default '0'," + "b_id int(11) NOT NULL default
+	 * '0'," + "c_id int(11) default NULL," + "order_num int(2) NOT NULL default
+	 * '0'," + "PRIMARY KEY (id)," + "KEY idx_problem_table__b_id (b_id)," +
+	 * "KEY idx_problem_table__a_id (a_id)," + "KEY idx_problem_table__c_id
+	 * (c_id)," + "CONSTRAINT fk_problem_table__c FOREIGN KEY (c_id) REFERENCES
+	 * c (id)," + "CONSTRAINT fk_problem_table__a FOREIGN KEY (a_id) REFERENCES
+	 * a (id)," + "CONSTRAINT fk_problem_table__b FOREIGN KEY (b_id) REFERENCES
+	 * b (id)" + ")" + "Type=InnoDB");
+	 * 
+	 * this.stmt .executeUpdate("INSERT INTO `a` VALUES " +
+	 * "(1),(4),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19),(20),(21),(22),(23" +
+	 * "),(24),(25),(26),(27),(28),(29),(30),(31),(32),(33),(34),(35),(36),(37),(38),(39" +
+	 * "),(40),(41),(42),(43),(45),(46),(47),(48),(49),(50)");
+	 * 
+	 * this.stmt .executeUpdate("INSERT INTO `b` VALUES " +
+	 * "(1),(2),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15),(16),(17),(18),(19" +
+	 * "),(20)");
+	 * 
+	 * this.stmt .executeUpdate("INSERT INTO `c` VALUES " +
+	 * "(1),(2),(3),(13),(15),(16),(22),(30),(31),(32),(33),(34),(35),(36),(37),(148),(1" +
+	 * "59),(167),(174),(176),(177),(178),(179),(180),(187),(188),(189),(190),(191),(192" +
+	 * "),(193),(194),(195),(196),(197),(198),(199),(200),(201),(202),(203),(204),(205)," +
+	 * "(206),(207),(208)");
+	 * 
+	 * this.stmt .executeUpdate("INSERT INTO `problem_table` VALUES " +
+	 * "(1,1,1,NULL,1),(2,1,4,NULL,1),(3,1,5,NULL,1),(4,1,8,NULL,1),(5,23,1,NULL,1),(6,2" +
+	 * "3,4,NULL,1),(7,24,1,NULL,1),(8,24,2,NULL,1),(9,24,4,NULL,1),(10,25,1,NULL,1),(11" +
+	 * ",25,2,NULL,1),(12,25,4,NULL,1),(13,27,1,NULL,1),(14,28,1,NULL,1),(15,29,1,NULL,1" +
+	 * "),(16,15,2,NULL,1),(17,15,5,NULL,1),(18,15,8,NULL,1),(19,30,1,NULL,1),(20,31,1,N" +
+	 * "ULL,1),(21,31,4,NULL,1),(22,32,2,NULL,1),(23,32,4,NULL,1),(24,32,6,NULL,1),(25,3" +
+	 * "2,8,NULL,1),(26,32,10,NULL,1),(27,32,11,NULL,1),(28,32,13,NULL,1),(29,32,16,NULL" +
+	 * ",1),(30,32,17,NULL,1),(31,32,18,NULL,1),(32,32,19,NULL,1),(33,32,20,NULL,1),(34," +
+	 * "33,15,NULL,1),(35,33,15,NULL,1),(36,32,20,206,1),(96,32,9,NULL,1),(100,47,6,NULL" +
+	 * ",1),(101,47,10,NULL,1),(102,47,5,NULL,1),(105,47,19,NULL,1)");
+	 * PreparedStatement ps = null;
+	 * 
+	 * try { ps = conn.prepareStatement("SELECT DISTINCT id,order_num FROM
+	 * problem_table WHERE a_id=? FOR UPDATE", ResultSet.TYPE_FORWARD_ONLY,
+	 * ResultSet.CONCUR_UPDATABLE);
+	 * 
+	 * ps.setInt(1, 32);
+	 * 
+	 * this.rs = ps.executeQuery();
+	 * 
+	 * while(this.rs.next()) { this.rs.updateInt(3, 51);
+	 * 
+	 * this.rs.updateRow(); } } finally { if (this.rs != null) { ResultSet
+	 * toCloseRs = this.rs; this.rs = null; toCloseRs.close(); }
+	 * 
+	 * if (ps != null) { PreparedStatement toClosePs = ps; ps = null;
+	 * toClosePs.close(); } } }
+	 */
+
+	public void testNPEWithUsageAdvisor() throws Exception {
+		Connection advisorConn = null;
+
+		try {
+			Properties props = new Properties();
+			props.setProperty("useUsageAdvisor", "true");
+
+			advisorConn = getConnectionWithProps(props);
+			this.pstmt = advisorConn.prepareStatement("SELECT 1");
+			this.rs = this.pstmt.executeQuery();
+			this.rs.close();
+			this.rs = this.pstmt.executeQuery();
+
+		} finally {
+		}
+	}
+	
+	public void testAllTypesForNull() throws Exception {
+		if (!isRunningOnJdk131()) {
+			Properties props = new Properties();
+			props.setProperty("jdbcCompliantTruncation", "false");
+			props.setProperty("zeroDateTimeBehavior", "round");
+			Connection conn2 = getConnectionWithProps(props);
+			Statement stmt2 = conn2.createStatement();
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+
+			this.rs = dbmd.getTypeInfo();
+
+			boolean firstColumn = true;
+			int numCols = 1;
+			StringBuffer createStatement = new StringBuffer(
+					"CREATE TABLE testAllTypes (");
+			List wasDatetimeTypeList = new ArrayList();
+
+			while (this.rs.next()) {
+				String dataType = this.rs.getString("TYPE_NAME").toUpperCase();
+
+				boolean wasDateTime = false;
+
+				if (dataType.indexOf("DATE") != -1
+						|| dataType.indexOf("TIME") != -1) {
+					wasDateTime = true;
+				}
+
+				if (!"BOOL".equalsIgnoreCase(dataType)
+						&& !"LONG VARCHAR".equalsIgnoreCase(dataType)
+						&& !"LONG VARBINARY".equalsIgnoreCase(dataType)
+						&& !"ENUM".equalsIgnoreCase(dataType)
+						&& !"SET".equalsIgnoreCase(dataType)) {
+					wasDatetimeTypeList.add(new Boolean(wasDateTime));
+					createStatement.append("\n\t");
+					if (!firstColumn) {
+						createStatement.append(",");
+					} else {
+						firstColumn = false;
+					}
+
+					createStatement.append("field_");
+					createStatement.append(numCols++);
+					createStatement.append(" ");
+
+					createStatement.append(dataType);
+
+					if (dataType.indexOf("CHAR") != -1
+							|| dataType.indexOf("BINARY") != -1
+							&& dataType.indexOf("BLOB") == -1
+							&& dataType.indexOf("TEXT") == -1) {
+						createStatement.append("(");
+						createStatement.append(this.rs.getString("PRECISION"));
+						createStatement.append(")");
+					}
+
+					createStatement.append(" NULL DEFAULT NULL");
+				}
+			}
+
+			createStatement.append("\n)");
+
+			stmt2.executeUpdate("DROP TABLE IF EXISTS testAllTypes");
+
+			stmt2.executeUpdate(createStatement.toString());
+			StringBuffer insertStatement = new StringBuffer(
+					"INSERT INTO testAllTypes VALUES (NULL");
+			for (int i = 1; i < numCols - 1; i++) {
+				insertStatement.append(", NULL");
+			}
+			insertStatement.append(")");
+			stmt2.executeUpdate(insertStatement.toString());
+
+			this.rs = stmt2.executeQuery("SELECT * FROM testAllTypes");
+
+			testAllFieldsForNull(this.rs);
+			this.rs.close();
+
+			this.rs = this.conn.prepareStatement("SELECT * FROM testAllTypes")
+					.executeQuery();
+			testAllFieldsForNull(this.rs);
+
+			stmt2.executeUpdate("DELETE FROM testAllTypes");
+
+			insertStatement = new StringBuffer(
+					"INSERT INTO testAllTypes VALUES (");
+
+			boolean needsNow = ((Boolean) wasDatetimeTypeList.get(0))
+					.booleanValue();
+
+			if (needsNow) {
+				insertStatement.append("NOW()");
+			} else {
+				insertStatement.append("'0'");
+			}
+
+			for (int i = 1; i < numCols - 1; i++) {
+				needsNow = ((Boolean) wasDatetimeTypeList.get(i))
+						.booleanValue();
+				insertStatement.append(",");
+				if (needsNow) {
+					insertStatement.append("NOW()");
+				} else {
+					insertStatement.append("'0'");
+				}
+			}
+
+			insertStatement.append(")");
+
+			stmt2.executeUpdate(insertStatement.toString());
+
+			this.rs = stmt2.executeQuery("SELECT * FROM testAllTypes");
+
+			testAllFieldsForNotNull(this.rs, wasDatetimeTypeList);
+			this.rs.close();
+
+			this.rs = conn2.prepareStatement("SELECT * FROM testAllTypes")
+					.executeQuery();
+			testAllFieldsForNotNull(this.rs, wasDatetimeTypeList);
+		}
+	}
+
+	private void testAllFieldsForNull(ResultSet rsToTest) throws Exception {
+		ResultSetMetaData rsmd = this.rs.getMetaData();
+		int numCols = rsmd.getColumnCount();
+
+		while (rsToTest.next()) {
+			for (int i = 0; i < numCols - 1; i++) {
+				String typeName = rsmd.getColumnTypeName(i + 1);
+
+				if ("VARBINARY".equalsIgnoreCase(typeName)) {
+					System.out.println();
+				}
+
+				if (!"BIT".equalsIgnoreCase(typeName)) {
+					assertEquals(false, rsToTest.getBoolean(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+
+					assertEquals(0, rsToTest.getDouble(i + 1), 0 /* delta */);
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(0, rsToTest.getFloat(i + 1), 0 /* delta */);
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(0, rsToTest.getInt(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(0, rsToTest.getLong(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getObject(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getString(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getAsciiStream(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getBigDecimal(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getBinaryStream(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getBlob(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(0, rsToTest.getByte(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getBytes(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getCharacterStream(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getClob(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getDate(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(0, rsToTest.getShort(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getTime(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getTimestamp(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getUnicodeStream(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+					assertEquals(null, rsToTest.getURL(i + 1));
+					assertTrue("for type " + typeName, rsToTest.wasNull());
+				}
+			}
+		}
+	}
+
+	private void testAllFieldsForNotNull(ResultSet rsToTest,
+			List wasDatetimeTypeList) throws Exception {
+		ResultSetMetaData rsmd = this.rs.getMetaData();
+		int numCols = rsmd.getColumnCount();
+
+		while (rsToTest.next()) {
+			for (int i = 0; i < numCols - 1; i++) {
+				boolean wasDatetimeType = ((Boolean) wasDatetimeTypeList.get(i))
+						.booleanValue();
+				String typeName = rsmd.getColumnTypeName(i + 1);
+				int sqlType = rsmd.getColumnType(i + 1);
+
+				if (!"BIT".equalsIgnoreCase(typeName)
+						&& sqlType != Types.BINARY
+						&& sqlType != Types.VARBINARY
+						&& sqlType != Types.LONGVARBINARY) {
+					if (!wasDatetimeType) {
+
+						assertEquals(false, rsToTest.getBoolean(i + 1));
+
+						assertTrue(!rsToTest.wasNull());
+
+						assertEquals(0, rsToTest.getDouble(i + 1), 0 /* delta */);
+						assertTrue(!rsToTest.wasNull());
+						assertEquals(0, rsToTest.getFloat(i + 1), 0 /* delta */);
+						assertTrue(!rsToTest.wasNull());
+						assertEquals(0, rsToTest.getInt(i + 1));
+						assertTrue(!rsToTest.wasNull());
+						assertEquals(0, rsToTest.getLong(i + 1));
+						assertTrue(!rsToTest.wasNull());
+						assertEquals(0, rsToTest.getByte(i + 1));
+						assertTrue(!rsToTest.wasNull());
+						assertEquals(0, rsToTest.getShort(i + 1));
+						assertTrue(!rsToTest.wasNull());
+					}
+
+					assertNotNull(rsToTest.getObject(i + 1));
+					assertTrue(!rsToTest.wasNull());
+					assertNotNull(rsToTest.getString(i + 1));
+					assertTrue(!rsToTest.wasNull());
+					assertNotNull(rsToTest.getAsciiStream(i + 1));
+					assertTrue(!rsToTest.wasNull());
+
+					assertNotNull(rsToTest.getBinaryStream(i + 1));
+					assertTrue(!rsToTest.wasNull());
+					assertNotNull(rsToTest.getBlob(i + 1));
+					assertTrue(!rsToTest.wasNull());
+					assertNotNull(rsToTest.getBytes(i + 1));
+					assertTrue(!rsToTest.wasNull());
+					assertNotNull(rsToTest.getCharacterStream(i + 1));
+					assertTrue(!rsToTest.wasNull());
+					assertNotNull(rsToTest.getClob(i + 1));
+					assertTrue(!rsToTest.wasNull());
+
+					String columnClassName = rsmd.getColumnClassName(i + 1);
+
+					boolean canBeUsedAsDate = !("java.lang.Boolean"
+							.equals(columnClassName)
+							|| "java.lang.Double".equals(columnClassName)
+							|| "java.lang.Float".equals(columnClassName)
+							|| "java.lang.Real".equals(columnClassName) || "java.math.BigDecimal"
+							.equals(columnClassName));
+
+					if (canBeUsedAsDate) {
+						assertNotNull(rsToTest.getDate(i + 1));
+						assertTrue(!rsToTest.wasNull());
+						assertNotNull(rsToTest.getTime(i + 1));
+						assertTrue(!rsToTest.wasNull());
+						assertNotNull(rsToTest.getTimestamp(i + 1));
+						assertTrue(!rsToTest.wasNull());
+					}
+
+					assertNotNull(rsToTest.getUnicodeStream(i + 1));
+					assertTrue(!rsToTest.wasNull());
+
+					try {
+						if (!isRunningOnJdk131()) {
+							assertNotNull(rsToTest.getURL(i + 1));
+						}
+					} catch (SQLException sqlEx) {
+						assertTrue(sqlEx.getMessage().indexOf("URL") != -1);
+					}
+
+					assertTrue(!rsToTest.wasNull());
+				}
+			}
+		}
+	}
+
+	public void testNPEWithStatementsAndTime() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testNPETime");
+			this.stmt
+					.executeUpdate("CREATE TABLE testNPETime (field1 TIME NULL, field2 DATETIME NULL, field3 DATE NULL)");
+			this.stmt
+					.executeUpdate("INSERT INTO testNPETime VALUES (null, null, null)");
+			this.pstmt = this.conn
+					.prepareStatement("SELECT field1, field2, field3 FROM testNPETime");
+			this.rs = this.pstmt.executeQuery();
+			this.rs.next();
+
+			for (int i = 0; i < 3; i++) {
+				assertEquals(null, this.rs.getTime(i + 1));
+				assertEquals(true, this.rs.wasNull());
+			}
+
+			for (int i = 0; i < 3; i++) {
+				assertEquals(null, this.rs.getTimestamp(i + 1));
+				assertEquals(true, this.rs.wasNull());
+			}
+
+			for (int i = 0; i < 3; i++) {
+				assertEquals(null, this.rs.getDate(i + 1));
+				assertEquals(true, this.rs.wasNull());
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testNPETime");
+		}
+	}
+
+	public void testEmptyStringsWithNumericGetters() throws Exception {
+		try {
+			createTable("emptyStringTable", "(field1 char(32))");
+			this.stmt.executeUpdate("INSERT INTO emptyStringTable VALUES ('')");
+			this.rs = this.stmt
+					.executeQuery("SELECT field1 FROM emptyStringTable");
+			assertTrue(this.rs.next());
+			createTable("emptyStringTable", "(field1 char(32))");
+			this.stmt.executeUpdate("INSERT INTO emptyStringTable VALUES ('')");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field1 FROM emptyStringTable");
+			assertTrue(this.rs.next());
+			checkEmptyConvertToZero();
+
+			this.rs = this.conn.prepareStatement(
+					"SELECT field1 FROM emptyStringTable").executeQuery();
+			assertTrue(this.rs.next());
+			checkEmptyConvertToZero();
+
+			Properties props = new Properties();
+			props.setProperty("useFastIntParsing", "false");
+
+			Connection noFastIntParseConn = getConnectionWithProps(props);
+			Statement noFastIntStmt = noFastIntParseConn.createStatement();
+
+			this.rs = noFastIntStmt
+					.executeQuery("SELECT field1 FROM emptyStringTable");
+			assertTrue(this.rs.next());
+			checkEmptyConvertToZero();
+
+			this.rs = noFastIntParseConn.prepareStatement(
+					"SELECT field1 FROM emptyStringTable").executeQuery();
+			assertTrue(this.rs.next());
+			checkEmptyConvertToZero();
+
+			//
+			// Now, be more pedantic....
+			//
+
+			props = new Properties();
+			props.setProperty("emptyStringsConvertToZero", "false");
+
+			Connection pedanticConn = getConnectionWithProps(props);
+			Statement pedanticStmt = pedanticConn.createStatement();
+
+			this.rs = pedanticStmt
+					.executeQuery("SELECT field1 FROM emptyStringTable");
+			assertTrue(this.rs.next());
+
+			checkEmptyConvertToZeroException();
+
+			this.rs = pedanticConn.prepareStatement(
+					"SELECT field1 FROM emptyStringTable").executeQuery();
+			assertTrue(this.rs.next());
+			checkEmptyConvertToZeroException();
+
+			props = new Properties();
+			props.setProperty("emptyStringsConvertToZero", "false");
+			props.setProperty("useFastIntParsing", "false");
+
+			pedanticConn = getConnectionWithProps(props);
+			pedanticStmt = pedanticConn.createStatement();
+
+			this.rs = pedanticStmt
+					.executeQuery("SELECT field1 FROM emptyStringTable");
+			assertTrue(this.rs.next());
+
+			checkEmptyConvertToZeroException();
+
+			this.rs = pedanticConn.prepareStatement(
+					"SELECT field1 FROM emptyStringTable").executeQuery();
+			assertTrue(this.rs.next());
+			checkEmptyConvertToZeroException();
+
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+
+				this.rs = null;
+			}
+		}
+	}
+
+	public void testNegativeOneIsTrue() throws Exception {
+		if (!versionMeetsMinimum(5, 0, 3)) {
+			String tableName = "testNegativeOneIsTrue";
+			Connection tinyInt1IsBitConn = null;
+
+			try {
+				createTable(tableName, "(field1 BIT)");
+				this.stmt.executeUpdate("INSERT INTO " + tableName
+						+ " VALUES (-1)");
+
+				Properties props = new Properties();
+				props.setProperty("tinyInt1isBit", "true");
+				tinyInt1IsBitConn = getConnectionWithProps(props);
+
+				this.rs = tinyInt1IsBitConn.createStatement().executeQuery(
+						"SELECT field1 FROM " + tableName);
+				assertTrue(this.rs.next());
+				assertEquals(true, this.rs.getBoolean(1));
+
+				this.rs = tinyInt1IsBitConn.prepareStatement(
+						"SELECT field1 FROM " + tableName).executeQuery();
+				assertTrue(this.rs.next());
+				assertEquals(true, this.rs.getBoolean(1));
+
+			} finally {
+				if (tinyInt1IsBitConn != null) {
+					tinyInt1IsBitConn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * @throws SQLException
+	 */
+	private void checkEmptyConvertToZero() throws SQLException {
+		assertEquals(0, this.rs.getByte(1));
+		assertEquals(0, this.rs.getShort(1));
+		assertEquals(0, this.rs.getInt(1));
+		assertEquals(0, this.rs.getLong(1));
+		assertEquals(0, this.rs.getFloat(1), 0.1);
+		assertEquals(0, this.rs.getDouble(1), 0.1);
+		assertEquals(0, this.rs.getBigDecimal(1).intValue());
+	}
+
+	/**
+	 * 
+	 */
+	private void checkEmptyConvertToZeroException() {
+		try {
+			assertEquals(0, this.rs.getByte(1));
+			fail("Should've thrown an exception!");
+		} catch (SQLException sqlEx) {
+			assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST,
+					sqlEx.getSQLState());
+		}
+		try {
+			assertEquals(0, this.rs.getShort(1));
+			fail("Should've thrown an exception!");
+		} catch (SQLException sqlEx) {
+			assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST,
+					sqlEx.getSQLState());
+		}
+		try {
+			assertEquals(0, this.rs.getInt(1));
+			fail("Should've thrown an exception!");
+		} catch (SQLException sqlEx) {
+			assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST,
+					sqlEx.getSQLState());
+		}
+		try {
+			assertEquals(0, this.rs.getLong(1));
+			fail("Should've thrown an exception!");
+		} catch (SQLException sqlEx) {
+			assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST,
+					sqlEx.getSQLState());
+		}
+		try {
+			assertEquals(0, this.rs.getFloat(1), 0.1);
+			fail("Should've thrown an exception!");
+		} catch (SQLException sqlEx) {
+			assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST,
+					sqlEx.getSQLState());
+		}
+		try {
+			assertEquals(0, this.rs.getDouble(1), 0.1);
+			fail("Should've thrown an exception!");
+		} catch (SQLException sqlEx) {
+			assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST,
+					sqlEx.getSQLState());
+		}
+		try {
+			assertEquals(0, this.rs.getBigDecimal(1).intValue());
+			fail("Should've thrown an exception!");
+		} catch (SQLException sqlEx) {
+			assertEquals(SQLError.SQL_STATE_INVALID_CHARACTER_VALUE_FOR_CAST,
+					sqlEx.getSQLState());
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#10485, SQLException thrown when retrieving YEAR(2) with
+	 * ResultSet.getString().
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug10485() throws Exception {
+		String tableName = "testBug10485";
+
+		Calendar nydCal = null;
+
+		if (((com.mysql.jdbc.Connection) this.conn)
+				.getUseGmtMillisForDatetimes()) {
+			nydCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+		} else {
+			nydCal = Calendar.getInstance();
+		}
+
+		nydCal.set(2005, 0, 1, 0, 0, 0);
+
+		Date newYears2005 = new Date(nydCal.getTime().getTime());
+
+		createTable(tableName, "(field1 YEAR(2))");
+		this.stmt.executeUpdate("INSERT INTO " + tableName + " VALUES ('05')");
+
+		this.rs = this.stmt.executeQuery("SELECT field1 FROM " + tableName);
+		assertTrue(this.rs.next());
+
+		assertEquals(newYears2005.toString(), this.rs.getString(1));
+
+		this.rs = this.conn.prepareStatement("SELECT field1 FROM " + tableName)
+				.executeQuery();
+		assertTrue(this.rs.next());
+		assertEquals(newYears2005.toString(), this.rs.getString(1));
+
+		Properties props = new Properties();
+		props.setProperty("yearIsDateType", "false");
+
+		Connection yearShortConn = getConnectionWithProps(props);
+		this.rs = yearShortConn.createStatement().executeQuery(
+				"SELECT field1 FROM " + tableName);
+		assertTrue(this.rs.next());
+		assertEquals("05", this.rs.getString(1));
+
+		this.rs = yearShortConn.prepareStatement(
+				"SELECT field1 FROM " + tableName).executeQuery();
+		assertTrue(this.rs.next());
+		assertEquals("05", this.rs.getString(1));
+
+		if (versionMeetsMinimum(5, 0)) {
+			try {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testBug10485");
+				this.stmt
+						.executeUpdate("CREATE PROCEDURE testBug10485()\nBEGIN\nSELECT field1 FROM "
+								+ tableName + ";\nEND");
+
+				this.rs = this.conn.prepareCall("{CALL testBug10485()}")
+						.executeQuery();
+				assertTrue(this.rs.next());
+				assertEquals(newYears2005.toString(), this.rs.getString(1));
+
+				this.rs = yearShortConn.prepareCall("{CALL testBug10485()}")
+						.executeQuery();
+				assertTrue(this.rs.next());
+				assertEquals("05", this.rs.getString(1));
+			} finally {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testBug10485");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11552, wrong values returned from server-side prepared
+	 * statements if values are unsigned.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11552() throws Exception {
+		try {
+			createTable(
+					"testBug11552",
+					"(field1 INT UNSIGNED, field2 TINYINT UNSIGNED, field3 SMALLINT UNSIGNED, field4 BIGINT UNSIGNED)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug11552 VALUES (2, 2, 2, 2), (4294967294, 255, 32768, 18446744073709551615 )");
+			this.rs = this.conn
+					.prepareStatement(
+							"SELECT field1, field2, field3, field4 FROM testBug11552 ORDER BY field1 ASC")
+					.executeQuery();
+			this.rs.next();
+			assertEquals("2", this.rs.getString(1));
+			assertEquals("2", this.rs.getObject(1).toString());
+			assertEquals("2", String.valueOf(this.rs.getLong(1)));
+
+			assertEquals("2", this.rs.getString(2));
+			assertEquals("2", this.rs.getObject(2).toString());
+			assertEquals("2", String.valueOf(this.rs.getLong(2)));
+
+			assertEquals("2", this.rs.getString(3));
+			assertEquals("2", this.rs.getObject(3).toString());
+			assertEquals("2", String.valueOf(this.rs.getLong(3)));
+
+			assertEquals("2", this.rs.getString(4));
+			assertEquals("2", this.rs.getObject(4).toString());
+			assertEquals("2", String.valueOf(this.rs.getLong(4)));
+
+			this.rs.next();
+
+			assertEquals("4294967294", this.rs.getString(1));
+			assertEquals("4294967294", this.rs.getObject(1).toString());
+			assertEquals("4294967294", String.valueOf(this.rs.getLong(1)));
+
+			assertEquals("255", this.rs.getString(2));
+			assertEquals("255", this.rs.getObject(2).toString());
+			assertEquals("255", String.valueOf(this.rs.getLong(2)));
+
+			assertEquals("32768", this.rs.getString(3));
+			assertEquals("32768", this.rs.getObject(3).toString());
+			assertEquals("32768", String.valueOf(this.rs.getLong(3)));
+
+			assertEquals("18446744073709551615", this.rs.getString(4));
+			assertEquals("18446744073709551615", this.rs.getObject(4)
+					.toString());
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests correct detection of truncation of non-sig digits.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testTruncationOfNonSigDigits() throws Exception {
+		if (versionMeetsMinimum(4, 1, 0)) {
+			createTable("testTruncationOfNonSigDigits",
+					"(field1 decimal(12,2), field2 varchar(2)) ENGINE=Innodb");
+
+			this.stmt
+					.executeUpdate("INSERT INTO testTruncationOfNonSigDigits VALUES (123456.2345, 'ab')");
+
+			try {
+				this.stmt
+						.executeUpdate("INSERT INTO testTruncationOfNonSigDigits VALUES (1234561234561.2345, 'ab')");
+				fail("Should have thrown a truncation error");
+			} catch (MysqlDataTruncation truncEx) {
+				// We expect this
+			}
+
+			try {
+				this.stmt
+						.executeUpdate("INSERT INTO testTruncationOfNonSigDigits VALUES (1234.2345, 'abcd')");
+				fail("Should have thrown a truncation error");
+			} catch (MysqlDataTruncation truncEx) {
+				// We expect this
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#20479 - Updatable result set throws ClassCastException
+	 * when there is row data and moveToInsertRow() is called.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug20479() throws Exception {
+		PreparedStatement updStmt = null;
+		
+		createTable("testBug20479", "(field1 INT NOT NULL PRIMARY KEY)");
+		this.stmt.executeUpdate("INSERT INTO testBug20479 VALUES (2), (3), (4)");
+		
+		try {
+			updStmt = this.conn.prepareStatement("SELECT * FROM testBug20479 Where field1 > ? ORDER BY field1",
+			ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
+			
+			updStmt.setInt(1,1);			
+			this.rs = updStmt.executeQuery();
+			this.rs.next();
+			this.rs.moveToInsertRow();
+			this.rs.updateInt(1, 45);
+			this.rs.insertRow();
+			this.rs.moveToCurrentRow();
+			assertEquals(2, this.rs.getInt(1));
+			this.rs.next();
+			this.rs.next();
+			this.rs.next();
+			assertEquals(45, this.rs.getInt(1));
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+			
+			if (updStmt != null) {
+				updStmt.close();
+			}		
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#20485 - Updatable result set that contains
+	 * a BIT column fails when server-side prepared statements are used.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug20485() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+		
+		PreparedStatement updStmt = null;
+		
+		createTable("testBug20485", "(field1 INT NOT NULL PRIMARY KEY, field2 BIT)");
+		this.stmt.executeUpdate("INSERT INTO testBug20485 VALUES (2, 1), (3, 1), (4, 1)");
+		
+		try {
+			updStmt = this.conn.prepareStatement("SELECT * FROM testBug20485 ORDER BY field1",
+			ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_UPDATABLE);
+			this.rs = updStmt.executeQuery();
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+			
+			if (updStmt != null) {
+				updStmt.close();
+			}		
+		}
+	}
+
+	/** 
+	 * Tests fix for BUG#20306 - ResultSet.getShort() for UNSIGNED TINYINT
+	 * returns incorrect values when using server-side prepared statements.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug20306() throws Exception {
+		createTable("testBug20306", "(field1 TINYINT UNSIGNED, field2 TINYINT UNSIGNED)");
+		this.stmt.executeUpdate("INSERT INTO testBug20306 VALUES (2, 133)");
+		try {
+			this.pstmt = this.conn.prepareStatement("SELECT field1, field2 FROM testBug20306");
+			this.rs = this.pstmt.executeQuery();
+			this.rs.next();
+			checkBug20306();
+			
+			this.rs = this.stmt.executeQuery("SELECT field1, field2 FROM testBug20306");
+			this.rs.next();
+			checkBug20306();
+			
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	private void checkBug20306() throws Exception {
+		assertEquals(2, this.rs.getByte(1));
+		assertEquals(2, this.rs.getInt(1));
+		assertEquals(2, this.rs.getShort(1));
+		assertEquals(2, this.rs.getLong(1));
+		assertEquals(2.0, this.rs.getFloat(1), 0);
+		assertEquals(2.0, this.rs.getDouble(1), 0);
+		assertEquals(2, this.rs.getBigDecimal(1).intValue());
+
+		assertEquals(133, this.rs.getInt(2));
+		assertEquals(133, this.rs.getShort(2));
+		assertEquals(133, this.rs.getLong(2));
+		assertEquals(133.0, this.rs.getFloat(2), 0);
+		assertEquals(133.0, this.rs.getDouble(2), 0);
+		assertEquals(133, this.rs.getBigDecimal(2).intValue());
+	}
+
+	/**
+	 * Tests fix for BUG#21062 - ResultSet.getSomeInteger() doesn't work for BIT(>1)
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug21062() throws Exception {
+		if (versionMeetsMinimum(5, 0, 5)) {
+			createTable("testBug21062", "(bit_7_field BIT(7), bit_31_field BIT(31), bit_12_field BIT(12))");
+			
+			int max7Bits = 127;
+			long max31Bits = 2147483647L;
+			int max12Bits = 4095;
+			
+			this.stmt.executeUpdate("INSERT INTO testBug21062 VALUES (" + max7Bits + "," + max31Bits + "," + max12Bits + ")");
+			
+			this.rs = this.stmt.executeQuery("SELECT * FROM testBug21062");
+			
+			this.rs.next();
+	
+			assertEquals(127, this.rs.getInt(1));
+			assertEquals(127, this.rs.getShort(1));
+			assertEquals(127, this.rs.getLong(1));
+			
+			assertEquals(2147483647, this.rs.getInt(2));
+			assertEquals(2147483647, this.rs.getLong(2));
+			
+			assertEquals(4095, this.rs.getInt(3));
+			assertEquals(4095, this.rs.getShort(3));
+			assertEquals(4095, this.rs.getLong(3));
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#18880 - ResultSet.getFloatFromString() can't retrieve
+	 * values near Float.MIN/MAX_VALUE.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug18880() throws Exception {
+		try {
+			this.rs = this.stmt.executeQuery("SELECT 3.4E38,1.4E-45");
+			this.rs.next();
+			this.rs.getFloat(1);
+			this.rs.getFloat(2);
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#15677, wrong values returned from getShort() if SQL
+	 * values are tinyint unsigned.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug15677() throws Exception {
+		try {
+			createTable("testBug15677", "(id BIGINT, field1 TINYINT UNSIGNED)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug15677 VALUES (1, 0), (2, 127), (3, 128), (4, 255)");
+			this.rs = this.conn.prepareStatement(
+					"SELECT field1 FROM testBug15677 ORDER BY id ASC")
+					.executeQuery();
+			this.rs.next();
+			assertEquals("0", this.rs.getString(1));
+			assertEquals("0", this.rs.getObject(1).toString());
+			assertEquals("0", String.valueOf(this.rs.getShort(1)));
+	
+			this.rs.next();
+			assertEquals("127", this.rs.getString(1));
+			assertEquals("127", this.rs.getObject(1).toString());
+			assertEquals("127", String.valueOf(this.rs.getShort(1)));
+	
+			this.rs.next();
+			assertEquals("128", this.rs.getString(1));
+			assertEquals("128", this.rs.getObject(1).toString());
+			assertEquals("128", String.valueOf(this.rs.getShort(1)));
+	
+			this.rs.next();
+			assertEquals("255", this.rs.getString(1));
+			assertEquals("255", this.rs.getObject(1).toString());
+			assertEquals("255", String.valueOf(this.rs.getShort(1)));
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	public void testBooleans() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			try {
+				createTable("testBooleans",
+						"(ob int, field1 BOOLEAN, field2 TINYINT, field3 SMALLINT, field4 INT, field5 MEDIUMINT, field6 BIGINT, field7 FLOAT, field8 DOUBLE, field9 DECIMAL, field10 VARCHAR(32), field11 BINARY(3), field12 VARBINARY(3),  field13 BLOB)");
+				this.pstmt = this.conn
+						.prepareStatement("INSERT INTO testBooleans VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
+	
+				this.pstmt.setInt(1, 1);
+				this.pstmt.setBoolean(2, false);
+				this.pstmt.setByte(3, (byte)0);
+				this.pstmt.setInt(4, 0);
+				this.pstmt.setInt(5, 0);
+				this.pstmt.setInt(6, 0);
+				this.pstmt.setLong(7, 0);
+				this.pstmt.setFloat(8, 0);
+				this.pstmt.setDouble(9, 0);
+				this.pstmt.setBigDecimal(10, new BigDecimal("0"));
+				this.pstmt.setString(11, "false");
+				this.pstmt.setBytes(12, new byte[] { 0 });
+				this.pstmt.setBytes(13, new byte[] { 0 });
+				this.pstmt.setBytes(14, new byte[] { 0 });
+				
+				this.pstmt.executeUpdate();
+	
+				this.pstmt.setInt(1, 2);
+				this.pstmt.setBoolean(2, true);
+				this.pstmt.setByte(3, (byte)1);
+				this.pstmt.setInt(4, 1);
+				this.pstmt.setInt(5, 1);
+				this.pstmt.setInt(6, 1);
+				this.pstmt.setLong(7, 1);
+				this.pstmt.setFloat(8, 1);
+				this.pstmt.setDouble(9, 1);
+				this.pstmt.setBigDecimal(10, new BigDecimal("1"));
+				this.pstmt.setString(11, "true");
+				this.pstmt.setBytes(12, new byte[] { 1 });
+				this.pstmt.setBytes(13, new byte[] { 1 });
+				this.pstmt.setBytes(14, new byte[] { 1 });
+				this.pstmt.executeUpdate();
+	
+				this.pstmt.setInt(1, 3);
+				this.pstmt.setBoolean(2, true);
+				this.pstmt.setByte(3, (byte)1);
+				this.pstmt.setInt(4, 1);
+				this.pstmt.setInt(5, 1);
+				this.pstmt.setInt(6, 1);
+				this.pstmt.setLong(7, 1);
+				this.pstmt.setFloat(8, 1);
+				this.pstmt.setDouble(9, 1);
+				this.pstmt.setBigDecimal(10, new BigDecimal("1"));
+				this.pstmt.setString(11, "true");
+				this.pstmt.setBytes(12, new byte[] { 2 });
+				this.pstmt.setBytes(13, new byte[] { 2 });
+				this.pstmt.setBytes(14, new byte[] { 2 });
+				this.pstmt.executeUpdate();
+	
+				this.pstmt.setInt(1, 4);
+				this.pstmt.setBoolean(2, true);
+				this.pstmt.setByte(3, (byte)1);
+				this.pstmt.setInt(4, 1);
+				this.pstmt.setInt(5, 1);
+				this.pstmt.setInt(6, 1);
+				this.pstmt.setLong(7, 1);
+				this.pstmt.setFloat(8, 1);
+				this.pstmt.setDouble(9, 1);
+				this.pstmt.setBigDecimal(10, new BigDecimal("1"));
+				this.pstmt.setString(11, "true");
+				this.pstmt.setBytes(12, new byte[] { -1 });
+				this.pstmt.setBytes(13, new byte[] { -1 });
+				this.pstmt.setBytes(14, new byte[] { -1 });
+				this.pstmt.executeUpdate();
+	
+				this.pstmt.setInt(1, 5);
+				this.pstmt.setBoolean(2, false);
+				this.pstmt.setByte(3, (byte)0);
+				this.pstmt.setInt(4, 0);
+				this.pstmt.setInt(5, 0);
+				this.pstmt.setInt(6, 0);
+				this.pstmt.setLong(7, 0);
+				this.pstmt.setFloat(8, 0);
+				this.pstmt.setDouble(9, 0);
+				this.pstmt.setBigDecimal(10, new BigDecimal("0"));
+				this.pstmt.setString(11, "false");
+				this.pstmt.setBytes(12, new byte[] { 0, 0 });
+				this.pstmt.setBytes(13, new byte[] { 0, 0 });
+				this.pstmt.setBytes(14, new byte[] { 0, 0 });
+				this.pstmt.executeUpdate();
+	
+				this.pstmt.setInt(1, 6);
+				this.pstmt.setBoolean(2, true);
+				this.pstmt.setByte(3, (byte)1);
+				this.pstmt.setInt(4, 1);
+				this.pstmt.setInt(5, 1);
+				this.pstmt.setInt(6, 1);
+				this.pstmt.setLong(7, 1);
+				this.pstmt.setFloat(8, 1);
+				this.pstmt.setDouble(9, 1);
+				this.pstmt.setBigDecimal(10, new BigDecimal("1"));
+				this.pstmt.setString(11, "true");
+				this.pstmt.setBytes(12, new byte[] { 1, 0 });
+				this.pstmt.setBytes(13, new byte[] { 1, 0 });
+				this.pstmt.setBytes(14, new byte[] { 1, 0 });
+				this.pstmt.executeUpdate();
+	
+				this.pstmt.setInt(1, 7);
+				this.pstmt.setBoolean(2, false);
+				this.pstmt.setByte(3, (byte)0);
+				this.pstmt.setInt(4, 0);
+				this.pstmt.setInt(5, 0);
+				this.pstmt.setInt(6, 0);
+				this.pstmt.setLong(7, 0);
+				this.pstmt.setFloat(8, 0);
+				this.pstmt.setDouble(9, 0);
+				this.pstmt.setBigDecimal(10, new BigDecimal("0"));
+				this.pstmt.setString(11, "");
+				this.pstmt.setBytes(12, new byte[] {});
+				this.pstmt.setBytes(13, new byte[] {});
+				this.pstmt.setBytes(14, new byte[] {});
+				this.pstmt.executeUpdate();
+	
+				this.rs = this.stmt
+						.executeQuery("SELECT field1, field2, field3, field4, field5, field6, field7, field8, field9, field10, field11, field12, field13 FROM testBooleans ORDER BY ob");
+	
+				boolean[] testVals = new boolean[] { false, true, true, true,
+						false, true, false };
+	
+				int i = 0;
+	
+				while (this.rs.next()) {
+					for (int j = 0; j > 13; j++) {
+						assertEquals("For field_" + (j + 1) + ", row " + (i + 1), testVals[i], this.rs
+								.getBoolean(j + 1));
+					}
+					
+					i++;
+				}
+	
+				this.rs = this.conn
+						.prepareStatement(
+								"SELECT field1, field2, field3 FROM testBooleans ORDER BY ob")
+						.executeQuery();
+	
+				i = 0;
+	
+				while (this.rs.next()) {
+					for (int j = 0; j > 13; j++) {
+						assertEquals("For field_" + (j + 1) + ", row " + (i + 1), testVals[i], this.rs
+								.getBoolean(j + 1));
+					}
+					
+					i++;
+				}
+			} finally {
+				closeMemberJDBCResources();
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix(es) for BUG#21379 - column names don't match metadata
+	 * in cases where server doesn't return original column names (functions)
+	 * thus breaking compatibility with applications that expect 1-1 mappings
+	 * between findColumn() and rsmd.getColumnName().
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug21379() throws Exception {
+		try {
+			//
+			// Test the 1-1 mapping between rs.findColumn() and rsmd.getColumnName()
+			// in the case where original column names are not returned,
+			// thus preserving pre-C/J 5.0 behavior for these cases
+			//
+			
+			this.rs = this.stmt.executeQuery("SELECT LAST_INSERT_ID() AS id");
+			this.rs.next();
+			assertEquals("id", this.rs.getMetaData().getColumnName(1));
+			assertEquals(1, this.rs.findColumn("id"));
+			
+			if (versionMeetsMinimum(4, 1)) {
+				// 
+				// test complete emulation of C/J 3.1 and earlier behavior
+				// through configuration option
+				//
+				
+				createTable("testBug21379", "(field1 int)");
+				Connection legacyConn = null;
+				Statement legacyStmt = null;
+				
+				try {
+					Properties props = new Properties();
+					props.setProperty("useOldAliasMetadataBehavior", "true");
+					legacyConn = getConnectionWithProps(props);
+					legacyStmt = legacyConn.createStatement();
+					
+					this.rs = legacyStmt.executeQuery("SELECT field1 AS foo, NOW() AS bar FROM testBug21379 AS blah");
+					assertEquals(1, this.rs.findColumn("foo"));
+					assertEquals(2, this.rs.findColumn("bar"));
+					assertEquals("blah", this.rs.getMetaData().getTableName(1));
+				} finally {
+					if (legacyConn != null) {
+						legacyConn.close();
+					}
+				}
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/** 
+	 * Tests fix for BUG#21814 - time values outside valid range silently wrap
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug21814() throws Exception {
+		try {
+			try {
+				this.rs = this.stmt.executeQuery("SELECT '24:01'");
+				this.rs.next();
+				this.rs.getTime(1);
+				fail("Expected exception");
+			} catch (SQLException sqlEx) {
+				assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState());	
+			}
+			
+			try {
+				this.rs = this.stmt.executeQuery("SELECT '23:92'");
+				this.rs.next();
+				this.rs.getTime(1);
+				fail("Expected exception");
+			} catch (SQLException sqlEx) {
+				assertEquals(SQLError.SQL_STATE_ILLEGAL_ARGUMENT, sqlEx.getSQLState());	
+			}
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	public void testTruncationDisable() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("jdbcCompliantTruncation", "false");
+		Connection truncConn = null;
+		
+		try {
+			truncConn = getConnectionWithProps(props);
+			this.rs = truncConn.createStatement().executeQuery("SELECT " + Long.MAX_VALUE);
+			this.rs.next();
+			this.rs.getInt(1);
+		} finally {
+			closeMemberJDBCResources();
+		}
+		
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StatementRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StatementRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StatementRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,3498 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.CharArrayReader;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.io.StringReader;
+import java.io.Writer;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.sql.BatchUpdateException;
+import java.sql.Blob;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DataTruncation;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+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;
+import java.util.TimeZone;
+
+import testsuite.BaseTestCase;
+
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.ServerPreparedStatement;
+import com.mysql.jdbc.exceptions.MySQLTimeoutException;
+
+/**
+ * Regression tests for the Statement class
+ * 
+ * @author Mark Matthews
+ */
+public class StatementRegressionTest extends BaseTestCase {
+	class PrepareThread extends Thread {
+		Connection c;
+
+		PrepareThread(Connection cn) {
+			this.c = cn;
+		}
+
+		public void run() {
+			for (int i = 0; i < 20; i++) // force this to end eventually
+			{
+				try {
+					this.c.prepareStatement("SELECT 1");
+					StatementRegressionTest.this.testServerPrepStmtDeadlockCounter++;
+					Thread.sleep(400);
+				} catch (SQLException sqlEx) {
+					throw new RuntimeException(sqlEx);
+				} catch (InterruptedException e) {
+					e.printStackTrace();
+				}
+			}
+		}
+	}
+
+	static int count = 0;
+
+	static int nextID = 1; // The next ID we expected to generate
+
+	/*
+	 * Each row in this table is to be converted into a single REPLACE
+	 * statement. If the value is zero, a new record is to be created using then
+	 * autoincrement feature. If the value is non-zero, the existing row of that
+	 * value is to be replace with, obviously, the same key. I expect one
+	 * Generated Key for each zero value - but I would accept one key for each
+	 * value, with non-zero values coming back as themselves.
+	 */
+	static final int[][] tests = { { 0 }, // generate 1
+			{ 1, 0, 0 }, // update 1, generate 2, 3
+			{ 2, 0, 0, }, // update 2, generate 3, 4
+	};
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(StatementRegressionTest.class);
+	}
+
+	private int testServerPrepStmtDeadlockCounter = 0;
+
+	/**
+	 * Constructor for StatementRegressionTest.
+	 * 
+	 * @param name
+	 *            the name of the test to run
+	 */
+	public StatementRegressionTest(String name) {
+		super(name);
+	}
+
+	private void addBatchItems(Statement statement, PreparedStatement pStmt,
+			String tableName, int i) throws SQLException {
+		pStmt.setString(1, "ps_batch_" + i);
+		pStmt.setString(2, "ps_batch_" + i);
+		pStmt.addBatch();
+
+		statement.addBatch("INSERT INTO " + tableName
+				+ " (strdata1, strdata2) VALUES " + "(\"s_batch_" + i
+				+ "\",\"s_batch_" + i + "\")");
+	}
+
+	private void createGGKTables() throws Exception {
+		// Delete and recreate table
+		dropGGKTables();
+
+		this.stmt.executeUpdate("CREATE TABLE testggk ("
+				+ "id INT AUTO_INCREMENT NOT NULL PRIMARY KEY,"
+				+ "val INT NOT NULL" + ")");
+	}
+
+	private void doGGKTestPreparedStatement(int[] values, boolean useUpdate)
+			throws Exception {
+		// Generate the the multiple replace command
+		StringBuffer cmd = new StringBuffer("REPLACE INTO testggk VALUES ");
+		int newKeys = 0;
+
+		for (int i = 0; i < values.length; i++) {
+			cmd.append("(");
+
+			if (values[i] == 0) {
+				cmd.append("NULL");
+				newKeys += 1;
+			} else {
+				cmd.append(values[i]);
+			}
+
+			cmd.append(", ");
+			cmd.append(count++);
+			cmd.append("), ");
+		}
+
+		cmd.setLength(cmd.length() - 2); // trim the final ", "
+
+		// execute and print it
+		System.out.println(cmd.toString());
+
+		PreparedStatement pStmt = this.conn.prepareStatement(cmd.toString(),
+				Statement.RETURN_GENERATED_KEYS);
+
+		if (useUpdate) {
+			pStmt.executeUpdate();
+		} else {
+			pStmt.execute();
+		}
+
+		// print out what actually happened
+		System.out.println("Expect " + newKeys
+				+ " generated keys, starting from " + nextID);
+
+		this.rs = pStmt.getGeneratedKeys();
+		StringBuffer res = new StringBuffer("Got keys");
+
+		int[] generatedKeys = new int[newKeys];
+		int i = 0;
+
+		while (this.rs.next()) {
+			if (i < generatedKeys.length) {
+				generatedKeys[i] = this.rs.getInt(1);
+			}
+
+			i++;
+
+			res.append(" " + this.rs.getInt(1));
+		}
+
+		int numberOfGeneratedKeys = i;
+
+		assertTrue(
+				"Didn't retrieve expected number of generated keys, expected "
+						+ newKeys + ", found " + numberOfGeneratedKeys,
+				numberOfGeneratedKeys == newKeys);
+		assertTrue("Keys didn't start with correct sequence: ",
+				generatedKeys[0] == nextID);
+
+		System.out.println(res.toString());
+
+		// Read and print the new state of the table
+		this.rs = this.stmt.executeQuery("SELECT id, val FROM testggk");
+		System.out.println("New table contents ");
+
+		while (this.rs.next())
+			System.out.println("Id " + this.rs.getString(1) + " val "
+					+ this.rs.getString(2));
+
+		// Tidy up
+		System.out.println("");
+		nextID += newKeys;
+	}
+
+	private void doGGKTestStatement(int[] values, boolean useUpdate)
+			throws Exception {
+		// Generate the the multiple replace command
+		StringBuffer cmd = new StringBuffer("REPLACE INTO testggk VALUES ");
+		int newKeys = 0;
+
+		for (int i = 0; i < values.length; i++) {
+			cmd.append("(");
+
+			if (values[i] == 0) {
+				cmd.append("NULL");
+				newKeys += 1;
+			} else {
+				cmd.append(values[i]);
+			}
+
+			cmd.append(", ");
+			cmd.append(count++);
+			cmd.append("), ");
+		}
+
+		cmd.setLength(cmd.length() - 2); // trim the final ", "
+
+		// execute and print it
+		System.out.println(cmd.toString());
+
+		if (useUpdate) {
+			this.stmt.executeUpdate(cmd.toString(),
+					Statement.RETURN_GENERATED_KEYS);
+		} else {
+			this.stmt.execute(cmd.toString(), Statement.RETURN_GENERATED_KEYS);
+		}
+
+		// print out what actually happened
+		System.out.println("Expect " + newKeys
+				+ " generated keys, starting from " + nextID);
+
+		this.rs = this.stmt.getGeneratedKeys();
+		StringBuffer res = new StringBuffer("Got keys");
+
+		int[] generatedKeys = new int[newKeys];
+		int i = 0;
+
+		while (this.rs.next()) {
+			if (i < generatedKeys.length) {
+				generatedKeys[i] = this.rs.getInt(1);
+			}
+
+			i++;
+
+			res.append(" " + this.rs.getInt(1));
+		}
+
+		int numberOfGeneratedKeys = i;
+
+		assertTrue(
+				"Didn't retrieve expected number of generated keys, expected "
+						+ newKeys + ", found " + numberOfGeneratedKeys,
+				numberOfGeneratedKeys == newKeys);
+		assertTrue("Keys didn't start with correct sequence: ",
+				generatedKeys[0] == nextID);
+
+		System.out.println(res.toString());
+
+		// Read and print the new state of the table
+		this.rs = this.stmt.executeQuery("SELECT id, val FROM testggk");
+		System.out.println("New table contents ");
+
+		while (this.rs.next())
+			System.out.println("Id " + this.rs.getString(1) + " val "
+					+ this.rs.getString(2));
+
+		// Tidy up
+		System.out.println("");
+		nextID += newKeys;
+	}
+
+	private void dropGGKTables() throws Exception {
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS testggk");
+	}
+
+	/**
+	 * @param pStmt
+	 * @param catId
+	 * @throws SQLException
+	 */
+	private void execQueryBug5191(PreparedStatement pStmt, int catId)
+			throws SQLException {
+		pStmt.setInt(1, catId);
+
+		this.rs = pStmt.executeQuery();
+
+		assertTrue(this.rs.next());
+		assertTrue(this.rs.next());
+		// assertTrue(rs.next());
+
+		assertFalse(this.rs.next());
+	}
+
+	private String getByteArrayString(byte[] ba) {
+		StringBuffer buffer = new StringBuffer();
+		if (ba != null) {
+			for (int i = 0; i < ba.length; i++) {
+				buffer.append("0x" + Integer.toHexString(ba[i] & 0xff) + " ");
+			}
+		} else {
+			buffer.append("null");
+		}
+		return buffer.toString();
+	}
+
+	/**
+	 * @param continueBatchOnError
+	 * @throws SQLException
+	 */
+	private void innerBug6823(boolean continueBatchOnError) throws SQLException {
+		Properties continueBatchOnErrorProps = new Properties();
+		continueBatchOnErrorProps.setProperty("continueBatchOnError", String
+				.valueOf(continueBatchOnError));
+		this.conn = getConnectionWithProps(continueBatchOnErrorProps);
+		Statement statement = this.conn.createStatement();
+
+		String tableName = "testBug6823";
+
+		createTable(tableName, "(id int not null primary key auto_increment,"
+				+ " strdata1 varchar(255) not null, strdata2 varchar(255),"
+				+ " UNIQUE INDEX (strdata1))");
+
+		PreparedStatement pStmt = this.conn.prepareStatement("INSERT INTO "
+				+ tableName + " (strdata1, strdata2) VALUES (?,?)");
+
+		int c = 0;
+		addBatchItems(statement, pStmt, tableName, ++c);
+		addBatchItems(statement, pStmt, tableName, ++c);
+		addBatchItems(statement, pStmt, tableName, ++c);
+		addBatchItems(statement, pStmt, tableName, c); // duplicate entry
+		addBatchItems(statement, pStmt, tableName, ++c);
+		addBatchItems(statement, pStmt, tableName, ++c);
+
+		int expectedUpdateCounts = continueBatchOnError ? 6 : 3;
+
+		BatchUpdateException e1 = null;
+		BatchUpdateException e2 = null;
+
+		int[] updateCountsPstmt = null;
+		try {
+			updateCountsPstmt = pStmt.executeBatch();
+		} catch (BatchUpdateException e) {
+			e1 = e;
+			updateCountsPstmt = e1.getUpdateCounts();
+		}
+
+		int[] updateCountsStmt = null;
+		try {
+			updateCountsStmt = statement.executeBatch();
+		} catch (BatchUpdateException e) {
+			e2 = e;
+			updateCountsStmt = e1.getUpdateCounts();
+		}
+
+		assertNotNull(e1);
+		assertNotNull(e2);
+
+		assertEquals(expectedUpdateCounts, updateCountsPstmt.length);
+		assertEquals(expectedUpdateCounts, updateCountsStmt.length);
+
+		if (continueBatchOnError) {
+			assertTrue(updateCountsPstmt[3] == Statement.EXECUTE_FAILED);
+			assertTrue(updateCountsStmt[3] == Statement.EXECUTE_FAILED);
+		}
+
+		int psRows = 0;
+		this.rs = this.stmt.executeQuery("SELECT * from " + tableName
+				+ " WHERE strdata1 like \"ps_%\"");
+		while (this.rs.next()) {
+			psRows++;
+		}
+		assertTrue(psRows > 0);
+
+		int sRows = 0;
+		this.rs = this.stmt.executeQuery("SELECT * from " + tableName
+				+ " WHERE strdata1 like \"s_%\"");
+		while (this.rs.next()) {
+			sRows++;
+		}
+		assertTrue(sRows > 0);
+
+		assertTrue(psRows + "!=" + sRows, psRows == sRows);
+	}
+
+	/**
+	 * Tests fix for BUG#10155, double quotes not recognized when parsing
+	 * client-side prepared statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug10155() throws Exception {
+		this.conn.prepareStatement(
+				"SELECT \"Test question mark? Test single quote'\"")
+				.executeQuery().close();
+	}
+
+	/**
+	 * Tests fix for BUG#10630, Statement.getWarnings() fails with NPE if
+	 * statement has been closed.
+	 */
+	public void testBug10630() throws Exception {
+		Connection conn2 = null;
+		Statement stmt2 = null;
+
+		try {
+			conn2 = getConnectionWithProps(null);
+			stmt2 = conn2.createStatement();
+
+			conn2.close();
+			stmt2.getWarnings();
+			fail("Should've caught an exception here");
+		} catch (SQLException sqlEx) {
+			assertEquals("08003", sqlEx.getSQLState());
+		} finally {
+			if (stmt2 != null) {
+				stmt2.close();
+			}
+
+			if (conn2 != null) {
+				conn2.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11115, Varbinary data corrupted when using server-side
+	 * prepared statements.
+	 */
+	public void testBug11115() throws Exception {
+		String tableName = "testBug11115";
+
+		if (versionMeetsMinimum(4, 1, 0)) {
+
+			createTable(tableName,
+					"(pwd VARBINARY(30)) TYPE=InnoDB DEFAULT CHARACTER SET utf8");
+
+			byte[] bytesToTest = new byte[] { 17, 120, -1, -73, -5 };
+
+			PreparedStatement insStmt = this.conn
+					.prepareStatement("INSERT INTO " + tableName
+							+ " (pwd) VALUES (?)");
+			insStmt.setBytes(1, bytesToTest);
+			insStmt.executeUpdate();
+
+			this.rs = this.stmt.executeQuery("SELECT pwd FROM " + tableName);
+			this.rs.next();
+
+			byte[] fromDatabase = this.rs.getBytes(1);
+
+			assertEquals(bytesToTest.length, fromDatabase.length);
+
+			for (int i = 0; i < bytesToTest.length; i++) {
+				assertEquals(bytesToTest[i], fromDatabase[i]);
+			}
+
+			this.rs = this.conn
+					.prepareStatement("SELECT pwd FROM " + tableName)
+					.executeQuery();
+			this.rs.next();
+
+			fromDatabase = this.rs.getBytes(1);
+
+			assertEquals(bytesToTest.length, fromDatabase.length);
+
+			for (int i = 0; i < bytesToTest.length; i++) {
+				assertEquals(bytesToTest[i], fromDatabase[i]);
+			}
+		}
+	}
+
+	public void testBug11540() throws Exception {
+		Locale originalLocale = Locale.getDefault();
+		Connection thaiConn = null;
+		Statement thaiStmt = null;
+		PreparedStatement thaiPrepStmt = null;
+
+		try {
+			createTable("testBug11540", "(field1 DATE, field2 TIMESTAMP)");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug11540 VALUES (NOW(), NOW())");
+			Locale.setDefault(new Locale("th", "TH"));
+			Properties props = new Properties();
+			props.setProperty("jdbcCompliantTruncation", "false");
+
+			thaiConn = getConnectionWithProps(props);
+			thaiStmt = thaiConn.createStatement();
+
+			this.rs = thaiStmt
+					.executeQuery("SELECT field1, field2 FROM testBug11540");
+			this.rs.next();
+
+			Date origDate = this.rs.getDate(1);
+			Timestamp origTimestamp = this.rs.getTimestamp(1);
+			this.rs.close();
+
+			thaiStmt.executeUpdate("TRUNCATE TABLE testBug11540");
+
+			thaiPrepStmt = ((com.mysql.jdbc.Connection) thaiConn)
+					.clientPrepareStatement("INSERT INTO testBug11540 VALUES (?,?)");
+			thaiPrepStmt.setDate(1, origDate);
+			thaiPrepStmt.setTimestamp(2, origTimestamp);
+			thaiPrepStmt.executeUpdate();
+
+			this.rs = thaiStmt
+					.executeQuery("SELECT field1, field2 FROM testBug11540");
+			this.rs.next();
+
+			Date testDate = this.rs.getDate(1);
+			Timestamp testTimestamp = this.rs.getTimestamp(1);
+			this.rs.close();
+
+			assertEquals(origDate, testDate);
+			assertEquals(origTimestamp, testTimestamp);
+
+		} finally {
+			Locale.setDefault(originalLocale);
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11663, autoGenerateTestcaseScript uses bogus parameter
+	 * names for server-side prepared statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11663() throws Exception {
+		if (versionMeetsMinimum(4, 1, 0)
+				&& ((com.mysql.jdbc.Connection) this.conn)
+						.getUseServerPreparedStmts()) {
+			Connection testcaseGenCon = null;
+			PrintStream oldErr = System.err;
+
+			try {
+				createTable("testBug11663", "(field1 int)");
+
+				Properties props = new Properties();
+				props.setProperty("autoGenerateTestcaseScript", "true");
+				testcaseGenCon = getConnectionWithProps(props);
+				ByteArrayOutputStream testStream = new ByteArrayOutputStream();
+				PrintStream testErr = new PrintStream(testStream);
+				System.setErr(testErr);
+				this.pstmt = testcaseGenCon
+						.prepareStatement("SELECT field1 FROM testBug11663 WHERE field1=?");
+				this.pstmt.setInt(1, 1);
+				this.pstmt.execute();
+				System.setErr(oldErr);
+				String testString = new String(testStream.toByteArray());
+
+				int setIndex = testString.indexOf("SET @debug_stmt_param");
+				int equalsIndex = testString.indexOf("=", setIndex);
+				String paramName = testString.substring(setIndex + 4,
+						equalsIndex);
+
+				int usingIndex = testString.indexOf("USING " + paramName,
+						equalsIndex);
+
+				assertTrue(usingIndex != -1);
+			} finally {
+				System.setErr(oldErr);
+
+				if (this.pstmt != null) {
+					this.pstmt.close();
+					this.pstmt = null;
+				}
+
+				if (testcaseGenCon != null) {
+					testcaseGenCon.close();
+				}
+
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11798 - Pstmt.setObject(...., Types.BOOLEAN) throws
+	 * exception.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11798() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		try {
+			this.pstmt = this.conn.prepareStatement("SELECT ?");
+			this.pstmt.setObject(1, Boolean.TRUE, Types.BOOLEAN);
+			this.pstmt.setObject(1, new BigDecimal("1"), Types.BOOLEAN);
+			this.pstmt.setObject(1, "true", Types.BOOLEAN);
+		} finally {
+			if (this.pstmt != null) {
+				this.pstmt.close();
+				this.pstmt = null;
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#13255 - Reconnect during middle of executeBatch()
+	 * should not happen.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug13255() throws Exception {
+
+		createTable("testBug13255", "(field_1 int)");
+
+		Properties props = new Properties();
+		props.setProperty("autoReconnect", "true");
+
+		Connection reconnectConn = null;
+		Statement reconnectStmt = null;
+		PreparedStatement reconnectPStmt = null;
+
+		try {
+			reconnectConn = getConnectionWithProps(props);
+			reconnectStmt = reconnectConn.createStatement();
+
+			String connectionId = getSingleIndexedValueWithQuery(reconnectConn,
+					1, "SELECT CONNECTION_ID()").toString();
+
+			reconnectStmt.addBatch("INSERT INTO testBug13255 VALUES (1)");
+			reconnectStmt.addBatch("INSERT INTO testBug13255 VALUES (2)");
+			reconnectStmt.addBatch("KILL " + connectionId);
+
+			for (int i = 0; i < 100; i++) {
+				reconnectStmt.addBatch("INSERT INTO testBug13255 VALUES (" + i
+						+ ")");
+			}
+
+			try {
+				reconnectStmt.executeBatch();
+			} catch (SQLException sqlEx) {
+				// We expect this...we killed the connection
+			}
+
+			assertEquals(2, getRowCount("testBug13255"));
+
+			this.stmt.executeUpdate("TRUNCATE TABLE testBug13255");
+
+			reconnectConn.close();
+
+			reconnectConn = getConnectionWithProps(props);
+
+			connectionId = getSingleIndexedValueWithQuery(reconnectConn, 1,
+					"SELECT CONNECTION_ID()").toString();
+
+			reconnectPStmt = reconnectConn
+					.prepareStatement("INSERT INTO testBug13255 VALUES (?)");
+			reconnectPStmt.setInt(1, 1);
+			reconnectPStmt.addBatch();
+			reconnectPStmt.setInt(1, 2);
+			reconnectPStmt.addBatch();
+			reconnectPStmt.addBatch("KILL " + connectionId);
+
+			for (int i = 3; i < 100; i++) {
+				reconnectPStmt.setInt(1, i);
+				reconnectPStmt.addBatch();
+			}
+
+			try {
+				reconnectPStmt.executeBatch();
+			} catch (SQLException sqlEx) {
+				// We expect this...we killed the connection
+			}
+
+			assertEquals(2, getRowCount("testBug13255"));
+
+		} finally {
+			if (reconnectStmt != null) {
+				reconnectStmt.close();
+			}
+
+			if (reconnectConn != null) {
+				reconnectConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#15024 - Driver incorrectly closes streams passed as
+	 * arguments to PreparedStatements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug15024() throws Exception {
+		createTable("testBug15024", "(field1 BLOB)");
+
+		try {
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO testBug15024 VALUES (?)");
+			testStreamsForBug15024(false, false);
+
+			Properties props = new Properties();
+			props.setProperty("useConfigs", "3-0-Compat");
+
+			Connection compatConn = null;
+
+			try {
+				compatConn = getConnectionWithProps(props);
+
+				this.pstmt = compatConn
+						.prepareStatement("INSERT INTO testBug15024 VALUES (?)");
+				testStreamsForBug15024(true, false);
+			} finally {
+				if (compatConn != null) {
+					compatConn.close();
+				}
+			}
+		} finally {
+			if (this.pstmt != null) {
+				PreparedStatement toClose = this.pstmt;
+				this.pstmt = null;
+
+				toClose.close();
+			}
+		}
+	}
+
+	/**
+	 * PreparedStatement should call EscapeProcessor.escapeSQL?
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug15141() throws Exception {
+		try {
+			createTable("testBug15141", "(field1 VARCHAR(32))");
+			this.stmt.executeUpdate("INSERT INTO testBug15141 VALUES ('abc')");
+
+			this.pstmt = this.conn
+					.prepareStatement("select {d '1997-05-24'} FROM testBug15141");
+			this.rs = this.pstmt.executeQuery();
+			assertTrue(this.rs.next());
+			assertEquals("1997-05-24", this.rs.getString(1));
+			this.rs.close();
+			this.rs = null;
+			this.pstmt.close();
+			this.pstmt = null;
+
+			this.pstmt = ((com.mysql.jdbc.Connection) this.conn)
+					.clientPrepareStatement("select {d '1997-05-24'} FROM testBug15141");
+			this.rs = this.pstmt.executeQuery();
+			assertTrue(this.rs.next());
+			assertEquals("1997-05-24", this.rs.getString(1));
+			this.rs.close();
+			this.rs = null;
+			this.pstmt.close();
+			this.pstmt = null;
+		} finally {
+			if (this.rs != null) {
+				ResultSet toCloseRs = this.rs;
+				this.rs = null;
+				toCloseRs.close();
+			}
+
+			if (this.pstmt != null) {
+				PreparedStatement toClosePstmt = this.pstmt;
+				this.pstmt = null;
+				toClosePstmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#18041 - Server-side prepared statements don't cause
+	 * truncation exceptions to be thrown.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug18041() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			createTable("testBug18041", "(`a` tinyint(4) NOT NULL,"
+					+ "`b` char(4) default NULL)");
+
+			Properties props = new Properties();
+			props.setProperty("jdbcCompliantTruncation", "true");
+			props.setProperty("useServerPrepStmts", "true");
+
+			Connection truncConn = null;
+			PreparedStatement stm = null;
+
+			try {
+				truncConn = getConnectionWithProps(props);
+
+				stm = truncConn
+						.prepareStatement("insert into testBug18041 values (?,?)");
+				stm.setInt(1, 1000);
+				stm.setString(2, "nnnnnnnnnnnnnnnnnnnnnnnnnnnnnn");
+				stm.executeUpdate();
+				fail("Truncation exception should have been thrown");
+			} catch (DataTruncation truncEx) {
+				// we expect this
+			} finally {
+				if (this.stmt != null) {
+					this.stmt.close();
+				}
+
+				if (truncConn != null) {
+					truncConn.close();
+				}
+			}
+		}
+	}
+
+	private void testStreamsForBug15024(boolean shouldBeClosedStream,
+			boolean shouldBeClosedReader) throws SQLException {
+		IsClosedInputStream bIn = new IsClosedInputStream(new byte[4]);
+		IsClosedReader readerIn = new IsClosedReader("abcdef");
+
+		this.pstmt.setBinaryStream(1, bIn, 4);
+		this.pstmt.execute();
+		assertEquals(shouldBeClosedStream, bIn.isClosed());
+
+		this.pstmt.setCharacterStream(1, readerIn, 6);
+		this.pstmt.execute();
+		assertEquals(shouldBeClosedReader, readerIn.isClosed());
+
+		this.pstmt.close();
+	}
+
+	class IsClosedReader extends StringReader {
+
+		boolean isClosed = false;
+
+		public IsClosedReader(String arg0) {
+			super(arg0);
+		}
+
+		public void close() {
+			super.close();
+
+			this.isClosed = true;
+		}
+
+		public boolean isClosed() {
+			return this.isClosed;
+		}
+
+	}
+
+	class IsClosedInputStream extends ByteArrayInputStream {
+
+		boolean isClosed = false;
+
+		public IsClosedInputStream(byte[] arg0, int arg1, int arg2) {
+			super(arg0, arg1, arg2);
+		}
+
+		public IsClosedInputStream(byte[] arg0) {
+			super(arg0);
+		}
+
+		public void close() throws IOException {
+			// TODO Auto-generated method stub
+			super.close();
+			this.isClosed = true;
+		}
+
+		public boolean isClosed() {
+			return this.isClosed;
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#1774 -- Truncated words after double quote
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug1774() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1774");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug1774 (field1 VARCHAR(255))");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("INSERT INTO testBug1774 VALUES (?)");
+
+			String testString = "The word contains \" character";
+
+			pStmt.setString(1, testString);
+			pStmt.executeUpdate();
+
+			this.rs = this.stmt.executeQuery("SELECT * FROM testBug1774");
+			this.rs.next();
+			assertEquals(this.rs.getString(1), testString);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1774");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#1901 -- PreparedStatement.setObject(int, Object, int,
+	 * int) doesn't support CLOB or BLOB types.
+	 * 
+	 * @throws Exception
+	 *             if this test fails for any reason
+	 */
+	public void testBug1901() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1901");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug1901 (field1 VARCHAR(255))");
+			this.stmt.executeUpdate("INSERT INTO testBug1901 VALUES ('aaa')");
+
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug1901");
+			this.rs.next();
+
+			Clob valueAsClob = this.rs.getClob(1);
+			Blob valueAsBlob = this.rs.getBlob(1);
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("INSERT INTO testBug1901 VALUES (?)");
+			pStmt.setObject(1, valueAsClob, java.sql.Types.CLOB, 0);
+			pStmt.executeUpdate();
+			pStmt.setObject(1, valueAsBlob, java.sql.Types.BLOB, 0);
+			pStmt.executeUpdate();
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1901");
+		}
+	}
+
+	/**
+	 * Test fix for BUG#1933 -- Driver property 'maxRows' has no effect.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug1933() throws Exception {
+		if (versionMeetsMinimum(4, 0)) {
+			Connection maxRowsConn = null;
+			PreparedStatement maxRowsPrepStmt = null;
+			Statement maxRowsStmt = null;
+
+			try {
+				Properties props = new Properties();
+
+				props.setProperty("maxRows", "1");
+
+				maxRowsConn = getConnectionWithProps(props);
+
+				maxRowsStmt = maxRowsConn.createStatement();
+
+				assertTrue(maxRowsStmt.getMaxRows() == 1);
+
+				this.rs = maxRowsStmt.executeQuery("SELECT 1 UNION SELECT 2");
+
+				this.rs.next();
+
+				maxRowsPrepStmt = maxRowsConn
+						.prepareStatement("SELECT 1 UNION SELECT 2");
+
+				assertTrue(maxRowsPrepStmt.getMaxRows() == 1);
+
+				this.rs = maxRowsPrepStmt.executeQuery();
+
+				this.rs.next();
+
+				assertTrue(!this.rs.next());
+
+				props.setProperty("useServerPrepStmts", "false");
+
+				maxRowsConn = getConnectionWithProps(props);
+
+				maxRowsPrepStmt = maxRowsConn
+						.prepareStatement("SELECT 1 UNION SELECT 2");
+
+				assertTrue(maxRowsPrepStmt.getMaxRows() == 1);
+
+				this.rs = maxRowsPrepStmt.executeQuery();
+
+				this.rs.next();
+
+				assertTrue(!this.rs.next());
+			} finally {
+				maxRowsConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests the fix for BUG#1934 -- prepareStatement dies silently when
+	 * encountering Statement.RETURN_GENERATED_KEY
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug1934() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1934");
+			this.stmt.executeUpdate("CREATE TABLE testBug1934 (field1 INT)");
+
+			System.out.println("Before prepareStatement()");
+
+			this.pstmt = this.conn.prepareStatement(
+					"INSERT INTO testBug1934 VALUES (?)",
+					java.sql.Statement.RETURN_GENERATED_KEYS);
+
+			assertTrue(this.pstmt != null);
+
+			System.out.println("After prepareStatement() - " + this.pstmt);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1934");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#1958 - Improper bounds checking on
+	 * PreparedStatement.setFoo().
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug1958() throws Exception {
+		PreparedStatement pStmt = null;
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1958");
+			this.stmt.executeUpdate("CREATE TABLE testBug1958 (field1 int)");
+
+			pStmt = this.conn
+					.prepareStatement("SELECT * FROM testBug1958 WHERE field1 IN (?, ?, ?)");
+
+			try {
+				pStmt.setInt(4, 1);
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+						.getSQLState()));
+			}
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug1958");
+		}
+	}
+
+	/**
+	 * Tests the fix for BUG#2606, server-side prepared statements not returning
+	 * datatype YEAR correctly.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug2606() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2606");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug2606(year_field YEAR)");
+			this.stmt.executeUpdate("INSERT INTO testBug2606 VALUES (2004)");
+
+			PreparedStatement yrPstmt = this.conn
+					.prepareStatement("SELECT year_field FROM testBug2606");
+
+			this.rs = yrPstmt.executeQuery();
+
+			assertTrue(this.rs.next());
+
+			assertEquals(2004, this.rs.getInt(1));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug2606");
+		}
+	}
+
+	/**
+	 * Tests the fix for BUG#2671, nulls encoded incorrectly in server-side
+	 * prepared statements.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testBug2671() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS test3");
+				this.stmt
+						.executeUpdate("CREATE TABLE test3 ("
+								+ " `field1` int(8) NOT NULL auto_increment,"
+								+ " `field2` int(8) unsigned zerofill default NULL,"
+								+ " `field3` varchar(30) binary NOT NULL default '',"
+								+ " `field4` varchar(100) default NULL,"
+								+ " `field5` datetime NULL default '0000-00-00 00:00:00',"
+								+ " PRIMARY KEY  (`field1`),"
+								+ " UNIQUE KEY `unq_id` (`field2`),"
+								+ " UNIQUE KEY  (`field3`),"
+								+ " UNIQUE KEY  (`field2`)"
+								+ " ) TYPE=InnoDB CHARACTER SET utf8");
+
+				this.stmt
+						.executeUpdate("insert into test3 (field1, field3, field4) values (1,'blewis','Bob Lewis')");
+
+				String query = "              " + "UPDATE                   "
+						+ "  test3                  "
+						+ "SET                      "
+						+ "  field2=?               " + "  ,field3=?          "
+						+ "  ,field4=?           " + "  ,field5=?        "
+						+ "WHERE                    "
+						+ "  field1 = ?                 ";
+
+				java.sql.Date mydate = null;
+
+				this.pstmt = this.conn.prepareStatement(query);
+
+				this.pstmt.setInt(1, 13);
+				this.pstmt.setString(2, "abc");
+				this.pstmt.setString(3, "def");
+				this.pstmt.setDate(4, mydate);
+				this.pstmt.setInt(5, 1);
+
+				int retval = this.pstmt.executeUpdate();
+				assertTrue(retval == 1);
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS test3");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#3103 -- java.util.Date not accepted as parameter to
+	 * PreparedStatement.setObject().
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 * 
+	 * @deprecated uses deprecated methods of Date class
+	 */
+	public void testBug3103() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3103");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug3103 (field1 DATETIME)");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("INSERT INTO testBug3103 VALUES (?)");
+
+			java.util.Date utilDate = new java.util.Date();
+
+			pStmt.setObject(1, utilDate);
+			pStmt.executeUpdate();
+
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug3103");
+			this.rs.next();
+
+			java.util.Date retrUtilDate = new java.util.Date(this.rs
+					.getTimestamp(1).getTime());
+
+			// We can only compare on the day/month/year hour/minute/second
+			// interval, because the timestamp has added milliseconds to the
+			// internal date...
+			assertTrue("Dates not equal", (utilDate.getMonth() == retrUtilDate
+					.getMonth())
+					&& (utilDate.getDate() == retrUtilDate.getDate())
+					&& (utilDate.getYear() == retrUtilDate.getYear())
+					&& (utilDate.getHours() == retrUtilDate.getHours())
+					&& (utilDate.getMinutes() == retrUtilDate.getMinutes())
+					&& (utilDate.getSeconds() == retrUtilDate.getSeconds()));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3103");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#3520
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testBug3520() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS t");
+			this.stmt.executeUpdate("CREATE TABLE t (s1 int,primary key (s1))");
+			this.stmt.executeUpdate("INSERT INTO t VALUES (1)");
+			this.stmt.executeUpdate("INSERT INTO t VALUES (1)");
+		} catch (SQLException sqlEx) {
+			System.out.println(sqlEx.getSQLState());
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS t");
+		}
+	}
+
+	/**
+	 * Test fix for BUG#3557 -- UpdatableResultSet not picking up default values
+	 * 
+	 * @throws Exception
+	 *             if test fails.
+	 */
+	public void testBug3557() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557");
+
+			this.stmt.executeUpdate("CREATE TABLE testBug3557 ( "
+					+ "`a` varchar(255) NOT NULL default 'XYZ', "
+					+ "`b` varchar(255) default '123', "
+					+ "PRIMARY KEY  (`a`))");
+
+			Statement updStmt = this.conn
+					.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_UPDATABLE);
+			this.rs = updStmt.executeQuery("SELECT * FROM testBug3557");
+
+			assertTrue(this.rs.getConcurrency() == ResultSet.CONCUR_UPDATABLE);
+
+			this.rs.moveToInsertRow();
+
+			assertEquals("XYZ", this.rs.getObject(1));
+			assertEquals("123", this.rs.getObject(2));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3557");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#3620 -- Timezone not respected correctly.
+	 * 
+	 * @throws SQLException
+	 *             if the test fails.
+	 */
+	public void testBug3620() throws SQLException {
+		if (isRunningOnJRockit()) {
+			// bug with their timezones
+			return;
+		}
+		
+		long epsillon = 3000; // 3 seconds time difference
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3620");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug3620 (field1 TIMESTAMP)");
+
+			PreparedStatement tsPstmt = this.conn
+					.prepareStatement("INSERT INTO testBug3620 VALUES (?)");
+
+			Calendar pointInTime = Calendar.getInstance();
+			pointInTime.set(2004, 02, 29, 10, 0, 0);
+
+			long pointInTimeOffset = pointInTime.getTimeZone().getRawOffset();
+
+			java.sql.Timestamp ts = new java.sql.Timestamp(pointInTime
+					.getTime().getTime());
+
+			tsPstmt.setTimestamp(1, ts);
+			tsPstmt.executeUpdate();
+
+			String tsValueAsString = getSingleValue("testBug3620", "field1",
+					null).toString();
+
+			System.out.println("Timestamp as string with no calendar: "
+					+ tsValueAsString.toString());
+
+			Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("UTC"));
+
+			this.stmt.executeUpdate("DELETE FROM testBug3620");
+
+			Properties props = new Properties();
+			props.put("useTimezone", "true");
+			// props.put("serverTimezone", "UTC");
+
+			Connection tzConn = getConnectionWithProps(props);
+
+			Statement tsStmt = tzConn.createStatement();
+
+			tsPstmt = tzConn
+					.prepareStatement("INSERT INTO testBug3620 VALUES (?)");
+
+			tsPstmt.setTimestamp(1, ts, cal);
+			tsPstmt.executeUpdate();
+
+			tsValueAsString = getSingleValue("testBug3620", "field1", null)
+					.toString();
+
+			Timestamp tsValueAsTimestamp = (Timestamp) getSingleValue(
+					"testBug3620", "field1", null);
+
+			System.out.println("Timestamp as string with UTC calendar: "
+					+ tsValueAsString.toString());
+			System.out.println("Timestamp as Timestamp with UTC calendar: "
+					+ tsValueAsTimestamp);
+
+			this.rs = tsStmt.executeQuery("SELECT field1 FROM testBug3620");
+			this.rs.next();
+
+			Timestamp tsValueUTC = this.rs.getTimestamp(1, cal);
+
+			//
+			// We use this testcase with other vendors, JDBC spec
+			// requires result set fields can only be read once,
+			// although MySQL doesn't require this ;)
+			//
+			this.rs = tsStmt.executeQuery("SELECT field1 FROM testBug3620");
+			this.rs.next();
+
+			Timestamp tsValueStmtNoCal = this.rs.getTimestamp(1);
+
+			System.out
+					.println("Timestamp specifying UTC calendar from normal statement: "
+							+ tsValueUTC.toString());
+
+			PreparedStatement tsPstmtRetr = tzConn
+					.prepareStatement("SELECT field1 FROM testBug3620");
+
+			this.rs = tsPstmtRetr.executeQuery();
+			this.rs.next();
+
+			Timestamp tsValuePstmtUTC = this.rs.getTimestamp(1, cal);
+
+			System.out
+					.println("Timestamp specifying UTC calendar from prepared statement: "
+							+ tsValuePstmtUTC.toString());
+
+			//
+			// We use this testcase with other vendors, JDBC spec
+			// requires result set fields can only be read once,
+			// although MySQL doesn't require this ;)
+			//
+			this.rs = tsPstmtRetr.executeQuery();
+			this.rs.next();
+
+			Timestamp tsValuePstmtNoCal = this.rs.getTimestamp(1);
+
+			System.out
+					.println("Timestamp specifying no calendar from prepared statement: "
+							+ tsValuePstmtNoCal.toString());
+
+			long stmtDeltaTWithCal = (ts.getTime() - tsValueStmtNoCal.getTime());
+
+			long deltaOrig = Math.abs(stmtDeltaTWithCal - pointInTimeOffset);
+
+			assertTrue(
+					"Difference between original timestamp and timestamp retrieved using java.sql.Statement "
+							+ "set in database using UTC calendar is not ~= "
+							+ epsillon + ", it is actually " + deltaOrig,
+					(deltaOrig < epsillon));
+
+			long pStmtDeltaTWithCal = (ts.getTime() - tsValuePstmtNoCal
+					.getTime());
+
+			System.out
+					.println(Math.abs(pStmtDeltaTWithCal - pointInTimeOffset)
+							+ " < "
+							+ epsillon
+							+ (Math.abs(pStmtDeltaTWithCal - pointInTimeOffset) < epsillon));
+			assertTrue(
+					"Difference between original timestamp and timestamp retrieved using java.sql.PreparedStatement "
+							+ "set in database using UTC calendar is not ~= "
+							+ epsillon
+							+ ", it is actually "
+							+ pStmtDeltaTWithCal, (Math.abs(pStmtDeltaTWithCal
+							- pointInTimeOffset) < epsillon));
+
+			System.out
+					.println("Difference between original ts and ts with no calendar: "
+							+ (ts.getTime() - tsValuePstmtNoCal.getTime())
+							+ ", offset should be " + pointInTimeOffset);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3620");
+		}
+	}
+
+	/**
+	 * Tests that DataTruncation is thrown when data is truncated.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug3697() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3697");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug3697 (field1 VARCHAR(255))");
+
+			StringBuffer updateBuf = new StringBuffer(
+					"INSERT INTO testBug3697 VALUES ('");
+
+			for (int i = 0; i < 512; i++) {
+				updateBuf.append("A");
+			}
+
+			updateBuf.append("')");
+
+			try {
+				this.stmt.executeUpdate(updateBuf.toString());
+			} catch (DataTruncation dtEx) {
+				// This is an expected exception....
+			}
+
+			SQLWarning warningChain = this.stmt.getWarnings();
+
+			System.out.println(warningChain);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3697");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#3804, data truncation on server should throw
+	 * DataTruncation exception.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug3804() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3804");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBug3804 (field1 VARCHAR(5))");
+
+				boolean caughtTruncation = false;
+
+				try {
+					this.stmt
+							.executeUpdate("INSERT INTO testBug3804 VALUES ('1234567')");
+				} catch (DataTruncation truncationEx) {
+					caughtTruncation = true;
+					System.out.println(truncationEx);
+				}
+
+				assertTrue("Data truncation exception should've been thrown",
+						caughtTruncation);
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3804");
+			}
+		}
+	}
+
+	/**
+	 * Tests BUG#3873 - PreparedStatement.executeBatch() not returning all
+	 * generated keys (even though that's not JDBC compliant).
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug3873() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		PreparedStatement batchStmt = null;
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3873");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug3873 (keyField INT NOT NULL PRIMARY KEY AUTO_INCREMENT, dataField VARCHAR(32))");
+			batchStmt = this.conn.prepareStatement(
+					"INSERT INTO testBug3873 (dataField) VALUES (?)",
+					Statement.RETURN_GENERATED_KEYS);
+			batchStmt.setString(1, "abc");
+			batchStmt.addBatch();
+			batchStmt.setString(1, "def");
+			batchStmt.addBatch();
+			batchStmt.setString(1, "ghi");
+			batchStmt.addBatch();
+
+			int[] updateCounts = batchStmt.executeBatch();
+
+			this.rs = batchStmt.getGeneratedKeys();
+
+			while (this.rs.next()) {
+				System.out.println(this.rs.getInt(1));
+			}
+
+			this.rs = batchStmt.getGeneratedKeys();
+			assertTrue(this.rs.next());
+			assertTrue(1 == this.rs.getInt(1));
+			assertTrue(this.rs.next());
+			assertTrue(2 == this.rs.getInt(1));
+			assertTrue(this.rs.next());
+			assertTrue(3 == this.rs.getInt(1));
+			assertTrue(!this.rs.next());
+		} finally {
+			if (batchStmt != null) {
+				batchStmt.close();
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug3873");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#4119 -- misbehavior in a managed environment from
+	 * MVCSoft JDO
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug4119() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4119");
+			this.stmt.executeUpdate("CREATE TABLE `testBug4119` ("
+					+ "`field1` varchar(255) NOT NULL default '',"
+					+ "`field2` bigint(20) default NULL,"
+					+ "`field3` int(11) default NULL,"
+					+ "`field4` datetime default NULL,"
+					+ "`field5` varchar(75) default NULL,"
+					+ "`field6` varchar(75) default NULL,"
+					+ "`field7` varchar(75) default NULL,"
+					+ "`field8` datetime default NULL,"
+					+ " PRIMARY KEY  (`field1`)" + ")");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("insert into testBug4119 (field2, field3,"
+							+ "field4, field5, field6, field7, field8, field1) values (?, ?,"
+							+ "?, ?, ?, ?, ?, ?)");
+
+			pStmt.setString(1, "0");
+			pStmt.setString(2, "0");
+			pStmt.setTimestamp(3, new java.sql.Timestamp(System
+					.currentTimeMillis()));
+			pStmt.setString(4, "ABC");
+			pStmt.setString(5, "DEF");
+			pStmt.setString(6, "AA");
+			pStmt.setTimestamp(7, new java.sql.Timestamp(System
+					.currentTimeMillis()));
+			pStmt.setString(8, "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+			pStmt.executeUpdate();
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4119");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#4311 - Error in JDBC retrieval of mediumint column when
+	 * using prepared statements and binary result sets.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug4311() throws Exception {
+		try {
+			int lowValue = -8388608;
+			int highValue = 8388607;
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4311");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug4311 (low MEDIUMINT, high MEDIUMINT)");
+			this.stmt.executeUpdate("INSERT INTO testBug4311 VALUES ("
+					+ lowValue + ", " + highValue + ")");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("SELECT low, high FROM testBug4311");
+			this.rs = pStmt.executeQuery();
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getInt(1) == lowValue);
+			assertTrue(this.rs.getInt(2) == highValue);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4311");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#4510 -- Statement.getGeneratedKeys() fails when key >
+	 * 32767
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug4510() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4510");
+			this.stmt.executeUpdate("CREATE TABLE testBug4510 ("
+					+ "field1 INT NOT NULL PRIMARY KEY AUTO_INCREMENT,"
+					+ "field2 VARCHAR(100))");
+			this.stmt
+					.executeUpdate("INSERT INTO testBug4510 (field1, field2) VALUES (32767, 'bar')");
+
+			PreparedStatement p = this.conn.prepareStatement(
+					"insert into testBug4510 (field2) values (?)",
+					Statement.RETURN_GENERATED_KEYS);
+
+			p.setString(1, "blah");
+
+			p.executeUpdate();
+
+			ResultSet rs = p.getGeneratedKeys();
+			rs.next();
+			System.out.println("Id: " + rs.getInt(1));
+			rs.close();
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4510");
+		}
+	}
+
+	/**
+	 * Server doesn't accept everything as a server-side prepared statement, so
+	 * by default we scan for stuff it can't handle.
+	 * 
+	 * @throws SQLException
+	 */
+	public void testBug4718() throws SQLException {
+		if (versionMeetsMinimum(4, 1, 0)
+				&& ((com.mysql.jdbc.Connection) this.conn)
+						.getUseServerPreparedStmts()) {
+			this.pstmt = this.conn.prepareStatement("SELECT 1 LIMIT ?");
+			assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement);
+
+			this.pstmt = this.conn.prepareStatement("SELECT 1 LIMIT 1");
+			assertTrue(this.pstmt instanceof com.mysql.jdbc.ServerPreparedStatement);
+
+			this.pstmt = this.conn.prepareStatement("SELECT 1 LIMIT 1, ?");
+			assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement);
+
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4718");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBug4718 (field1 char(32))");
+
+				this.pstmt = this.conn
+						.prepareStatement("ALTER TABLE testBug4718 ADD INDEX (field1)");
+				assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement);
+
+				this.pstmt = this.conn.prepareStatement("SELECT 1");
+				assertTrue(this.pstmt instanceof ServerPreparedStatement);
+
+				this.pstmt = this.conn
+						.prepareStatement("UPDATE testBug4718 SET field1=1");
+				assertTrue(this.pstmt instanceof ServerPreparedStatement);
+
+				this.pstmt = this.conn
+						.prepareStatement("UPDATE testBug4718 SET field1=1 LIMIT 1");
+				assertTrue(this.pstmt instanceof ServerPreparedStatement);
+
+				this.pstmt = this.conn
+						.prepareStatement("UPDATE testBug4718 SET field1=1 LIMIT ?");
+				assertTrue(this.pstmt instanceof com.mysql.jdbc.PreparedStatement);
+
+				this.pstmt = this.conn
+						.prepareStatement("UPDATE testBug4718 SET field1='Will we ignore LIMIT ?,?'");
+				assertTrue(this.pstmt instanceof ServerPreparedStatement);
+
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug4718");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#5012 -- ServerPreparedStatements dealing with return of
+	 * DECIMAL type don't work.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug5012() throws Exception {
+		PreparedStatement pStmt = null;
+		String valueAsString = "12345.12";
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5012");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug5012(field1 DECIMAL(10,2))");
+			this.stmt.executeUpdate("INSERT INTO testBug5012 VALUES ("
+					+ valueAsString + ")");
+
+			pStmt = this.conn
+					.prepareStatement("SELECT field1 FROM testBug5012");
+			this.rs = pStmt.executeQuery();
+			assertTrue(this.rs.next());
+			assertEquals(new BigDecimal(valueAsString), this.rs
+					.getBigDecimal(1));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5012");
+
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#5133 -- PreparedStatement.toString() doesn't return
+	 * correct value if no parameters are present in statement.
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug5133() throws Exception {
+		String query = "SELECT 1";
+		String output = this.conn.prepareStatement(query).toString();
+		System.out.println(output);
+
+		assertTrue(output.indexOf(query) != -1);
+	}
+
+	/**
+	 * Tests for BUG#5191 -- PreparedStatement.executeQuery() gives
+	 * OutOfMemoryError
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug5191() throws Exception {
+		PreparedStatement pStmt = null;
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5191Q");
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5191C");
+
+			this.stmt.executeUpdate("CREATE TABLE testBug5191Q"
+					+ "(QuestionId int NOT NULL AUTO_INCREMENT, "
+					+ "Text VARCHAR(200), " + "PRIMARY KEY(QuestionId))");
+
+			this.stmt.executeUpdate("CREATE TABLE testBug5191C"
+					+ "(CategoryId int, " + "QuestionId int)");
+
+			String[] questions = new String[] { "What is your name?",
+					"What is your quest?",
+					"What is the airspeed velocity of an unladen swollow?",
+					"How many roads must a man walk?", "Where's the tea?", };
+
+			for (int i = 0; i < questions.length; i++) {
+				this.stmt.executeUpdate("INSERT INTO testBug5191Q(Text)"
+						+ " VALUES (\"" + questions[i] + "\")");
+				int catagory = (i < 3) ? 0 : i;
+
+				this.stmt.executeUpdate("INSERT INTO testBug5191C"
+						+ "(CategoryId, QuestionId) VALUES (" + catagory + ", "
+						+ i + ")");
+				/*
+				 * this.stmt.executeUpdate("INSERT INTO testBug5191C" +
+				 * "(CategoryId, QuestionId) VALUES (" + catagory + ", (SELECT
+				 * testBug5191Q.QuestionId" + " FROM testBug5191Q " + "WHERE
+				 * testBug5191Q.Text LIKE '" + questions[i] + "'))");
+				 */
+			}
+
+			pStmt = this.conn.prepareStatement("SELECT qc.QuestionId, q.Text "
+					+ "FROM testBug5191Q q, testBug5191C qc "
+					+ "WHERE qc.CategoryId = ? "
+					+ " AND q.QuestionId = qc.QuestionId");
+
+			int catId = 0;
+			for (int i = 0; i < 100; i++) {
+				execQueryBug5191(pStmt, catId);
+			}
+
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5191Q");
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5191C");
+
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	public void testBug5235() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("zeroDateTimeBehavior", "convertToNull");
+
+		Connection convertToNullConn = getConnectionWithProps(props);
+		Statement convertToNullStmt = convertToNullConn.createStatement();
+		try {
+			convertToNullStmt.executeUpdate("DROP TABLE IF EXISTS testBug5235");
+			convertToNullStmt
+					.executeUpdate("CREATE TABLE testBug5235(field1 DATE)");
+			convertToNullStmt
+					.executeUpdate("INSERT INTO testBug5235 (field1) VALUES ('0000-00-00')");
+
+			PreparedStatement ps = convertToNullConn
+					.prepareStatement("SELECT field1 FROM testBug5235");
+			this.rs = ps.executeQuery();
+
+			if (this.rs.next()) {
+				Date d = (Date) this.rs.getObject("field1");
+				System.out.println("date: " + d);
+			}
+		} finally {
+			convertToNullStmt.executeUpdate("DROP TABLE IF EXISTS testBug5235");
+		}
+	}
+
+	public void testBug5450() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			String table = "testBug5450";
+			String column = "policyname";
+
+			try {
+				Properties props = new Properties();
+				props.setProperty("characterEncoding", "utf8");
+
+				Connection utf8Conn = getConnectionWithProps(props);
+				Statement utfStmt = utf8Conn.createStatement();
+
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS " + table);
+
+				this.stmt.executeUpdate("CREATE TABLE " + table
+						+ "(policyid int NOT NULL AUTO_INCREMENT, " + column
+						+ " VARCHAR(200), "
+						+ "PRIMARY KEY(policyid)) DEFAULT CHARACTER SET utf8");
+
+				String pname0 = "inserted \uac00 - foo - \u4e00";
+
+				utfStmt.executeUpdate("INSERT INTO " + table + "(" + column
+						+ ")" + " VALUES (\"" + pname0 + "\")");
+
+				this.rs = utfStmt.executeQuery("SELECT " + column + " FROM "
+						+ table);
+
+				this.rs.first();
+				String pname1 = this.rs.getString(column);
+
+				assertEquals(pname0, pname1);
+				byte[] bytes = this.rs.getBytes(column);
+
+				String pname2 = new String(bytes, "utf-8");
+				assertEquals(pname1, pname2);
+
+				utfStmt.executeUpdate("delete from " + table + " where "
+						+ column + " like 'insert%'");
+
+				PreparedStatement s1 = utf8Conn.prepareStatement("insert into "
+						+ table + "(" + column + ") values (?)");
+
+				s1.setString(1, pname0);
+				s1.executeUpdate();
+
+				String byteesque = "byte " + pname0;
+				byte[] newbytes = byteesque.getBytes("utf-8");
+
+				s1.setBytes(1, newbytes);
+				s1.executeUpdate();
+
+				this.rs = utfStmt.executeQuery("select " + column + " from "
+						+ table + " where " + column + " like 'insert%'");
+				this.rs.first();
+				String pname3 = this.rs.getString(column);
+				assertEquals(pname0, pname3);
+
+				this.rs = utfStmt.executeQuery("select " + column + " from "
+						+ table + " where " + column + " like 'byte insert%'");
+				this.rs.first();
+
+				String pname4 = this.rs.getString(column);
+				assertEquals(byteesque, pname4);
+
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS " + table);
+			}
+		}
+	}
+
+	public void testBug5510() throws Exception {
+		// This is a server bug that should be fixed by 4.1.6
+		if (versionMeetsMinimum(4, 1, 6)) {
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5510");
+
+				this.stmt
+						.executeUpdate("CREATE TABLE `testBug5510` ("
+								+ "`a` bigint(20) NOT NULL auto_increment,"
+								+ "`b` varchar(64) default NULL,"
+								+ "`c` varchar(64) default NULL,"
+								+ "`d` varchar(255) default NULL,"
+								+ "`e` int(11) default NULL,"
+								+ "`f` varchar(32) default NULL,"
+								+ "`g` varchar(32) default NULL,"
+								+ "`h` varchar(80) default NULL,"
+								+ "`i` varchar(255) default NULL,"
+								+ "`j` varchar(255) default NULL,"
+								+ "`k` varchar(255) default NULL,"
+								+ "`l` varchar(32) default NULL,"
+								+ "`m` varchar(32) default NULL,"
+								+ "`n` timestamp NOT NULL default CURRENT_TIMESTAMP on update"
+								+ " CURRENT_TIMESTAMP,"
+								+ "`o` int(11) default NULL,"
+								+ "`p` int(11) default NULL,"
+								+ "PRIMARY KEY  (`a`)"
+								+ ") ENGINE=InnoDB DEFAULT CHARSET=latin1");
+				PreparedStatement pStmt = this.conn
+						.prepareStatement("INSERT INTO testBug5510 (a) VALUES (?)");
+				pStmt.setNull(1, 0);
+				pStmt.executeUpdate();
+
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug5510");
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#5874, timezone correction goes in wrong 'direction'
+	 * when useTimezone=true and server timezone differs from client timezone.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug5874() throws Exception {
+		try {
+			String clientTimezoneName = "America/Los_Angeles";
+			String serverTimezoneName = "America/Chicago";
+
+			TimeZone.setDefault(TimeZone.getTimeZone(clientTimezoneName));
+
+			long epsillon = 3000; // 3 seconds difference
+
+			long clientTimezoneOffsetMillis = TimeZone.getDefault()
+					.getRawOffset();
+			long serverTimezoneOffsetMillis = TimeZone.getTimeZone(
+					serverTimezoneName).getRawOffset();
+
+			long offsetDifference = clientTimezoneOffsetMillis
+					- serverTimezoneOffsetMillis;
+
+			Properties props = new Properties();
+			props.put("useTimezone", "true");
+			props.put("serverTimezone", serverTimezoneName);
+
+			Connection tzConn = getConnectionWithProps(props);
+			Statement tzStmt = tzConn.createStatement();
+			tzStmt.executeUpdate("DROP TABLE IF EXISTS timeTest");
+			tzStmt
+					.executeUpdate("CREATE TABLE timeTest (tstamp DATETIME, t TIME)");
+
+			PreparedStatement pstmt = tzConn
+					.prepareStatement("INSERT INTO timeTest VALUES (?, ?)");
+
+			long now = System.currentTimeMillis(); // Time in milliseconds
+			// since 1/1/1970 GMT
+
+			Timestamp nowTstamp = new Timestamp(now);
+			Time nowTime = new Time(now);
+
+			pstmt.setTimestamp(1, nowTstamp);
+			pstmt.setTime(2, nowTime);
+			pstmt.executeUpdate();
+
+			this.rs = tzStmt.executeQuery("SELECT * from timeTest");
+
+			// Timestamps look like this: 2004-11-29 13:43:21
+			SimpleDateFormat timestampFormat = new SimpleDateFormat(
+					"yyyy-MM-dd HH:mm:ss");
+			SimpleDateFormat timeFormat = new SimpleDateFormat("HH:mm:ss");
+
+			while (this.rs.next()) {
+				// Driver now converts/checks DATE/TIME/TIMESTAMP/DATETIME types
+				// when calling getString()...
+				String retrTimestampString = new String(this.rs.getBytes(1));
+				Timestamp retrTimestamp = this.rs.getTimestamp(1);
+
+				java.util.Date timestampOnServer = timestampFormat
+						.parse(retrTimestampString);
+
+				long retrievedOffsetForTimestamp = retrTimestamp.getTime()
+						- timestampOnServer.getTime();
+
+				assertTrue(
+						"Difference between original timestamp and timestamp retrieved using client timezone is not "
+								+ offsetDifference, (Math
+								.abs(retrievedOffsetForTimestamp
+										- offsetDifference) < epsillon));
+
+				String retrTimeString = new String(this.rs.getBytes(2));
+				Time retrTime = this.rs.getTime(2);
+
+				java.util.Date timeOnServerAsDate = timeFormat
+						.parse(retrTimeString);
+				Time timeOnServer = new Time(timeOnServerAsDate.getTime());
+
+				long retrievedOffsetForTime = retrTime.getTime()
+						- timeOnServer.getTime();
+
+				assertTrue(
+						"Difference between original times and time retrieved using client timezone is not "
+								+ offsetDifference,
+						(Math.abs(retrievedOffsetForTime - offsetDifference) < epsillon));
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS timeTest");
+		}
+	}
+
+	public void testBug6823() throws SQLException {
+		innerBug6823(true);
+		innerBug6823(false);
+	}
+
+	public void testBug7461() throws Exception {
+		String tableName = "testBug7461";
+
+		try {
+			createTable(tableName, "(field1 varchar(4))");
+			File tempFile = File.createTempFile("mysql-test", ".txt");
+			tempFile.deleteOnExit();
+
+			FileOutputStream fOut = new FileOutputStream(tempFile);
+			fOut.write("abcdefghijklmnop".getBytes());
+			fOut.close();
+
+			try {
+				this.stmt.executeQuery("LOAD DATA LOCAL INFILE '"
+						+ tempFile.toString() + "' INTO TABLE " + tableName);
+			} catch (SQLException sqlEx) {
+				this.stmt.getWarnings();
+			}
+
+		} finally {
+			dropTable(tableName);
+		}
+
+	}
+
+	public void testBug8181() throws Exception {
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8181");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBug8181(col1 VARCHAR(20),col2 INT)");
+
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO testBug8181(col1,col2) VALUES(?,?)");
+
+			for (int i = 0; i < 20; i++) {
+				this.pstmt.setString(1, "Test " + i);
+				this.pstmt.setInt(2, i);
+				this.pstmt.addBatch();
+			}
+
+			this.pstmt.executeBatch();
+
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testBug8181");
+
+			if (this.pstmt != null) {
+				this.pstmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#8487 - PreparedStatements not creating streaming result
+	 * sets.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug8487() throws Exception {
+		try {
+			this.pstmt = this.conn.prepareStatement("SELECT 1",
+					ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY);
+
+			this.pstmt.setFetchSize(Integer.MIN_VALUE);
+			this.rs = this.pstmt.executeQuery();
+			try {
+				this.conn.createStatement().executeQuery("SELECT 2");
+				fail("Should have caught a streaming exception here");
+			} catch (SQLException sqlEx) {
+				assertTrue(sqlEx.getMessage() != null
+						&& sqlEx.getMessage().indexOf("Streaming") != -1);
+			}
+
+		} finally {
+			if (this.rs != null) {
+				while (this.rs.next())
+					;
+
+				this.rs.close();
+			}
+
+			if (this.pstmt != null) {
+				this.pstmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests multiple statement support with fix for BUG#9704.
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testBug9704() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			Connection multiStmtConn = null;
+			Statement multiStmt = null;
+
+			try {
+				Properties props = new Properties();
+				props.setProperty("allowMultiQueries", "true");
+
+				multiStmtConn = getConnectionWithProps(props);
+
+				multiStmt = multiStmtConn.createStatement();
+
+				multiStmt
+						.executeUpdate("DROP TABLE IF EXISTS testMultiStatements");
+				multiStmt
+						.executeUpdate("CREATE TABLE testMultiStatements (field1 VARCHAR(255), field2 INT, field3 DOUBLE)");
+				multiStmt
+						.executeUpdate("INSERT INTO testMultiStatements VALUES ('abcd', 1, 2)");
+
+				multiStmt
+						.execute("SELECT field1 FROM testMultiStatements WHERE field1='abcd';"
+								+ "UPDATE testMultiStatements SET field3=3;"
+								+ "SELECT field3 FROM testMultiStatements WHERE field3=3");
+
+				this.rs = multiStmt.getResultSet();
+
+				assertTrue(this.rs.next());
+
+				assertTrue("abcd".equals(this.rs.getString(1)));
+				this.rs.close();
+
+				// Next should be an update count...
+				assertTrue(!multiStmt.getMoreResults());
+
+				assertTrue("Update count was " + multiStmt.getUpdateCount()
+						+ ", expected 1", multiStmt.getUpdateCount() == 1);
+
+				assertTrue(multiStmt.getMoreResults());
+
+				this.rs = multiStmt.getResultSet();
+
+				assertTrue(this.rs.next());
+
+				assertTrue(this.rs.getDouble(1) == 3);
+
+				// End of multi results
+				assertTrue(!multiStmt.getMoreResults());
+				assertTrue(multiStmt.getUpdateCount() == -1);
+			} finally {
+				if (multiStmt != null) {
+					multiStmt
+							.executeUpdate("DROP TABLE IF EXISTS testMultiStatements");
+
+					multiStmt.close();
+				}
+
+				if (multiStmtConn != null) {
+					multiStmtConn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests that you can close a statement twice without an NPE.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testCloseTwice() throws Exception {
+		Statement closeMe = this.conn.createStatement();
+		closeMe.close();
+		closeMe.close();
+	}
+
+	public void testCsc4194() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		Connection sjisConn = null;
+		Connection windows31JConn = null;
+
+		try {
+			String tableNameText = "testCsc4194Text";
+			String tableNameBlob = "testCsc4194Blob";
+
+			createTable(tableNameBlob, "(field1 BLOB)");
+			String charset = "";
+
+			if (versionMeetsMinimum(5, 0, 3) || versionMeetsMinimum(4, 1, 12)) {
+				charset = " CHARACTER SET cp932";
+			} else if (versionMeetsMinimum(4, 1, 0)) {
+				charset = " CHARACTER SET sjis";
+			}
+
+			createTable(tableNameText, "(field1 TEXT)" + charset);
+
+			Properties windows31JProps = new Properties();
+			windows31JProps.setProperty("useUnicode", "true");
+			windows31JProps.setProperty("characterEncoding", "Windows-31J");
+
+			windows31JConn = getConnectionWithProps(windows31JProps);
+			testCsc4194InsertCheckBlob(windows31JConn, tableNameBlob);
+
+			if (versionMeetsMinimum(4, 1, 0)) {
+				testCsc4194InsertCheckText(windows31JConn, tableNameText,
+						"Windows-31J");
+			}
+
+			Properties sjisProps = new Properties();
+			sjisProps.setProperty("useUnicode", "true");
+			sjisProps.setProperty("characterEncoding", "sjis");
+
+			sjisConn = getConnectionWithProps(sjisProps);
+			testCsc4194InsertCheckBlob(sjisConn, tableNameBlob);
+
+			if (versionMeetsMinimum(5, 0, 3)) {
+				testCsc4194InsertCheckText(sjisConn, tableNameText,
+						"Windows-31J");
+			}
+
+		} finally {
+
+			if (windows31JConn != null) {
+				windows31JConn.close();
+			}
+
+			if (sjisConn != null) {
+				sjisConn.close();
+			}
+		}
+	}
+
+	private void testCsc4194InsertCheckBlob(Connection c, String tableName)
+			throws Exception {
+		byte[] bArray = new byte[] { (byte) 0xac, (byte) 0xed, (byte) 0x00,
+				(byte) 0x05 };
+
+		PreparedStatement testStmt = c.prepareStatement("INSERT INTO "
+				+ tableName + " VALUES (?)");
+		testStmt.setBytes(1, bArray);
+		testStmt.executeUpdate();
+
+		this.rs = c.createStatement().executeQuery(
+				"SELECT field1 FROM " + tableName);
+		assertTrue(this.rs.next());
+		assertEquals(getByteArrayString(bArray), getByteArrayString(this.rs
+				.getBytes(1)));
+		this.rs.close();
+	}
+
+	private void testCsc4194InsertCheckText(Connection c, String tableName,
+			String encoding) throws Exception {
+		byte[] kabuInShiftJIS = { (byte) 0x87, // a double-byte
+				// charater("kabu") in Shift JIS
+				(byte) 0x8a, };
+
+		String expected = new String(kabuInShiftJIS, encoding);
+		PreparedStatement testStmt = c.prepareStatement("INSERT INTO "
+				+ tableName + " VALUES (?)");
+		testStmt.setString(1, expected);
+		testStmt.executeUpdate();
+
+		this.rs = c.createStatement().executeQuery(
+				"SELECT field1 FROM " + tableName);
+		assertTrue(this.rs.next());
+		assertEquals(expected, this.rs.getString(1));
+		this.rs.close();
+	}
+
+	/**
+	 * Tests all forms of statements influencing getGeneratedKeys().
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testGetGeneratedKeysAllCases() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		System.out.println("Using Statement.executeUpdate()\n");
+
+		try {
+			createGGKTables();
+
+			// Do the tests
+			for (int i = 0; i < tests.length; i++) {
+				doGGKTestStatement(tests[i], true);
+			}
+		} finally {
+			dropGGKTables();
+		}
+
+		nextID = 1;
+		count = 0;
+
+		System.out.println("Using Statement.execute()\n");
+
+		try {
+			createGGKTables();
+
+			// Do the tests
+			for (int i = 0; i < tests.length; i++) {
+				doGGKTestStatement(tests[i], false);
+			}
+		} finally {
+			dropGGKTables();
+		}
+
+		nextID = 1;
+		count = 0;
+
+		System.out.println("Using PreparedStatement.executeUpdate()\n");
+
+		try {
+			createGGKTables();
+
+			// Do the tests
+			for (int i = 0; i < tests.length; i++) {
+				doGGKTestPreparedStatement(tests[i], true);
+			}
+		} finally {
+			dropGGKTables();
+		}
+
+		nextID = 1;
+		count = 0;
+
+		System.out.println("Using PreparedStatement.execute()\n");
+
+		try {
+			createGGKTables();
+
+			// Do the tests
+			for (int i = 0; i < tests.length; i++) {
+				doGGKTestPreparedStatement(tests[i], false);
+			}
+		} finally {
+			dropGGKTables();
+		}
+	}
+
+	/**
+	 * Tests that max_rows and 'limit' don't cause exceptions to be thrown.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testLimitAndMaxRows() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testMaxRowsAndLimit");
+			this.stmt
+					.executeUpdate("CREATE TABLE testMaxRowsAndLimit(limitField INT)");
+
+			for (int i = 0; i < 500; i++) {
+				this.stmt
+						.executeUpdate("INSERT INTO testMaxRowsAndLimit VALUES ("
+								+ i + ")");
+			}
+
+			this.stmt.setMaxRows(250);
+			this.stmt
+					.executeQuery("SELECT limitField FROM testMaxRowsAndLimit");
+		} finally {
+			this.stmt.setMaxRows(0);
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testMaxRowsAndLimit");
+		}
+	}
+
+	/*
+	 * public void testBug9595() throws Exception { double[] vals = new double[]
+	 * {52.21, 52.22, 52.23, 52.24};
+	 * 
+	 * createTable("testBug9595", "(field1 DECIMAL(10,2), sortField INT)");
+	 * 
+	 * this.pstmt = this.conn.prepareStatement("INSERT INTO testBug9595 VALUES
+	 * (?, ?)"); // Try setting as doubles for (int i = 0; i < vals.length; i++) {
+	 * this.pstmt.setDouble(1, vals[i]); this.pstmt.setInt(2, i);
+	 * this.pstmt.executeUpdate(); }
+	 * 
+	 * this.pstmt = this.conn.prepareStatement("SELECT field1 FROM testBug9595
+	 * ORDER BY sortField"); this.rs = this.pstmt.executeQuery();
+	 * 
+	 * int i = 0;
+	 * 
+	 * while (this.rs.next()) { double valToTest = vals[i++];
+	 * 
+	 * assertEquals(this.rs.getDouble(1), valToTest, 0.001);
+	 * assertEquals(this.rs.getBigDecimal(1).doubleValue(), valToTest, 0.001); }
+	 * 
+	 * this.pstmt = this.conn.prepareStatement("INSERT INTO testBug9595 VALUES
+	 * (?, ?)");
+	 * 
+	 * this.stmt.executeUpdate("TRUNCATE TABLE testBug9595"); // Now, as
+	 * BigDecimals for (i = 0; i < vals.length; i++) { BigDecimal foo = new
+	 * BigDecimal(vals[i]);
+	 * 
+	 * this.pstmt.setObject(1, foo, Types.DECIMAL, 2); this.pstmt.setInt(2, i);
+	 * this.pstmt.executeUpdate(); }
+	 * 
+	 * this.pstmt = this.conn.prepareStatement("SELECT field1 FROM testBug9595
+	 * ORDER BY sortField"); this.rs = this.pstmt.executeQuery();
+	 * 
+	 * i = 0;
+	 * 
+	 * while (this.rs.next()) { double valToTest = vals[i++];
+	 * System.out.println(this.rs.getString(1));
+	 * assertEquals(this.rs.getDouble(1), valToTest, 0.001);
+	 * assertEquals(this.rs.getBigDecimal(1).doubleValue(), valToTest, 0.001); } }
+	 */
+
+	/**
+	 * Tests that 'LOAD DATA LOCAL INFILE' works
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testLoadData() throws Exception {
+		try {
+			int maxAllowedPacket = 1048576;
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS loadDataRegress");
+			this.stmt
+					.executeUpdate("CREATE TABLE loadDataRegress (field1 int, field2 int)");
+
+			File tempFile = File.createTempFile("mysql", ".txt");
+
+			// tempFile.deleteOnExit();
+			System.out.println(tempFile);
+
+			Writer out = new FileWriter(tempFile);
+
+			int localCount = 0;
+			int rowCount = 128; // maxAllowedPacket * 4;
+
+			for (int i = 0; i < rowCount; i++) {
+				out.write((localCount++) + "\t" + (localCount++) + "\n");
+			}
+
+			out.close();
+
+			StringBuffer fileNameBuf = null;
+
+			if (File.separatorChar == '\\') {
+				fileNameBuf = new StringBuffer();
+
+				String fileName = tempFile.getAbsolutePath();
+				int fileNameLength = fileName.length();
+
+				for (int i = 0; i < fileNameLength; i++) {
+					char c = fileName.charAt(i);
+
+					if (c == '\\') {
+						fileNameBuf.append("/");
+					} else {
+						fileNameBuf.append(c);
+					}
+				}
+			} else {
+				fileNameBuf = new StringBuffer(tempFile.getAbsolutePath());
+			}
+
+			int updateCount = this.stmt
+					.executeUpdate("LOAD DATA LOCAL INFILE '"
+							+ fileNameBuf.toString()
+							+ "' INTO TABLE loadDataRegress");
+			assertTrue(updateCount == rowCount);
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS loadDataRegress");
+		}
+	}
+
+	public void testNullClob() throws Exception {
+		createTable("testNullClob", "(field1 TEXT NULL)");
+
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = this.conn
+					.prepareStatement("INSERT INTO testNullClob VALUES (?)");
+			pStmt.setClob(1, null);
+			pStmt.executeUpdate();
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#1658
+	 * 
+	 * @throws Exception
+	 *             if the fix for parameter bounds checking doesn't work.
+	 */
+	public void testParameterBoundsCheck() throws Exception {
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testParameterBoundsCheck");
+			this.stmt
+					.executeUpdate("CREATE TABLE testParameterBoundsCheck(f1 int, f2 int, f3 int, f4 int, f5 int)");
+
+			PreparedStatement pstmt = this.conn
+					.prepareStatement("UPDATE testParameterBoundsCheck SET f1=?, f2=?,f3=?,f4=? WHERE f5=?");
+
+			pstmt.setString(1, "");
+			pstmt.setString(2, "");
+
+			try {
+				pstmt.setString(25, "");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+						.getSQLState()));
+			}
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testParameterBoundsCheck");
+		}
+	}
+
+	public void testPStmtTypesBug() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testPStmtTypesBug");
+			this.stmt
+					.executeUpdate("CREATE TABLE testPStmtTypesBug(field1 INT)");
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO testPStmtTypesBug VALUES (?)");
+			this.pstmt.setObject(1, null, Types.INTEGER);
+			this.pstmt.addBatch();
+			this.pstmt.setInt(1, 1);
+			this.pstmt.addBatch();
+			this.pstmt.executeBatch();
+
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testPStmtTypesBug");
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#1511
+	 * 
+	 * @throws Exception
+	 *             if the quoteid parsing fix in PreparedStatement doesn't work.
+	 */
+	public void testQuotedIdRecognition() throws Exception {
+		if (!this.versionMeetsMinimum(4, 1)) {
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testQuotedId");
+				this.stmt
+						.executeUpdate("CREATE TABLE testQuotedId (col1 VARCHAR(32))");
+
+				PreparedStatement pStmt = this.conn
+						.prepareStatement("SELECT * FROM testQuotedId WHERE col1='ABC`DEF' or col1=?");
+				pStmt.setString(1, "foo");
+				pStmt.execute();
+
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testQuotedId2");
+				this.stmt
+						.executeUpdate("CREATE TABLE testQuotedId2 (`Works?` INT)");
+				pStmt = this.conn
+						.prepareStatement("INSERT INTO testQuotedId2 (`Works?`) VALUES (?)");
+				pStmt.setInt(1, 1);
+				pStmt.executeUpdate();
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testQuotedId");
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testQuotedId2");
+			}
+		}
+	}
+
+	/**
+	 * Tests for BUG#9288, parameter index out of range if LIKE, ESCAPE '\'
+	 * present in query.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	/*
+	 * public void testBug9288() throws Exception { String tableName =
+	 * "testBug9288"; PreparedStatement pStmt = null;
+	 * 
+	 * try { createTable(tableName, "(field1 VARCHAR(32), field2 INT)"); pStmt =
+	 * ((com.mysql.jdbc.Connection)this.conn).clientPrepareStatement( "SELECT
+	 * COUNT(1) FROM " + tableName + " WHERE " + "field1 LIKE '%' ESCAPE '\\'
+	 * AND " + "field2 > ?"); pStmt.setInt(1, 0);
+	 * 
+	 * this.rs = pStmt.executeQuery(); } finally { if (this.rs != null) {
+	 * this.rs.close(); this.rs = null; }
+	 * 
+	 * if (pStmt != null) { pStmt.close(); } } }
+	 */
+
+	/*
+	 * public void testBug10999() throws Exception { if (versionMeetsMinimum(5,
+	 * 0, 5)) {
+	 * 
+	 * String tableName = "testBug10999"; String updateTrigName =
+	 * "testBug10999Update"; String insertTrigName = "testBug10999Insert"; try {
+	 * createTable(tableName, "(pkfield INT PRIMARY KEY NOT NULL AUTO_INCREMENT,
+	 * field1 VARCHAR(32))");
+	 * 
+	 * try { this.stmt.executeUpdate("DROP TRIGGER " + updateTrigName); } catch
+	 * (SQLException sqlEx) { // ignore for now }
+	 * 
+	 * this.stmt.executeUpdate("CREATE TRIGGER " + updateTrigName + " AFTER
+	 * UPDATE ON " + tableName + " FOR EACH ROW " + "BEGIN " + "END");
+	 * 
+	 * try { this.stmt.executeUpdate("DROP TRIGGER " + insertTrigName); } catch
+	 * (SQLException sqlEx) { // ignore }
+	 * 
+	 * this.stmt.executeUpdate("CREATE TRIGGER " + insertTrigName + " AFTER
+	 * INSERT ON " + tableName + " FOR EACH ROW " + " BEGIN " + "END");
+	 * 
+	 * this.conn.setAutoCommit(false);
+	 * 
+	 * String updateSQL = "INSERT INTO " + tableName + " (field1) VALUES
+	 * ('abcdefg')"; int rowCount = this.stmt.executeUpdate(updateSQL,
+	 * Statement.RETURN_GENERATED_KEYS);
+	 * 
+	 * this.rs = stmt.getGeneratedKeys(); if (rs.next()) {
+	 * System.out.println(rs.getInt(1)); int id = rs.getInt(1); //if
+	 * (log.isDebugEnabled()) // log.debug("Retrieved ID = " + id); } //else {
+	 * //log.error("Can't retrieve ID with getGeneratedKeys."); // Retrieve ID
+	 * using a SELECT statement instead. // querySQL = "SELECT id from tab1
+	 * WHERE ...";
+	 * 
+	 * //if (log.isDebugEnabled()) // log.debug(querySQL);
+	 * 
+	 * //rs = stmt.executeQuery(querySQL); this.rs =
+	 * this.stmt.executeQuery("SELECT pkfield FROM " + tableName); } finally {
+	 * this.conn.setAutoCommit(true);
+	 * 
+	 * try { this.stmt.executeUpdate("DROP TRIGGER IF EXISTS " +
+	 * insertTrigName); } catch (SQLException sqlEx) { // ignore }
+	 * 
+	 * try { this.stmt.executeUpdate("DROP TRIGGER IF EXISTS " +
+	 * updateTrigName); } catch (SQLException sqlEx) { // ignore } } } }
+	 */
+
+	/**
+	 * Tests that binary dates/times are encoded/decoded correctly.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 * 
+	 * @deprecated because we need to use this particular constructor for the
+	 *             date class, as Calendar-constructed dates don't pass the
+	 *             .equals() test :(
+	 */
+	public void testServerPrepStmtAndDate() throws Exception {
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testServerPrepStmtAndDate");
+			this.stmt.executeUpdate("CREATE TABLE testServerPrepStmtAndDate("
+					+ "`P_ID` int(10) NOT NULL default '0',"
+					+ "`H_ID` int(10) NOT NULL default '0',"
+					+ "`R_ID` int(10) NOT NULL default '0',"
+					+ "`H_Age` int(10) default NULL,"
+					+ "`R_Date` date NOT NULL default '0000-00-00',"
+					+ "`Comments` varchar(255) default NULL,"
+					+ "`Weight` int(10) default NULL,"
+					+ "`HeadGear` char(1) NOT NULL default '',"
+					+ "`FinPos` int(10) default NULL,"
+					+ "`Jock_ID` int(10) default NULL,"
+					+ "`BtnByPrev` double default NULL,"
+					+ "`BtnByWinner` double default NULL,"
+					+ "`Jock_All` int(10) default NULL,"
+					+ "`Draw` int(10) default NULL,"
+					+ "`SF` int(10) default NULL,"
+					+ "`RHR` int(10) default NULL,"
+					+ "`ORating` int(10) default NULL,"
+					+ "`Odds` double default NULL,"
+					+ "`RaceFormPlus` int(10) default NULL,"
+					+ "`PrevPerform` int(10) default NULL,"
+					+ "`TrainerID` int(10) NOT NULL default '0',"
+					+ "`DaysSinceRun` int(10) default NULL,"
+					+ "UNIQUE KEY `P_ID` (`P_ID`),"
+					+ "UNIQUE KEY `R_H_ID` (`R_ID`,`H_ID`),"
+					+ "KEY `R_Date` (`R_Date`)," + "KEY `H_Age` (`H_Age`),"
+					+ "KEY `TrainerID` (`TrainerID`)," + "KEY `H_ID` (`H_ID`)"
+					+ ")");
+
+			Date dt = new java.sql.Date(102, 1, 2); // Note, this represents the
+			// date 2002-02-02
+
+			PreparedStatement pStmt2 = this.conn
+					.prepareStatement("INSERT INTO testServerPrepStmtAndDate (P_ID, R_Date) VALUES (171576, ?)");
+			pStmt2.setDate(1, dt);
+			pStmt2.executeUpdate();
+			pStmt2.close();
+
+			this.rs = this.stmt
+					.executeQuery("SELECT R_Date FROM testServerPrepStmtAndDate");
+			this.rs.next();
+
+			System.out.println("Date that was stored (as String) "
+					+ this.rs.getString(1)); // comes back as 2002-02-02
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("Select P_ID,R_Date from testServerPrepStmtAndDate Where R_Date = ?   and P_ID = 171576");
+			pStmt.setDate(1, dt);
+
+			this.rs = pStmt.executeQuery();
+
+			assertTrue(this.rs.next());
+
+			assertTrue("171576".equals(this.rs.getString(1)));
+
+			Date retDt = this.rs.getDate(2);
+
+			assertTrue(dt.equals(this.rs.getDate(2)));
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testServerPrepStmtAndDate");
+		}
+	}
+
+	public void testServerPrepStmtDeadlock() throws Exception {
+
+		Connection c = getConnectionWithProps(null);
+
+		Thread testThread1 = new PrepareThread(c);
+		Thread testThread2 = new PrepareThread(c);
+		testThread1.start();
+		testThread2.start();
+		Thread.sleep(30000);
+		assertTrue(this.testServerPrepStmtDeadlockCounter >= 10);
+	}
+
+	/**
+	 * Tests PreparedStatement.setCharacterStream() to ensure it accepts > 4K
+	 * streams
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testSetCharacterStream() throws Exception {
+		try {
+			((com.mysql.jdbc.Connection) this.conn).setTraceProtocol(true);
+
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS charStreamRegressTest");
+			this.stmt
+					.executeUpdate("CREATE TABLE charStreamRegressTest(field1 text)");
+
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO charStreamRegressTest VALUES (?)");
+
+			// char[] charBuf = new char[16384];
+			char[] charBuf = new char[32];
+
+			for (int i = 0; i < charBuf.length; i++) {
+				charBuf[i] = 'A';
+			}
+
+			CharArrayReader reader = new CharArrayReader(charBuf);
+
+			this.pstmt.setCharacterStream(1, reader, charBuf.length);
+			this.pstmt.executeUpdate();
+
+			this.rs = this.stmt
+					.executeQuery("SELECT LENGTH(field1) FROM charStreamRegressTest");
+
+			this.rs.next();
+
+			System.out.println("Character stream length: "
+					+ this.rs.getString(1));
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field1 FROM charStreamRegressTest");
+
+			this.rs.next();
+
+			String result = this.rs.getString(1);
+
+			assertTrue(result.length() == charBuf.length);
+
+			this.stmt.execute("TRUNCATE TABLE charStreamRegressTest");
+
+			// Test that EOF is not thrown
+			reader = new CharArrayReader(charBuf);
+			this.pstmt.clearParameters();
+			this.pstmt.setCharacterStream(1, reader, charBuf.length);
+			this.pstmt.executeUpdate();
+
+			this.rs = this.stmt
+					.executeQuery("SELECT LENGTH(field1) FROM charStreamRegressTest");
+
+			this.rs.next();
+
+			System.out.println("Character stream length: "
+					+ this.rs.getString(1));
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field1 FROM charStreamRegressTest");
+
+			this.rs.next();
+
+			result = this.rs.getString(1);
+
+			assertTrue("Retrieved value of length " + result.length()
+					+ " != length of inserted value " + charBuf.length, result
+					.length() == charBuf.length);
+
+			// Test single quotes inside identifers
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS `charStream'RegressTest`");
+			this.stmt
+					.executeUpdate("CREATE TABLE `charStream'RegressTest`(field1 text)");
+
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO `charStream'RegressTest` VALUES (?)");
+
+			reader = new CharArrayReader(charBuf);
+			this.pstmt.setCharacterStream(1, reader, (charBuf.length * 2));
+			this.pstmt.executeUpdate();
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field1 FROM `charStream'RegressTest`");
+
+			this.rs.next();
+
+			result = this.rs.getString(1);
+
+			assertTrue("Retrieved value of length " + result.length()
+					+ " != length of inserted value " + charBuf.length, result
+					.length() == charBuf.length);
+		} finally {
+			((com.mysql.jdbc.Connection) this.conn).setTraceProtocol(false);
+
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (Exception ex) {
+					// ignore
+				}
+
+				this.rs = null;
+			}
+
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS `charStream'RegressTest`");
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS charStreamRegressTest");
+		}
+	}
+
+	/**
+	 * Tests a bug where Statement.setFetchSize() does not work for values other
+	 * than 0 or Integer.MIN_VALUE
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testSetFetchSize() throws Exception {
+		int oldFetchSize = this.stmt.getFetchSize();
+
+		try {
+			this.stmt.setFetchSize(10);
+		} finally {
+			this.stmt.setFetchSize(oldFetchSize);
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#907
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testSetMaxRows() throws Exception {
+		Statement maxRowsStmt = null;
+
+		try {
+			maxRowsStmt = this.conn.createStatement();
+			maxRowsStmt.setMaxRows(1);
+			maxRowsStmt.executeQuery("SELECT 1");
+		} finally {
+			if (maxRowsStmt != null) {
+				maxRowsStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests for timestamp NPEs occuring in binary-format timestamps.
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 * 
+	 * @deprecated yes, we know we are using deprecated methods here :)
+	 */
+	public void testTimestampNPE() throws Exception {
+		try {
+			Timestamp ts = new Timestamp(System.currentTimeMillis());
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testTimestampNPE");
+			this.stmt
+					.executeUpdate("CREATE TABLE testTimestampNPE (field1 TIMESTAMP)");
+
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO testTimestampNPE VALUES (?)");
+			this.pstmt.setTimestamp(1, ts);
+			this.pstmt.executeUpdate();
+
+			this.pstmt = this.conn
+					.prepareStatement("SELECT field1 FROM testTimestampNPE");
+
+			this.rs = this.pstmt.executeQuery();
+
+			this.rs.next();
+
+			System.out.println(this.rs.getString(1));
+
+			this.rs.getDate(1);
+
+			Timestamp rTs = this.rs.getTimestamp(1);
+			assertTrue("Retrieved year of " + rTs.getYear()
+					+ " does not match " + ts.getYear(), rTs.getYear() == ts
+					.getYear());
+			assertTrue("Retrieved month of " + rTs.getMonth()
+					+ " does not match " + ts.getMonth(), rTs.getMonth() == ts
+					.getMonth());
+			assertTrue("Retrieved date of " + rTs.getDate()
+					+ " does not match " + ts.getDate(), rTs.getDate() == ts
+					.getDate());
+		} finally {
+		}
+	}
+
+	public void testTruncationWithChar() throws Exception {
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testTruncationWithChar");
+			this.stmt
+					.executeUpdate("CREATE TABLE testTruncationWithChar (field1 char(2))");
+
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO testTruncationWithChar VALUES (?)");
+			this.pstmt.setString(1, "00");
+			this.pstmt.executeUpdate();
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testTruncationWithChar");
+		}
+	}
+
+	/**
+	 * Tests fix for updatable streams being supported in updatable result sets.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testUpdatableStream() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS updateStreamTest");
+			this.stmt
+					.executeUpdate("CREATE TABLE updateStreamTest (keyField INT NOT NULL AUTO_INCREMENT PRIMARY KEY, field1 BLOB)");
+
+			int streamLength = 16385;
+			byte[] streamData = new byte[streamLength];
+
+			/* create an updatable statement */
+			Statement updStmt = this.conn.createStatement(
+					ResultSet.TYPE_SCROLL_INSENSITIVE,
+					ResultSet.CONCUR_UPDATABLE);
+
+			/* fill the resultset with some values */
+			ResultSet updRs = updStmt
+					.executeQuery("SELECT * FROM updateStreamTest");
+
+			/* move to insertRow */
+			updRs.moveToInsertRow();
+
+			/* update the table */
+			updRs.updateBinaryStream("field1", new ByteArrayInputStream(
+					streamData), streamLength);
+
+			updRs.insertRow();
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS updateStreamTest");
+		}
+	}
+
+	/**
+	 * Tests fix for 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).
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug15383() throws Exception {
+		createTable(
+				"testBug15383",
+				"(id INTEGER UNSIGNED NOT NULL "
+						+ "AUTO_INCREMENT,value BIGINT UNSIGNED NULL DEFAULT 0,PRIMARY "
+						+ "KEY(id))ENGINE=InnoDB;");
+
+		this.stmt.executeUpdate("INSERT INTO testBug15383(value) VALUES(1)");
+
+		Statement updatableStmt = this.conn.createStatement(
+				ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE);
+
+		try {
+			this.rs = updatableStmt.executeQuery("SELECT * from testBug15383");
+
+			assertTrue(this.rs.next());
+
+			Object bigIntObj = this.rs.getObject("value");
+			assertEquals("java.math.BigInteger", bigIntObj.getClass().getName());
+
+			this.rs.updateObject("value", new BigInteger("3"));
+			this.rs.updateRow();
+
+			assertEquals("3", this.rs.getString("value"));
+		} finally {
+			if (this.rs != null) {
+				ResultSet toClose = this.rs;
+				this.rs = null;
+				toClose.close();
+			}
+
+			if (updatableStmt != null) {
+				updatableStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#17099 - Statement.getGeneratedKeys() throws NPE when no
+	 * query has been processed.
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug17099() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid
+		}
+
+		Statement newStmt = this.conn.createStatement();
+		assertNotNull(newStmt.getGeneratedKeys());
+
+		PreparedStatement pStmt = this.conn.prepareStatement("SELECT 1");
+		assertNotNull(pStmt.getGeneratedKeys());
+
+		if (versionMeetsMinimum(4, 1)) {
+			pStmt = ((com.mysql.jdbc.Connection) this.conn)
+					.clientPrepareStatement("SELECT 1");
+			assertNotNull(pStmt.getGeneratedKeys());
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#17587 - clearParameters() on a closed prepared
+	 * statement causes NPE.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug17587() throws Exception {
+		createTable("testBug17857", "(field1 int)");
+		PreparedStatement pStmt = null;
+
+		try {
+			pStmt = this.conn
+					.prepareStatement("INSERT INTO testBug17857 VALUES (?)");
+			pStmt.close();
+			try {
+				pStmt.clearParameters();
+			} catch (SQLException sqlEx) {
+				assertEquals("08003", sqlEx.getSQLState());
+			}
+
+			pStmt = ((com.mysql.jdbc.Connection) this.conn)
+					.clientPrepareStatement("INSERT INTO testBug17857 VALUES (?)");
+			pStmt.close();
+			try {
+				pStmt.clearParameters();
+			} catch (SQLException sqlEx) {
+				assertEquals("08003", sqlEx.getSQLState());
+			}
+
+		} finally {
+			if (pStmt != null) {
+				pStmt.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#18740 - Data truncation and getWarnings() only returns
+	 * last warning in set.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug18740() throws Exception {
+		if (!versionMeetsMinimum(5, 0, 2)) {
+			createTable("testWarnings", "(field1 smallint(6),"
+					+ "field2 varchar(6)," + "UNIQUE KEY field1(field1))");
+
+			try {
+				this.stmt.executeUpdate("INSERT INTO testWarnings VALUES "
+						+ "(10001, 'data1')," + "(10002, 'data2 foo'),"
+						+ "(10003, 'data3')," + "(10004999, 'data4'),"
+						+ "(10005, 'data5')");
+			} catch (SQLException sqlEx) {
+				assertEquals("01004", sqlEx.getSQLState());
+				assertEquals("01004", sqlEx.getNextException().getSQLState());
+
+				SQLWarning sqlWarn = this.stmt.getWarnings();
+				assertEquals("01000", sqlWarn.getSQLState());
+				assertEquals("01000", sqlWarn.getNextWarning().getSQLState());
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#19615, PreparedStatement.setObject(int, Object, int)
+	 * doesn't respect scale of BigDecimals.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug19615() throws Exception {
+		createTable("testBug19615", "(field1 DECIMAL(19, 12))");
+
+		try {
+			BigDecimal dec = new BigDecimal("1.234567");
+
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO testBug19615 VALUES (?)");
+			this.pstmt.setObject(1, dec, Types.DECIMAL);
+			this.pstmt.executeUpdate();
+			this.pstmt.close();
+
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug19615");
+			this.rs.next();
+			assertEquals(dec, this.rs.getBigDecimal(1).setScale(6));
+			this.rs.close();
+			this.stmt.executeUpdate("TRUNCATE TABLE testBug19615");
+
+			this.pstmt = ((com.mysql.jdbc.Connection) this.conn)
+					.clientPrepareStatement("INSERT INTO testBug19615 VALUES (?)");
+			this.pstmt.setObject(1, dec, Types.DECIMAL);
+			this.pstmt.executeUpdate();
+			this.pstmt.close();
+
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testBug19615");
+			this.rs.next();
+			assertEquals(dec, this.rs.getBigDecimal(1).setScale(6));
+			this.rs.close();
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#20029 - NPE thrown from executeBatch().
+	 * 
+	 * @throws Exception
+	 */
+	public void testBug20029() throws Exception {
+		createTable("testBug20029", ("(field1 int)"));
+		
+		long initialTimeout = 20; // may need to raise this depending on environment
+		                          // we try and do this automatically in this testcase
+		
+		for (int i = 0; i < 10; i++) {
+			final Connection toBeKilledConn = getConnectionWithProps(new Properties());
+			final long timeout = initialTimeout;
+			PreparedStatement toBeKilledPstmt = null;
+			
+			try {
+				toBeKilledPstmt = ((com.mysql.jdbc.Connection)toBeKilledConn).clientPrepareStatement("INSERT INTO testBug20029 VALUES (?)");
+				
+				for (int j = 0; j < 1000; j++) {
+					toBeKilledPstmt.setInt(1, j);
+					toBeKilledPstmt.addBatch();
+				}
+				
+				Thread t = new Thread() {
+					public void run() {
+						try {
+							sleep(timeout);
+							toBeKilledConn.close();
+						} catch (Throwable t) {
+							
+						}
+					}
+				};
+				
+				t.start();
+				
+				try {
+					if (toBeKilledConn.isClosed()) {
+						initialTimeout *= 2;
+						continue;
+					}
+					
+					toBeKilledPstmt.executeBatch();
+					fail("Should've caught a SQLException for the statement being closed here");
+				} catch (BatchUpdateException batchEx) {
+					assertEquals("08003", batchEx.getSQLState());
+					break;
+				}
+				
+				fail("Connection didn't close while in the middle of PreparedStatement.executeBatch()");
+			} finally {
+				if (toBeKilledPstmt != null) {
+					toBeKilledPstmt.close();
+				}
+				
+				if (toBeKilledConn != null) {
+					toBeKilledConn.close();
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Fixes BUG#20687 - Can't pool server-side prepared statements, exception
+	 * raised when re-using them.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug20687() throws Exception {
+		if (!isRunningOnJdk131() && versionMeetsMinimum(5, 0)) {
+			createTable("testBug20687", "(field1 int)");
+			Connection poolingConn = null;
+			
+			Properties props = new Properties();
+			props.setProperty("cachePrepStmts", "true");
+			
+			PreparedStatement pstmt1 = null;
+			PreparedStatement pstmt2  = null;
+			
+			try {
+				poolingConn = getConnectionWithProps(props);
+				pstmt1 = poolingConn.prepareStatement("SELECT field1 FROM testBug20687");
+				pstmt1.executeQuery();
+				pstmt1.close();
+				
+				pstmt2 = poolingConn.prepareStatement("SELECT field1 FROM testBug20687");
+				pstmt2.executeQuery();
+				assertTrue(pstmt1 == pstmt2);
+				pstmt2.close();
+			} finally {
+				if (pstmt1 != null) {
+					pstmt1.close();
+				}
+				
+				if (pstmt2 != null) {
+					pstmt2.close();
+				}
+				
+				if (poolingConn != null) {
+					poolingConn.close();
+				}
+			}
+		}
+	}
+	
+	public void testLikeWithBackslashes() throws Exception {
+		if (!versionMeetsMinimum(5, 0, 0)) {
+			return;
+		}
+
+		Connection noBackslashEscapesConn = null;
+
+		try {
+			Properties props = new Properties();
+			props.setProperty("sessionVariables",
+					"sql_mode=NO_BACKSLASH_ESCAPES");
+
+			noBackslashEscapesConn = getConnectionWithProps(props);
+
+			createTable(
+					"X_TEST",
+					"(userName varchar(32) not null, ivalue integer, CNAME varchar(255), bvalue CHAR(1), svalue varchar(255), ACTIVE CHAR(1), primary key (userName))");
+
+			String insert_sql = "insert into X_TEST (ivalue, CNAME, bvalue, svalue, ACTIVE, userName) values (?, ?, ?, ?, ?, ?)";
+
+			this.pstmt = noBackslashEscapesConn.prepareStatement(insert_sql);
+			this.pstmt.setInt(1, 0);
+			this.pstmt.setString(2, "c:\\jetson");
+			this.pstmt.setInt(3, 1);
+			this.pstmt.setString(4, "c:\\jetson");
+			this.pstmt.setInt(5, 1);
+			this.pstmt.setString(6, "c:\\jetson");
+			this.pstmt.execute();
+
+			String select_sql = "select user0_.userName as userName0_0_, user0_.ivalue as ivalue0_0_, user0_.CNAME as CNAME0_0_, user0_.bvalue as bvalue0_0_, user0_.svalue as svalue0_0_, user0_.ACTIVE as ACTIVE0_0_ from X_TEST user0_ where user0_.userName like ?";
+			this.pstmt = noBackslashEscapesConn.prepareStatement(select_sql);
+			this.pstmt.setString(1, "c:\\j%");
+			// if we comment out the previous line and uncomment the following, the like clause matches
+			// stmt.setString(1,"c:\\\\j%");
+			System.out.println("about to execute query " + select_sql);
+			this.rs = this.pstmt.executeQuery();
+			assertTrue(this.rs.next());
+		} finally {
+			closeMemberJDBCResources();
+
+			if (noBackslashEscapesConn != null) {
+				noBackslashEscapesConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#20650 - Statement.cancel() causes NullPointerException
+     * if underlying connection has been closed due to server failure.
+     * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug20650() throws Exception {
+		Connection closedConn = null;
+		Statement cancelStmt = null;
+		
+		try {
+			closedConn = getConnectionWithProps(null);
+			cancelStmt = closedConn.createStatement();
+		
+			closedConn.close();
+
+			cancelStmt.cancel();
+		} finally {
+			if (cancelStmt != null) {
+				cancelStmt.close();
+			}
+			
+			if (closedConn != null && !closedConn.isClosed()) {
+				closedConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#20888 - escape of quotes in client-side prepared 
+	 * statements parsing not respected.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug20888() throws Exception {
+		
+		try {
+			String s = "SELECT 'What do you think about D\\'Artanian''?', \"What do you think about D\\\"Artanian\"\"?\"";
+			this.pstmt = ((com.mysql.jdbc.Connection)this.conn).clientPrepareStatement(s);
+			
+			this.rs = this.pstmt.executeQuery();
+			this.rs.next();
+			assertEquals(this.rs.getString(1), "What do you think about D'Artanian'?");
+			assertEquals(this.rs.getString(2), "What do you think about D\"Artanian\"?");
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+	/**
+	 * Tests Bug#21207 - Driver throws NPE when tracing prepared statements that
+	 * have been closed (in asSQL()).
+	 * 
+	 * @throws Exception if the test fails
+	 */
+	public void testBug21207() throws Exception {
+		try {
+			this.pstmt = this.conn.prepareStatement("SELECT 1");
+			this.pstmt.close();
+			this.pstmt.toString(); // this used to cause an NPE
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+	
+	/**
+	 * Tests fix for BUG#22359 - Driver was using millis for
+	 * Statement.setQueryTimeout() when spec says argument is
+	 * seconds.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug22359() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			Statement timeoutStmt = null;
+			
+			try {
+				timeoutStmt = this.conn.createStatement();
+				timeoutStmt.setQueryTimeout(2);
+				
+				long begin = System.currentTimeMillis();
+				
+				try {
+					timeoutStmt.execute("SELECT SLEEP(30)");
+				} catch (MySQLTimeoutException timeoutEx) {
+					long end = System.currentTimeMillis();
+					
+					assertTrue((end - begin) > 1000);
+				}
+			} finally {
+				if (timeoutStmt != null) {
+					timeoutStmt.close();
+				}
+			}
+		}
+	}
+	
+	/**
+	 * Tests fix for 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).
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testBug22290() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+		
+		createTable(
+				"testbug22290",
+				"(`id` int(11) NOT NULL default '1',`cost` decimal(10,2) NOT NULL,PRIMARY KEY  (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8");
+		assertEquals(
+				this.stmt
+						.executeUpdate("INSERT INTO testbug22290 (`id`,`cost`) VALUES (1,'1.00')"),
+				1);
+
+		Connection configuredConn = null;
+		
+		try {
+			Properties props = new Properties();
+			props.setProperty("sessionVariables", "sql_mode='STRICT_TRANS_TABLES'");
+			
+			
+			configuredConn = getConnectionWithProps(props);
+			
+			this.pstmt = configuredConn
+					.prepareStatement("update testbug22290 set cost = cost + ? where id = 1");
+			this.pstmt.setBigDecimal(1, new BigDecimal("1.11"));
+			assertEquals(this.pstmt.executeUpdate(), 1);
+			
+			assertEquals(this.stmt
+					.executeUpdate("UPDATE testbug22290 SET cost='1.00'"), 1);
+			this.pstmt = ((com.mysql.jdbc.Connection)configuredConn)
+				.clientPrepareStatement("update testbug22290 set cost = cost + ? where id = 1");
+			this.pstmt.setBigDecimal(1, new BigDecimal("1.11"));
+			assertEquals(this.pstmt.executeUpdate(), 1);
+		} finally {
+			closeMemberJDBCResources();
+			
+			if (configuredConn != null) {
+				configuredConn.close();
+			}
+		}
+	}
+
+	public void testClientPreparedSetBoolean() throws Exception {
+		try {
+			this.pstmt = ((com.mysql.jdbc.Connection)this.conn).clientPrepareStatement("SELECT ?");
+			this.pstmt.setBoolean(1, false);
+			assertEquals("SELECT 0", 
+					this.pstmt.toString().substring(this.pstmt.toString().indexOf("SELECT")));
+			this.pstmt.setBoolean(1, true);
+			assertEquals("SELECT 1", 
+					this.pstmt.toString().substring(this.pstmt.toString().indexOf("SELECT")));
+		} finally {
+			closeMemberJDBCResources();
+		}
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StressRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StressRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StressRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,313 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Properties;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests for multi-thread stress regressions.
+ * 
+ * @author Mark Matthews
+ * @version $Id: StressRegressionTest.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class StressRegressionTest extends BaseTestCase {
+	private int numThreadsStarted;
+
+	/**
+	 * Creates a new StressRegressionTest
+	 * 
+	 * @param name
+	 *            the name of the test.
+	 */
+	public StressRegressionTest(String name) {
+		super(name);
+
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(StressRegressionTest.class);
+	}
+
+	/**
+	 * 
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public synchronized void testContention() throws Exception {
+		if (false) {
+			System.out.println("Calculating baseline elapsed time...");
+
+			long start = System.currentTimeMillis();
+
+			contentiousWork(this.conn, this.stmt, 0);
+
+			long singleThreadElapsedTimeMillis = System.currentTimeMillis()
+					- start;
+
+			System.out.println("Single threaded execution took "
+					+ singleThreadElapsedTimeMillis + " ms.");
+
+			int numThreadsToStart = 95;
+
+			System.out.println("\nStarting " + numThreadsToStart + " threads.");
+
+			this.numThreadsStarted = numThreadsToStart;
+
+			ContentionThread[] threads = new ContentionThread[this.numThreadsStarted];
+
+			for (int i = 0; i < numThreadsToStart; i++) {
+				threads[i] = new ContentionThread(i);
+				threads[i].start();
+			}
+
+			for (;;) {
+				try {
+					wait();
+
+					if (this.numThreadsStarted == 0) {
+						break;
+					}
+				} catch (InterruptedException ie) {
+					// ignore
+				}
+			}
+
+			// Collect statistics...
+			System.out.println("Done!");
+
+			double avgElapsedTimeMillis = 0;
+
+			List elapsedTimes = new ArrayList();
+
+			for (int i = 0; i < numThreadsToStart; i++) {
+				elapsedTimes.add(new Long(threads[i].elapsedTimeMillis));
+
+				avgElapsedTimeMillis += ((double) threads[i].elapsedTimeMillis / numThreadsToStart);
+			}
+
+			Collections.sort(elapsedTimes);
+
+			System.out.println("Average elapsed time per-thread was "
+					+ avgElapsedTimeMillis + " ms.");
+			System.out.println("Median elapsed time per-thread was "
+					+ elapsedTimes.get(elapsedTimes.size() / 2) + " ms.");
+			System.out.println("Minimum elapsed time per-thread was "
+					+ elapsedTimes.get(0) + " ms.");
+			System.out.println("Maximum elapsed time per-thread was "
+					+ elapsedTimes.get(elapsedTimes.size() - 1) + " ms.");
+		}
+	}
+
+	/**
+	 * 
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testCreateConnections() throws Exception {
+		new CreateThread().run();
+	}
+
+	/**
+	 * 
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testCreateConnectionsUnderLoad() throws Exception {
+		new CreateThread(new BusyThread()).run();
+	}
+
+	void contentiousWork(Connection threadConn, Statement threadStmt,
+			int threadNumber) {
+		Date now = new Date();
+
+		try {
+			for (int i = 0; i < 1000; i++) {
+				ResultSet threadRs = threadStmt.executeQuery("SELECT 1, 2");
+
+				while (threadRs.next()) {
+					threadRs.getString(1);
+					threadRs.getString(2);
+				}
+
+				threadRs.close();
+
+				PreparedStatement pStmt = threadConn
+						.prepareStatement("SELECT ?");
+				pStmt.setTimestamp(1, new Timestamp(now.getTime()));
+
+				threadRs = pStmt.executeQuery();
+
+				while (threadRs.next()) {
+					threadRs.getTimestamp(1);
+				}
+
+				threadRs.close();
+				pStmt.close();
+			}
+		} catch (Exception ex) {
+			throw new RuntimeException(ex.toString());
+		}
+	}
+
+	synchronized void reportDone() {
+		this.numThreadsStarted--;
+		notify();
+	}
+
+	public class BusyThread extends Thread {
+		boolean stop = false;
+
+		public void run() {
+			while (!this.stop) {
+			}
+		}
+	}
+
+	class ContentionThread extends Thread {
+		Connection threadConn;
+
+		Statement threadStmt;
+
+		int threadNumber;
+
+		long elapsedTimeMillis;
+
+		public ContentionThread(int num) throws SQLException {
+			this.threadNumber = num;
+			this.threadConn = getConnectionWithProps(new Properties());
+			this.threadStmt = this.threadConn.createStatement();
+
+			System.out.println(this.threadConn);
+		}
+
+		public void run() {
+			long start = System.currentTimeMillis();
+
+			try {
+				contentiousWork(this.threadConn, this.threadStmt,
+						this.threadNumber);
+				this.elapsedTimeMillis = System.currentTimeMillis() - start;
+
+				System.out
+						.println("Thread " + this.threadNumber + " finished.");
+			} finally {
+				if (this.elapsedTimeMillis == 0) {
+					this.elapsedTimeMillis = System.currentTimeMillis() - start;
+				}
+
+				reportDone();
+
+				try {
+					this.threadStmt.close();
+					this.threadConn.close();
+				} catch (SQLException ex) {
+					// ignore
+				}
+			}
+		}
+	}
+
+	class CreateThread extends Thread {
+		BusyThread busyThread;
+
+		int numConnections = 15;
+
+		public CreateThread() {
+		}
+
+		public CreateThread(BusyThread toStop) {
+			this.busyThread = toStop;
+		}
+
+		public CreateThread(int numConns) {
+			this.numConnections = numConns;
+		}
+
+		public void run() {
+			try {
+				Connection[] connList = new Connection[this.numConnections];
+
+				long maxConnTime = Long.MIN_VALUE;
+				long minConnTime = Long.MAX_VALUE;
+				double averageTime = 0;
+
+				Properties nullProps = new Properties();
+
+				for (int i = 0; i < this.numConnections; i++) {
+					long startTime = System.currentTimeMillis();
+					connList[i] = getConnectionWithProps(nullProps);
+
+					long endTime = System.currentTimeMillis();
+					long ellapsedTime = endTime - startTime;
+
+					if (ellapsedTime < minConnTime) {
+						minConnTime = ellapsedTime;
+					}
+
+					if (ellapsedTime > maxConnTime) {
+						maxConnTime = ellapsedTime;
+					}
+
+					averageTime += ((double) ellapsedTime / this.numConnections);
+				}
+
+				if (this.busyThread != null) {
+					this.busyThread.stop = true;
+				}
+
+				for (int i = 0; i < this.numConnections; i++) {
+					connList[i].close();
+				}
+
+				System.out.println(minConnTime + "/" + maxConnTime + "/"
+						+ averageTime);
+			} catch (Exception ex) {
+				throw new RuntimeException(ex);
+			}
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StringRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StringRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/StringRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,838 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.sql.Clob;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.Statement;
+import java.util.Properties;
+
+import testsuite.BaseTestCase;
+
+import com.mysql.jdbc.StringUtils;
+
+/**
+ * Tests for regressions of bugs in String handling in the driver.
+ * 
+ * @author Mark Matthews
+ * @version StringRegressionTest.java,v 1.1 2002/11/04 14:58:25 mark_matthews
+ *          Exp
+ */
+public class StringRegressionTest extends BaseTestCase {
+	/**
+	 * Creates a new StringTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public StringRegressionTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(StringRegressionTest.class);
+	}
+
+	/**
+	 * Tests character conversion bug.
+	 * 
+	 * @throws Exception
+	 *             if there is an internal error (which is a bug).
+	 */
+	public void testAsciiCharConversion() throws Exception {
+		byte[] buf = new byte[10];
+		buf[0] = (byte) '?';
+		buf[1] = (byte) 'S';
+		buf[2] = (byte) 't';
+		buf[3] = (byte) 'a';
+		buf[4] = (byte) 't';
+		buf[5] = (byte) 'e';
+		buf[6] = (byte) '-';
+		buf[7] = (byte) 'b';
+		buf[8] = (byte) 'o';
+		buf[9] = (byte) 't';
+
+		String testString = "?State-bot";
+		String convertedString = StringUtils.toAsciiString(buf);
+
+		for (int i = 0; i < convertedString.length(); i++) {
+			System.out.println((byte) convertedString.charAt(i));
+		}
+
+		assertTrue("Converted string != test string", testString
+				.equals(convertedString));
+	}
+
+	/**
+	 * Tests fix for BUG#4010 -- GBK encoding getting escaped doubly when
+	 * database default character set is GBK. Requires version older than 4.1.0
+	 * and server set to default character set of 'gbk' to run.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug4010() throws Exception {
+		if (!versionMeetsMinimum(4, 1)) {
+			if ("GBK".equalsIgnoreCase(getMysqlVariable("character_set"))) {
+				String origString = "\u603d";
+				Properties props = new Properties();
+				props.put("useUnicode", "true");
+				props.put("characterEncoding", "GBK");
+
+				Connection unicodeConn = getConnectionWithProps(props);
+				Statement unicodeStmt = unicodeConn.createStatement();
+				PreparedStatement unicodePstmt = unicodeConn
+						.prepareStatement("INSERT INTO testBug4010 VALUES (?)");
+
+				try {
+					unicodeStmt
+							.executeUpdate("DROP TABLE IF EXISTS testBug4010");
+					unicodeStmt
+							.executeUpdate("CREATE TABLE testBug4010 (field1 varchar(10))");
+
+					unicodePstmt.setString(1, origString);
+					unicodePstmt.executeUpdate();
+
+					this.rs = unicodeStmt
+							.executeQuery("SELECT * FROM testBug4010");
+					assertTrue(this.rs.next());
+
+					String stringFromDb = this.rs.getString(1);
+					assertTrue("Retrieved string != sent string", origString
+							.equals(stringFromDb));
+				} finally {
+					unicodeStmt
+							.executeUpdate("DROP TABLE IF EXISTS testBug4010");
+					unicodeStmt.close();
+					unicodePstmt.close();
+					unicodeConn.close();
+				}
+			} else {
+				System.err
+						.println("WARN: Test not valid for servers not running GBK encoding");
+			}
+		} else {
+			System.err
+					.println("WARN: Test not valid for MySQL version > 4.1.0, skipping");
+		}
+	}
+
+	/**
+	 * Tests for regression of encoding forced by user, reported by Jive
+	 * Software
+	 * 
+	 * @throws Exception
+	 *             when encoding is not supported (which is a bug)
+	 */
+	public void testEncodingRegression() throws Exception {
+		Properties props = new Properties();
+		props.put("characterEncoding", "UTF-8");
+		props.put("useUnicode", "true");
+		DriverManager.getConnection(dbUrl, props).close();
+	}
+
+	/**
+	 * Tests fix for BUG#879
+	 * 
+	 * @throws Exception
+	 *             if the bug resurfaces.
+	 */
+	public void testEscapeSJISDoubleEscapeBug() throws Exception {
+		if (!isRunningOnJdk131()) {
+			String testString = "'It\\'s a boy!'";
+
+			byte[] testStringAsBytes = testString.getBytes("SJIS");
+
+			byte[] escapedStringBytes = StringUtils
+					.escapeEasternUnicodeByteStream(testStringAsBytes,
+							testString, 0, testString.length());
+
+			String escapedString = new String(escapedStringBytes, "SJIS");
+
+			assertTrue(testString.equals(escapedString));
+
+			byte[] origByteStream = new byte[] { (byte) 0x95, (byte) 0x5c,
+					(byte) 0x8e, (byte) 0x96, (byte) 0x5c, (byte) 0x62,
+					(byte) 0x5c };
+
+			String origString = "\u955c\u8e96\u5c62\\";
+
+			byte[] newByteStream = StringUtils.escapeEasternUnicodeByteStream(
+					origByteStream, origString, 0, origString.length());
+
+			assertTrue((newByteStream.length == (origByteStream.length + 2))
+					&& (newByteStream[1] == 0x5c) && (newByteStream[2] == 0x5c)
+					&& (newByteStream[5] == 0x5c) && (newByteStream[6] == 0x5c));
+
+			origByteStream = new byte[] { (byte) 0x8d, (byte) 0xb2,
+					(byte) 0x93, (byte) 0x91, (byte) 0x81, (byte) 0x40,
+					(byte) 0x8c, (byte) 0x5c };
+
+			testString = new String(origByteStream, "SJIS");
+
+			Properties connProps = new Properties();
+			connProps.put("useUnicode", "true");
+			connProps.put("characterEncoding", "sjis");
+
+			Connection sjisConn = getConnectionWithProps(connProps);
+			Statement sjisStmt = sjisConn.createStatement();
+
+			try {
+				sjisStmt
+						.executeUpdate("DROP TABLE IF EXISTS doubleEscapeSJISTest");
+				sjisStmt
+						.executeUpdate("CREATE TABLE doubleEscapeSJISTest (field1 BLOB)");
+
+				PreparedStatement sjisPStmt = sjisConn
+						.prepareStatement("INSERT INTO doubleEscapeSJISTest VALUES (?)");
+				sjisPStmt.setString(1, testString);
+				sjisPStmt.executeUpdate();
+
+				this.rs = sjisStmt
+						.executeQuery("SELECT * FROM doubleEscapeSJISTest");
+
+				this.rs.next();
+
+				String retrString = this.rs.getString(1);
+
+				System.out.println(retrString.equals(testString));
+			} finally {
+				sjisStmt
+						.executeUpdate("DROP TABLE IF EXISTS doubleEscapeSJISTest");
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testGreekUtf8411() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			try {
+				Properties newProps = new Properties();
+				newProps.put("useUnicode", "true");
+				newProps.put("characterEncoding", "UTF-8");
+
+				Connection utf8Conn = this.getConnectionWithProps(newProps);
+
+				Statement utfStmt = utf8Conn.createStatement();
+
+				utfStmt.executeUpdate("DROP TABLE IF EXISTS greekunicode");
+				utfStmt
+						.executeUpdate("CREATE TABLE greekunicode(ID INTEGER NOT NULL "
+								+ " AUTO_INCREMENT,UpperCase VARCHAR (30),LowerCase VARCHAR (30),Accented "
+								+ " VARCHAR (30),Special VARCHAR (30),PRIMARY KEY(ID)) TYPE = InnoDB, DEFAULT "
+								+ "CHARACTER SET utf8");
+
+				String upper = "\u0394\u930F\u039A\u0399\u039C\u0397";
+				String lower = "\u03B4\u03BF\u03BA\u03B9\u03BC\u03B7";
+				String accented = "\u03B4\u03CC\u03BA\u03AF\u03BC\u03AE";
+				String special = "\u037E\u03C2\u03B0";
+
+				utfStmt.executeUpdate("INSERT INTO greekunicode VALUES "
+						+ "('1','" + upper + "','" + lower + "','" + accented
+						+ "','" + special + "')");
+
+				this.rs = utfStmt
+						.executeQuery("SELECT UpperCase, LowerCase, Accented, Special from greekunicode");
+
+				this.rs.next();
+
+				assertTrue(upper.equals(this.rs.getString(1)));
+				assertTrue(lower.equals(this.rs.getString(2)));
+				assertTrue(accented.equals(this.rs.getString(3)));
+				assertTrue(special.equals(this.rs.getString(4)));
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS greekunicode");
+			}
+		}
+	}
+
+	/**
+	 * Tests that 'latin1' character conversion works correctly.
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testLatin1Encoding() throws Exception {
+		char[] latin1Charset = { 0x0000, 0x0001, 0x0002, 0x0003, 0x0004,
+				0x0005, 0x0006, 0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C,
+				0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0014,
+				0x0015, 0x0016, 0x0017, 0x0018, 0x0019, 0x001A, 0x001B, 0x001C,
+				0x001D, 0x001E, 0x001F, 0x0020, 0x0021, 0x0022, 0x0023, 0x0024,
+				0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002A, 0x002B, 0x002C,
+				0x002D, 0x002E, 0x002F, 0x0030, 0x0031, 0x0032, 0x0033, 0x0034,
+				0x0035, 0x0036, 0x0037, 0x0038, 0x0039, 0x003A, 0x003B, 0x003C,
+				0x003D, 0x003E, 0x003F, 0x0040, 0x0041, 0x0042, 0x0043, 0x0044,
+				0x0045, 0x0046, 0x0047, 0x0048, 0x0049, 0x004A, 0x004B, 0x004C,
+				0x004D, 0x004E, 0x004F, 0x0050, 0x0051, 0x0052, 0x0053, 0x0054,
+				0x0055, 0x0056, 0x0057, 0x0058, 0x0059, 0x005A, 0x005B, 0x005C,
+				0x005D, 0x005E, 0x005F, 0x0060, 0x0061, 0x0062, 0x0063, 0x0064,
+				0x0065, 0x0066, 0x0067, 0x0068, 0x0069, 0x006A, 0x006B, 0x006C,
+				0x006D, 0x006E, 0x006F, 0x0070, 0x0071, 0x0072, 0x0073, 0x0074,
+				0x0075, 0x0076, 0x0077, 0x0078, 0x0079, 0x007A, 0x007B, 0x007C,
+				0x007D, 0x007E, 0x007F, 0x0080, 0x0081, 0x0082, 0x0083, 0x0084,
+				0x0085, 0x0086, 0x0087, 0x0088, 0x0089, 0x008A, 0x008B, 0x008C,
+				0x008D, 0x008E, 0x008F, 0x0090, 0x0091, 0x0092, 0x0093, 0x0094,
+				0x0095, 0x0096, 0x0097, 0x0098, 0x0099, 0x009A, 0x009B, 0x009C,
+				0x009D, 0x009E, 0x009F, 0x00A0, 0x00A1, 0x00A2, 0x00A3, 0x00A4,
+				0x00A5, 0x00A6, 0x00A7, 0x00A8, 0x00A9, 0x00AA, 0x00AB, 0x00AC,
+				0x00AD, 0x00AE, 0x00AF, 0x00B0, 0x00B1, 0x00B2, 0x00B3, 0x00B4,
+				0x00B5, 0x00B6, 0x00B7, 0x00B8, 0x00B9, 0x00BA, 0x00BB, 0x00BC,
+				0x00BD, 0x00BE, 0x00BF, 0x00C0, 0x00C1, 0x00C2, 0x00C3, 0x00C4,
+				0x00C5, 0x00C6, 0x00C7, 0x00C8, 0x00C9, 0x00CA, 0x00CB, 0x00CC,
+				0x00CD, 0x00CE, 0x00CF, 0x00D0, 0x00D1, 0x00D2, 0x00D3, 0x00D4,
+				0x00D5, 0x00D6, 0x00D7, 0x00D8, 0x00D9, 0x00DA, 0x00DB, 0x00DC,
+				0x00DD, 0x00DE, 0x00DF, 0x00E0, 0x00E1, 0x00E2, 0x00E3, 0x00E4,
+				0x00E5, 0x00E6, 0x00E7, 0x00E8, 0x00E9, 0x00EA, 0x00EB, 0x00EC,
+				0x00ED, 0x00EE, 0x00EF, 0x00F0, 0x00F1, 0x00F2, 0x00F3, 0x00F4,
+				0x00F5, 0x00F6, 0x00F7, 0x00F8, 0x00F9, 0x00FA, 0x00FB, 0x00FC,
+				0x00FD, 0x00FE, 0x00FF };
+
+		String latin1String = new String(latin1Charset);
+		PreparedStatement pStmt = null;
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS latin1RegressTest");
+			this.stmt
+					.executeUpdate("CREATE TABLE latin1RegressTest (stringField TEXT)");
+
+			pStmt = this.conn
+					.prepareStatement("INSERT INTO latin1RegressTest VALUES (?)");
+			pStmt.setString(1, latin1String);
+			pStmt.executeUpdate();
+
+			((com.mysql.jdbc.Connection) this.conn).setTraceProtocol(true);
+
+			this.rs = this.stmt.executeQuery("SELECT * FROM latin1RegressTest");
+			((com.mysql.jdbc.Connection) this.conn).setTraceProtocol(false);
+
+			this.rs.next();
+
+			String retrievedString = this.rs.getString(1);
+
+			System.out.println(latin1String);
+			System.out.println(retrievedString);
+
+			if (!retrievedString.equals(latin1String)) {
+				int stringLength = Math.min(retrievedString.length(),
+						latin1String.length());
+
+				for (int i = 0; i < stringLength; i++) {
+					char rChar = retrievedString.charAt(i);
+					char origChar = latin1String.charAt(i);
+
+					if ((rChar != '?') && (rChar != origChar)) {
+						fail("characters differ at position "
+								+ i
+								+ "'"
+								+ rChar
+								+ "' retrieved from database, original char was '"
+								+ origChar + "'");
+					}
+				}
+			}
+		} finally {
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (Exception ex) {
+					// ignore
+				}
+			}
+
+			if (pStmt != null) {
+				try {
+					pStmt.close();
+				} catch (Exception ex) {
+					// ignore
+				}
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS latin1RegressTest");
+		}
+	}
+
+	/**
+	 * Tests newline being treated correctly.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testNewlines() throws Exception {
+		String newlineStr = "Foo\nBar\n\rBaz";
+
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS newlineRegressTest");
+		this.stmt
+				.executeUpdate("CREATE TABLE newlineRegressTest (field1 MEDIUMTEXT)");
+
+		try {
+			this.stmt.executeUpdate("INSERT INTO newlineRegressTest VALUES ('"
+					+ newlineStr + "')");
+			this.pstmt = this.conn
+					.prepareStatement("INSERT INTO newlineRegressTest VALUES (?)");
+			this.pstmt.setString(1, newlineStr);
+			this.pstmt.executeUpdate();
+
+			this.rs = this.stmt
+					.executeQuery("SELECT * FROM newlineRegressTest");
+
+			while (this.rs.next()) {
+				assertTrue(this.rs.getString(1).equals(newlineStr));
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS newlineRegressTest");
+		}
+	}
+
+	/**
+	 * Tests that single-byte character conversion works correctly.
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	// TODO: Use Unicode Literal escapes for this, for now, this test is
+	// broken :(
+	/*
+	 * public void testSingleByteConversion() throws Exception {
+	 * testConversionForString("latin1", "��� ����");
+	 * testConversionForString("latin1", "Kaarle ��nis Ilmari");
+	 * testConversionForString("latin1",
+	 * "������������������"); }
+	 */
+
+	/**
+	 * Tests that the 0x5c escaping works (we didn't use to have this).
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testSjis5c() throws Exception {
+		byte[] origByteStream = new byte[] { (byte) 0x95, (byte) 0x5c,
+				(byte) 0x8e, (byte) 0x96 };
+
+		//
+		// Print the hex values of the string
+		//
+		StringBuffer bytesOut = new StringBuffer();
+
+		for (int i = 0; i < origByteStream.length; i++) {
+			bytesOut.append(Integer.toHexString(origByteStream[i] & 255));
+			bytesOut.append(" ");
+		}
+
+		System.out.println(bytesOut.toString());
+
+		String origString = new String(origByteStream, "SJIS");
+		byte[] newByteStream = StringUtils.getBytes(origString, "SJIS",
+				"ISO8859_1              ", false, null);
+
+		//
+		// Print the hex values of the string (should have an extra 0x5c)
+		//
+		bytesOut = new StringBuffer();
+
+		for (int i = 0; i < newByteStream.length; i++) {
+			bytesOut.append(Integer.toHexString(newByteStream[i] & 255));
+			bytesOut.append(" ");
+		}
+
+		System.out.println(bytesOut.toString());
+
+		//
+		// Now, insert and retrieve the value from the database
+		//
+		Connection sjisConn = null;
+		Statement sjisStmt = null;
+
+		try {
+			Properties props = new Properties();
+			props.put("useUnicode", "true");
+			props.put("characterEncoding", "SJIS");
+			sjisConn = getConnectionWithProps(props);
+
+			sjisStmt = sjisConn.createStatement();
+
+			this.rs = sjisStmt
+					.executeQuery("SHOW VARIABLES LIKE 'character_set%'");
+
+			while (this.rs.next()) {
+				System.out.println(this.rs.getString(1) + " = "
+						+ this.rs.getString(2));
+			}
+
+			sjisStmt.executeUpdate("DROP TABLE IF EXISTS sjisTest");
+
+			if (versionMeetsMinimum(4, 1)) {
+				sjisStmt
+						.executeUpdate("CREATE TABLE sjisTest (field1 char(50)) DEFAULT CHARACTER SET SJIS");
+			} else {
+				sjisStmt
+						.executeUpdate("CREATE TABLE sjisTest (field1 char(50))");
+			}
+
+			this.pstmt = sjisConn
+					.prepareStatement("INSERT INTO sjisTest VALUES (?)");
+			this.pstmt.setString(1, origString);
+			this.pstmt.executeUpdate();
+
+			this.rs = sjisStmt.executeQuery("SELECT * FROM sjisTest");
+
+			while (this.rs.next()) {
+				byte[] testValueAsBytes = this.rs.getBytes(1);
+
+				bytesOut = new StringBuffer();
+
+				for (int i = 0; i < testValueAsBytes.length; i++) {
+					bytesOut.append(Integer
+							.toHexString(testValueAsBytes[i] & 255));
+					bytesOut.append(" ");
+				}
+
+				System.out.println("Value retrieved from database: "
+						+ bytesOut.toString());
+
+				String testValue = this.rs.getString(1);
+
+				assertTrue(testValue.equals(origString));
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS sjisTest");
+		}
+	}
+
+	/**
+	 * Tests that UTF-8 character conversion works correctly.
+	 * 
+	 * @throws Exception
+	 *             if any errors occur
+	 */
+	public void testUtf8Encoding() throws Exception {
+		Properties props = new Properties();
+		props.put("characterEncoding", "UTF8");
+		props.put("useUnicode", "true");
+		props.put("jdbcCompliantTruncation", "false");
+
+		Connection utfConn = DriverManager.getConnection(dbUrl, props);
+		testConversionForString("UTF8", utfConn, "\u043c\u0438\u0445\u0438");
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             ...
+	 */
+	public void testUtf8Encoding2() throws Exception {
+		String field1 = "K��sel";
+		String field2 = "B�b";
+		byte[] field1AsBytes = field1.getBytes("utf-8");
+		byte[] field2AsBytes = field2.getBytes("utf-8");
+
+		Properties props = new Properties();
+		props.put("characterEncoding", "UTF8");
+		props.put("useUnicode", "true");
+
+		Connection utfConn = DriverManager.getConnection(dbUrl, props);
+		Statement utfStmt = utfConn.createStatement();
+
+		try {
+			utfStmt.executeUpdate("DROP TABLE IF EXISTS testUtf8");
+			utfStmt
+					.executeUpdate("CREATE TABLE testUtf8 (field1 varchar(32), field2 varchar(32)) CHARACTER SET UTF8");
+			utfStmt.executeUpdate("INSERT INTO testUtf8 VALUES ('" + field1
+					+ "','" + field2 + "')");
+
+			PreparedStatement pStmt = utfConn
+					.prepareStatement("INSERT INTO testUtf8 VALUES (?, ?)");
+			pStmt.setString(1, field1);
+			pStmt.setString(2, field2);
+			pStmt.executeUpdate();
+
+			ResultSet rs = utfStmt.executeQuery("SELECT * FROM testUtf8");
+			assertTrue(rs.next());
+
+			// Compare results stored using direct statement
+			// Compare to original string
+			assertTrue(field1.equals(rs.getString(1)));
+			assertTrue(field2.equals(rs.getString(2)));
+
+			// Compare byte-for-byte, ignoring encoding
+			assertTrue(bytesAreSame(field1AsBytes, rs.getBytes(1)));
+			assertTrue(bytesAreSame(field2AsBytes, rs.getBytes(2)));
+
+			assertTrue(rs.next());
+
+			// Compare to original string
+			assertTrue(field1.equals(rs.getString(1)));
+			assertTrue(field2.equals(rs.getString(2)));
+
+			// Compare byte-for-byte, ignoring encoding
+			assertTrue(bytesAreSame(field1AsBytes, rs.getBytes(1)));
+			assertTrue(bytesAreSame(field2AsBytes, rs.getBytes(2)));
+		} finally {
+			utfStmt.executeUpdate("DROP TABLE IF EXISTS testUtf8");
+		}
+	}
+
+	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;
+	}
+
+	private void testConversionForString(String charsetName,
+			Connection convConn, String charsToTest) throws Exception {
+		PreparedStatement pStmt = null;
+
+		try {
+			this.stmt = convConn.createStatement();
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS charConvTest");
+			this.stmt
+					.executeUpdate("CREATE TABLE charConvTest (field1 varchar(255))");
+			this.stmt.executeUpdate("INSERT INTO charConvTest VALUES ('"
+					+ charsToTest + "')");
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS charConvTest_"
+					+ charsetName);
+
+			if (!versionMeetsMinimum(4, 1)) {
+				this.stmt.executeUpdate("CREATE TABLE charConvTest_"
+						+ charsetName + "(field1 CHAR(50))");
+			} else {
+				this.stmt.executeUpdate("CREATE TABLE charConvTest_"
+						+ charsetName + "(field1 CHAR(50) CHARACTER SET "
+						+ charsetName + ")");
+			}
+
+			this.stmt.executeUpdate("INSERT INTO charConvTest_" + charsetName
+					+ " VALUES ('" + charsToTest + "')");
+			pStmt = convConn.prepareStatement("INSERT INTO charConvTest_"
+					+ charsetName + " VALUES (?)");
+			pStmt.setString(1, charsToTest);
+			pStmt.executeUpdate();
+			this.rs = this.stmt.executeQuery("SELECT * FROM charConvTest_"
+					+ charsetName);
+
+			boolean hadRows = false;
+
+			assertTrue(this.rs.next());
+
+			String testValue = this.rs.getString(1);
+			System.out.println(testValue);
+			assertTrue(testValue.equals(charsToTest));
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS charConvTest_"
+					+ charsetName);
+		}
+	}
+
+	private void testConversionForString(String charsetName, String charsToTest)
+			throws Exception {
+		testConversionForString(charsetName, this.conn, charsToTest);
+	}
+
+	/**
+	 * Tests fix for BUG#7601, '+' duplicated in fixDecimalExponent().
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testBug7601() throws Exception {
+		assertTrue("1.5E+7".equals(StringUtils.fixDecimalExponent("1.5E+7")));
+		assertTrue("1.5E-7".equals(StringUtils.fixDecimalExponent("1.5E-7")));
+		assertTrue("1.5E+7".equals(StringUtils.fixDecimalExponent("1.5E7")));
+	}
+
+	public void testBug11629() throws Exception {
+		if (isRunningOnJdk131()) {
+			return;
+		}
+
+		PrintStream oldOut = System.out;
+		PrintStream oldError = System.err;
+
+		try {
+			ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+			PrintStream newOut = new PrintStream(bOut);
+			System.setOut(newOut);
+
+			ByteArrayOutputStream bErr = new ByteArrayOutputStream();
+			PrintStream newErr = new PrintStream(bErr);
+			System.setErr(newErr);
+
+			Properties props = new Properties();
+			props.setProperty("characterEncoding", "utf8");
+			getConnectionWithProps(props).close();
+			String withExclaims = new String(bOut.toByteArray());
+			assertTrue(withExclaims.indexOf("!") == -1);
+			assertTrue(withExclaims.length() == 0); // to catch any other
+			// System.out.printlns()
+
+			withExclaims = new String(bErr.toByteArray());
+			assertTrue(withExclaims.indexOf("!") == -1);
+			assertTrue(withExclaims.length() == 0); // to catch any other
+			// System.err.printlns()
+		} finally {
+			System.setOut(oldOut);
+			System.setErr(oldError);
+		}
+	}
+
+	/**
+	 * Tests fix for BUG#11614 - StringUtils.getBytes() doesn't work when using
+	 * multibyte character encodings and a length in _characters_ is specified.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testBug11614() throws Exception {
+		if (isRunningOnJdk131()) {
+			return; // test not valid on JDK-1.3.1
+		}
+
+		if (versionMeetsMinimum(4, 1)) {
+			createTable(
+					"testBug11614",
+					"(`id` INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,"
+							+ "`text` TEXT NOT NULL,"
+							+ "PRIMARY KEY(`id`)) CHARACTER SET utf8 COLLATE utf8_general_ci");
+
+			Properties props = new Properties();
+			props.setProperty("characterEncoding", "utf8");
+
+			Connection utf8Conn = null;
+
+			try {
+				utf8Conn = getConnectionWithProps(props);
+
+				utf8Conn
+						.createStatement()
+						.executeUpdate(
+								"INSERT INTO testBug11614  (`id`,`text`) values (1,'')");
+				this.rs = utf8Conn.createStatement().executeQuery(
+						"SELECT `text` FROM testBug11614 WHERE id=1");
+				assertTrue(this.rs.next());
+
+				Clob c = this.rs.getClob(1);
+				c.truncate(0);
+				int blockSize = 8192;
+				int sizeToTest = blockSize + 100;
+
+				StringBuffer blockBuf = new StringBuffer(sizeToTest);
+
+				for (int i = 0; i < sizeToTest; i++) {
+					blockBuf.append('\u00f6');
+				}
+
+				String valueToTest = blockBuf.toString();
+
+				c.setString(1, valueToTest);
+				this.pstmt = utf8Conn
+						.prepareStatement("UPDATE testBug11614 SET `text` = ? WHERE id=1");
+				this.pstmt.setClob(1, c);
+				this.pstmt.executeUpdate();
+				this.pstmt.close();
+
+				String fromDatabase = getSingleIndexedValueWithQuery(utf8Conn,
+						1, "SELECT `text` FROM testBug11614").toString();
+				assertEquals(valueToTest, fromDatabase);
+			} finally {
+				if (this.rs != null) {
+					this.rs.close();
+					this.rs = null;
+				}
+
+				if (this.pstmt != null) {
+					this.pstmt.close();
+
+					this.pstmt = null;
+				}
+
+				if (utf8Conn != null) {
+					utf8Conn.close();
+				}
+			}
+		}
+	}
+
+	public void testCodePage1252() throws Exception {
+		if (versionMeetsMinimum(4, 1, 0)) {
+			/*
+			 * from
+			 * ftp://ftp.unicode.org/Public/MAPPINGS/VENDORS/MICSFT/WINDOWS/CP1252.TXT
+			 * 
+			 * 0x80 0x20AC #EURO SIGN 0x81 #UNDEFINED 0x82 0x201A #SINGLE LOW-9
+			 * QUOTATION MARK 0x83 0x0192 #LATIN SMALL LETTER F WITH HOOK 0x84
+			 * 0x201E #DOUBLE LOW-9 QUOTATION MARK 0x85 0x2026 #HORIZONTAL
+			 * ELLIPSIS 0x86 0x2020 #DAGGER 0x87 0x2021 #DOUBLE DAGGER 0x88
+			 * 0x02C6 #MODIFIER LETTER CIRCUMFLEX ACCENT 0x89 0x2030 #PER MILLE
+			 * SIGN 0x8A 0x0160 #LATIN CAPITAL LETTER S WITH CARON 0x8B 0x2039
+			 * #SINGLE LEFT-POINTING ANGLE QUOTATION MARK 0x8C 0x0152 #LATIN
+			 * CAPITAL LIGATURE OE 0x8D #UNDEFINED 0x8E 0x017D #LATIN CAPITAL
+			 * LETTER Z WITH CARON 0x8F #UNDEFINED 0x90 #UNDEFINED
+			 */
+			String codePage1252 = new String(new byte[] { (byte) 0x80,
+					(byte) 0x82, (byte) 0x83, (byte) 0x84, (byte) 0x85,
+					(byte) 0x86, (byte) 0x87, (byte) 0x88, (byte) 0x89,
+					(byte) 0x8a, (byte) 0x8b, (byte) 0x8c, (byte) 0x8e },
+					"Cp1252");
+
+			System.out.println(codePage1252);
+
+			Properties props = new Properties();
+			props.setProperty("characterEncoding", "Cp1252");
+			Connection cp1252Conn = getConnectionWithProps(props);
+			createTable("testCp1252",
+					"(field1 varchar(32) CHARACTER SET latin1)");
+			cp1252Conn.createStatement().executeUpdate(
+					"INSERT INTO testCp1252 VALUES ('" + codePage1252 + "')");
+			this.rs = cp1252Conn.createStatement().executeQuery(
+					"SELECT field1 FROM testCp1252");
+			this.rs.next();
+			assertEquals(this.rs.getString(1), codePage1252);
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/SubqueriesRegressionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/SubqueriesRegressionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/regression/SubqueriesRegressionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,289 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.regression;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests SubQueries on MySQL > 4.1
+ * 
+ * @author Mark Matthews
+ * @version $Id: SubqueriesRegressionTest.java,v 1.1.2.1 2005/05/13 18:58:38
+ *          mmatthews Exp $
+ */
+public class SubqueriesRegressionTest extends BaseTestCase {
+	private final static int REPETITIONS = 100;
+
+	/**
+	 * 
+	 */
+	public SubqueriesRegressionTest(String name) {
+		super(name);
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	public void setUp() throws Exception {
+		// TODO Auto-generated method stub
+		super.setUp();
+	}
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	public void tearDown() throws Exception {
+		// TODO Auto-generated method stub
+		super.tearDown();
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(SubqueriesRegressionTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testSubQuery1() 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 = t1.colB)");
+					assertTrue(this.rs.next());
+					assertTrue("bbbb".equals(this.rs.getString(1)));
+					assertTrue(!this.rs.next());
+				} finally {
+					try {
+						if (this.rs != null) {
+							this.rs.close();
+						}
+					} finally {
+						dropTables();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	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)");
+					assertTrue(this.rs.next());
+					assertTrue("bbbb".equals(this.rs.getString(1)));
+					assertTrue(!this.rs.next());
+				} finally {
+					try {
+						if (this.rs != null) {
+							this.rs.close();
+						}
+					} finally {
+						dropTables();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	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)");
+					assertTrue(this.rs.next());
+					assertTrue("efgh".equals(this.rs.getString(1)));
+					assertTrue("2".equals(this.rs.getString(2)));
+					assertTrue(!this.rs.next());
+				} finally {
+					try {
+						if (this.rs != null) {
+							this.rs.close();
+						}
+					} finally {
+						dropTables();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testSubQuery4() throws Exception {
+		// 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");
+
+					assertTrue(this.rs.next());
+					assertTrue("type1".equals(this.rs.getString(1)));
+					assertTrue("".equals(this.rs.getString(2)));
+
+					assertTrue(this.rs.next());
+					assertTrue("type2".equals(this.rs.getString(1)));
+					assertTrue("".equals(this.rs.getString(2)));
+
+					assertTrue(this.rs.next());
+					assertTrue("type3".equals(this.rs.getString(1)));
+					assertTrue("".equals(this.rs.getString(2)));
+
+					assertTrue(this.rs.next());
+					assertTrue("aaaa".equals(this.rs.getString(1)));
+					assertTrue("'" + this.rs.getString(2)
+							+ "' != expected of 'abcd'", "abcd".equals(this.rs
+							.getString(2)));
+
+					assertTrue(this.rs.next());
+					assertTrue("bbbb".equals(this.rs.getString(1)));
+					assertTrue("efgh".equals(this.rs.getString(2)));
+
+					assertTrue(this.rs.next());
+					assertTrue("cccc".equals(this.rs.getString(1)));
+					assertTrue("'" + this.rs.getString(2)
+							+ "' != expected of 'ijkl'", "ijkl".equals(this.rs
+							.getString(2)));
+
+					assertTrue(!this.rs.next());
+				} finally {
+					try {
+						if (this.rs != null) {
+							this.rs.close();
+						}
+					} finally {
+						dropTables();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testSubQuery5() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			for (int i = 0; i < REPETITIONS; i++) {
+				createTables();
+
+				try {
+					this.rs = this.stmt
+							.executeQuery("select t1.colA from t1, t4 where t4.colA = t1.colA and exists (select 'X' from t2 where t2.colA = t4.colB)");
+					assertTrue(this.rs.next());
+					assertTrue("abcd".equals(this.rs.getString(1)));
+					assertTrue(this.rs.next());
+					assertTrue("efgh".equals(this.rs.getString(1)));
+					assertTrue(this.rs.next());
+					assertTrue("ijkl".equals(this.rs.getString(1)));
+					assertTrue(!this.rs.next());
+				} finally {
+					try {
+						if (this.rs != null) {
+							this.rs.close();
+						}
+					} finally {
+						dropTables();
+					}
+				}
+			}
+		}
+	}
+
+	private void createTables() throws Exception {
+		this.stmt.executeUpdate("drop table if exists t1");
+		this.stmt.executeUpdate("drop table if exists t1");
+		this.stmt.executeUpdate("drop table if exists t2");
+		this.stmt.executeUpdate("drop table if exists t3");
+		this.stmt.executeUpdate("drop table if exists t4");
+		this.stmt
+				.executeUpdate("create table t1(colA varchar(10), colB decimal(3,0))");
+		this.stmt
+				.executeUpdate("create table t2(colA varchar(10), colB varchar(10))");
+		this.stmt
+				.executeUpdate("create table t3(colA varchar(10), colB varchar(10))");
+		this.stmt
+				.executeUpdate("create table t4(colA varchar(10), colB varchar(10))");
+		this.stmt
+				.executeUpdate("insert into t1 values ('abcd', 1), ('efgh', 2), ('ijkl', 3)");
+		this.stmt
+				.executeUpdate("insert into t2 values ('type1', '1'), ('type2', '2'), ('type3', '3')");
+		this.stmt
+				.executeUpdate("insert into t3 values ('aaaa', 'abcd'), ('bbbb', 'efgh'), ('cccc', 'ijkl')");
+		this.stmt
+				.executeUpdate("insert into t4 values ('abcd', 'type1'), ('efgh', 'type2'), ('ijkl', 'type3')");
+	}
+
+	private void dropTables() throws Exception {
+		this.stmt.executeUpdate("drop table if exists t1");
+		this.stmt.executeUpdate("drop table if exists t1");
+		this.stmt.executeUpdate("drop table if exists t2");
+		this.stmt.executeUpdate("drop table if exists t3");
+		this.stmt.executeUpdate("drop table if exists t4");
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/BlobTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/BlobTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/BlobTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,282 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import testsuite.BaseTestCase;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+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 $
+ */
+public class BlobTest extends BaseTestCase {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	private static File testBlobFile;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new BlobTest object.
+	 * 
+	 * @param name
+	 *            the test to run
+	 */
+	public BlobTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(BlobTest.class);
+	}
+
+	/**
+	 * Setup the test case
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+
+		if (versionMeetsMinimum(4, 0)) {
+			int requiredSize = 32 * 1024 * 1024;
+
+			if (testBlobFile == null || testBlobFile.length() != requiredSize) {
+				createBlobFile(requiredSize);
+			}
+
+		} else {
+			int requiredSize = 8 * 1024 * 1024;
+
+			if (testBlobFile == null || testBlobFile.length() != requiredSize) {
+				createBlobFile(requiredSize);
+			}
+		}
+
+		createTestTable();
+	}
+
+	/**
+	 * Destroy resources created by test case
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void tearDown() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS BLOBTEST");
+		} finally {
+			super.tearDown();
+		}
+	}
+
+	public void testByteStreamInsert() throws Exception {
+		testByteStreamInsert(this.conn);
+	}
+	/**
+	 * Tests inserting blob data as a stream
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	private void testByteStreamInsert(Connection c) throws Exception {
+		BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(
+				testBlobFile));
+		this.pstmt = c
+				.prepareStatement("INSERT INTO BLOBTEST(blobdata) VALUES (?)");
+		this.pstmt.setBinaryStream(1, bIn, (int) testBlobFile.length());
+		this.pstmt.execute();
+
+		this.pstmt.clearParameters();
+		doRetrieval();
+	}
+
+	private boolean checkBlob(byte[] retrBytes) throws Exception {
+		boolean passed = false;
+		BufferedInputStream bIn = new BufferedInputStream(new FileInputStream(
+				testBlobFile));
+
+		try {
+			int fileLength = (int) testBlobFile.length();
+			if (retrBytes.length == fileLength) {
+				for (int i = 0; i < fileLength; i++) {
+					byte fromFile = (byte) (bIn.read() & 0xff);
+
+					if (retrBytes[i] != fromFile) {
+						passed = false;
+						System.out.println("Byte pattern differed at position "
+								+ i + " , " + retrBytes[i] + " != " + fromFile);
+
+						for (int j = 0; (j < (i + 10)) /* && (j < i) */; j++) {
+							System.out.print(Integer
+									.toHexString(retrBytes[j] & 0xff)
+									+ " ");
+						}
+
+						break;
+					}
+
+					passed = true;
+				}
+			} else {
+				passed = false;
+				System.out.println("retrBytes.length(" + retrBytes.length
+						+ ") != testBlob.length(" + fileLength + ")");
+			}
+
+			return passed;
+		} finally {
+			if (bIn != null) {
+				bIn.close();
+			}
+		}
+	}
+
+	private void createTestTable() throws Exception {
+		//
+		// Catch the error, the table might exist
+		//
+		try {
+			this.stmt.executeUpdate("DROP TABLE BLOBTEST");
+		} catch (SQLException SQLE) {
+			;
+		}
+
+		this.stmt
+				.executeUpdate("CREATE TABLE BLOBTEST (pos int PRIMARY KEY auto_increment, "
+						+ "blobdata LONGBLOB)");
+	}
+
+	/**
+	 * Mark this as deprecated to avoid warnings from compiler...
+	 * 
+	 * @deprecated
+	 * 
+	 * @throws Exception
+	 *             if an error occurs retrieving the value
+	 */
+	private void doRetrieval() throws Exception {
+		boolean passed = false;
+		this.rs = this.stmt
+				.executeQuery("SELECT blobdata from BLOBTEST LIMIT 1");
+		this.rs.next();
+
+		byte[] retrBytes = this.rs.getBytes(1);
+		passed = checkBlob(retrBytes);
+		assertTrue(
+				"Inserted BLOB data did not match retrieved BLOB data for getBytes().",
+				passed);
+		retrBytes = this.rs.getBlob(1).getBytes(1L,
+				(int) this.rs.getBlob(1).length());
+		passed = checkBlob(retrBytes);
+		assertTrue(
+				"Inserted BLOB data did not match retrieved BLOB data for getBlob().",
+				passed);
+
+		InputStream inStr = this.rs.getBinaryStream(1);
+		ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+		int b;
+
+		while ((b = inStr.read()) != -1) {
+			bOut.write((byte) b);
+		}
+
+		retrBytes = bOut.toByteArray();
+		passed = checkBlob(retrBytes);
+		assertTrue(
+				"Inserted BLOB data did not match retrieved BLOB data for getBinaryStream().",
+				passed);
+		inStr = this.rs.getAsciiStream(1);
+		bOut = new ByteArrayOutputStream();
+
+		while ((b = inStr.read()) != -1) {
+			bOut.write((byte) b);
+		}
+
+		retrBytes = bOut.toByteArray();
+		passed = checkBlob(retrBytes);
+		assertTrue(
+				"Inserted BLOB data did not match retrieved BLOB data for getAsciiStream().",
+				passed);
+		inStr = this.rs.getUnicodeStream(1);
+		bOut = new ByteArrayOutputStream();
+
+		while ((b = inStr.read()) != -1) {
+			bOut.write((byte) b);
+		}
+
+		retrBytes = bOut.toByteArray();
+		passed = checkBlob(retrBytes);
+		assertTrue(
+				"Inserted BLOB data did not match retrieved BLOB data for getUnicodeStream().",
+				passed);
+	}
+
+	private void createBlobFile(int size) throws Exception {
+		if (testBlobFile != null && testBlobFile.length() != size) {
+			testBlobFile.delete();
+		}
+
+		testBlobFile = File.createTempFile("testblob", ".dat");
+		testBlobFile.deleteOnExit();
+
+		BufferedOutputStream bOut = new BufferedOutputStream(
+				new FileOutputStream(testBlobFile));
+
+		int dataRange = Byte.MAX_VALUE - Byte.MIN_VALUE;
+
+		for (int i = 0; i < size; i++) {
+			bOut.write((byte) ((Math.random() * dataRange) + Byte.MIN_VALUE));
+		}
+
+		bOut.flush();
+		bOut.close();
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/CallableStatementTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/CallableStatementTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/CallableStatementTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,499 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import com.mysql.jdbc.SQLError;
+
+import testsuite.BaseTestCase;
+
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+
+import java.util.Properties;
+
+/**
+ * Tests callable statement functionality.
+ * 
+ * @author Mark Matthews
+ * @version $Id: CallableStatementTest.java,v 1.1.2.1 2005/05/13 18:58:37
+ *          mmatthews Exp $
+ */
+public class CallableStatementTest extends BaseTestCase {
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @param name
+	 */
+	public CallableStatementTest(String name) {
+		super(name);
+
+		// TODO Auto-generated constructor stub
+	}
+
+	/**
+	 * Tests functioning of inout parameters
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+
+	public void testInOutParams() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			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");
+
+				storedProc = this.conn.prepareCall("{call testInOutParam(?, ?)}");
+
+				storedProc.setString(1, "abcd");
+				storedProc.setInt(2, 4);
+				storedProc.registerOutParameter(2, Types.INTEGER);
+
+				storedProc.execute();
+		
+				assertEquals(5, storedProc.getInt(2));
+			} finally {
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testInOutParam");
+			}
+		}
+	}
+
+	public void testBatch() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			CallableStatement storedProc = 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"
+								+ "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();
+				
+				assertEquals(2, counts.length);
+				assertEquals(1, counts[0]);
+				assertEquals(1, counts[1]);
+				
+				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));
+			} finally {
+				if (this.rs != null) {
+					this.rs.close();
+					this.rs = null;
+				}
+				
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBatch");
+			}
+		}
+	}
+
+	/**
+	 * Tests functioning of output parameters.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testOutParams() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			CallableStatement storedProc = null;
+
+			try {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testOutParam");
+				this.stmt
+						.executeUpdate("CREATE PROCEDURE testOutParam(x int, out y int)\n"
+								+ "begin\n"
+								+ "declare z int;\n"
+								+ "set z = x+1, y = z;\n" + "end\n");
+
+				storedProc = this.conn.prepareCall("{call testOutParam(?, ?)}");
+
+				storedProc.setInt(1, 5);
+				storedProc.registerOutParameter(2, Types.INTEGER);
+
+				storedProc.execute();
+
+				System.out.println(storedProc);
+
+				int indexedOutParamToTest = storedProc.getInt(2);
+				int namedOutParamToTest = storedProc.getInt("y");
+
+				assertTrue("Named and indexed parameter are not the same",
+						indexedOutParamToTest == namedOutParamToTest);
+				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;
+					}
+				}
+
+				try {
+					storedProc.registerOutParameter(1, 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;
+					}
+				}
+			} finally {
+				this.stmt.executeUpdate("DROP PROCEDURE testOutParam");
+			}
+		}
+	}
+
+	/**
+	 * Tests functioning of output parameters.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testResultSet() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			CallableStatement storedProc = null;
+
+			try {
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testSpResultTbl1");
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testSpResultTbl2");
+				this.stmt
+						.executeUpdate("CREATE TABLE testSpResultTbl1 (field1 INT)");
+				this.stmt
+						.executeUpdate("INSERT INTO testSpResultTbl1 VALUES (1), (2)");
+				this.stmt
+						.executeUpdate("CREATE TABLE testSpResultTbl2 (field2 varchar(255))");
+				this.stmt
+						.executeUpdate("INSERT INTO testSpResultTbl2 VALUES ('abc'), ('def')");
+
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testSpResult");
+				this.stmt
+						.executeUpdate("CREATE PROCEDURE testSpResult()\n"
+								+ "BEGIN\n"
+								+ "SELECT field2 FROM testSpResultTbl2 WHERE field2='abc';\n"
+								+ "UPDATE testSpResultTbl1 SET field1=2;\n"
+								+ "SELECT field2 FROM testSpResultTbl2 WHERE field2='def';\n"
+								+ "end\n");
+
+				storedProc = this.conn.prepareCall("{call testSpResult()}");
+
+				storedProc.execute();
+
+				this.rs = storedProc.getResultSet();
+
+				ResultSetMetaData rsmd = this.rs.getMetaData();
+
+				assertTrue(rsmd.getColumnCount() == 1);
+				assertTrue("field2".equals(rsmd.getColumnName(1)));
+				assertTrue(rsmd.getColumnType(1) == Types.VARCHAR);
+
+				assertTrue(this.rs.next());
+
+				assertTrue("abc".equals(this.rs.getString(1)));
+
+				// TODO: This does not yet work in MySQL 5.0
+				// assertTrue(!storedProc.getMoreResults());
+				// assertTrue(storedProc.getUpdateCount() == 2);
+				assertTrue(storedProc.getMoreResults());
+
+				ResultSet nextResultSet = storedProc.getResultSet();
+
+				rsmd = nextResultSet.getMetaData();
+
+				assertTrue(rsmd.getColumnCount() == 1);
+				assertTrue("field2".equals(rsmd.getColumnName(1)));
+				assertTrue(rsmd.getColumnType(1) == Types.VARCHAR);
+
+				assertTrue(nextResultSet.next());
+
+				assertTrue("def".equals(nextResultSet.getString(1)));
+
+				nextResultSet.close();
+
+				this.rs.close();
+
+				storedProc.execute();
+
+			} finally {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testSpResult");
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testSpResultTbl1");
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testSpResultTbl2");
+			}
+		}
+	}
+
+	/**
+	 * Tests parsing of stored procedures
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testSPParse() throws Exception {
+
+		if (versionMeetsMinimum(5, 0)) {
+
+			CallableStatement storedProc = null;
+
+			try {
+
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testSpParse");
+				this.stmt
+						.executeUpdate("CREATE PROCEDURE testSpParse(IN FOO VARCHAR(15))\n"
+								+ "BEGIN\n" + "SELECT 1;\n" + "end\n");
+
+				storedProc = this.conn.prepareCall("{call testSpParse()}");
+
+			} finally {
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testSpParse");
+			}
+		}
+	}
+
+	/**
+	 * Tests parsing/execution of stored procedures with no parameters...
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testSPNoParams() throws Exception {
+
+		if (versionMeetsMinimum(5, 0)) {
+
+			CallableStatement storedProc = null;
+
+			try {
+
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testSPNoParams");
+				this.stmt.executeUpdate("CREATE PROCEDURE testSPNoParams()\n"
+						+ "BEGIN\n" + "SELECT 1;\n" + "end\n");
+
+				storedProc = this.conn.prepareCall("{call testSPNoParams()}");
+				storedProc.execute();
+
+			} finally {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testSPNoParams");
+			}
+		}
+	}
+
+	/**
+	 * Tests parsing of stored procedures
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testSPCache() throws Exception {
+
+		if (versionMeetsMinimum(5, 0)) {
+
+			CallableStatement storedProc = null;
+
+			try {
+
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testSpParse");
+				this.stmt
+						.executeUpdate("CREATE PROCEDURE testSpParse(IN FOO VARCHAR(15))\n"
+								+ "BEGIN\n" + "SELECT 1;\n" + "end\n");
+
+				int numIterations = 10000;
+
+				long startTime = System.currentTimeMillis();
+
+				for (int i = 0; i < numIterations; i++) {
+					storedProc = this.conn.prepareCall("{call testSpParse(?)}");
+					storedProc.close();
+				}
+
+				long elapsedTime = System.currentTimeMillis() - startTime;
+
+				System.out.println("Standard parsing/execution: " + elapsedTime
+						+ " ms");
+
+				storedProc = this.conn.prepareCall("{call testSpParse(?)}");
+				storedProc.setString(1, "abc");
+				this.rs = storedProc.executeQuery();
+
+				assertTrue(this.rs.next());
+				assertTrue(this.rs.getInt(1) == 1);
+
+				Properties props = new Properties();
+				props.setProperty("cacheCallableStmts", "true");
+
+				Connection cachedSpConn = getConnectionWithProps(props);
+
+				startTime = System.currentTimeMillis();
+
+				for (int i = 0; i < numIterations; i++) {
+					storedProc = cachedSpConn
+							.prepareCall("{call testSpParse(?)}");
+					storedProc.close();
+				}
+
+				elapsedTime = System.currentTimeMillis() - startTime;
+
+				System.out
+						.println("Cached parse stage: " + elapsedTime + " ms");
+
+				storedProc = cachedSpConn.prepareCall("{call testSpParse(?)}");
+				storedProc.setString(1, "abc");
+				this.rs = storedProc.executeQuery();
+
+				assertTrue(this.rs.next());
+				assertTrue(this.rs.getInt(1) == 1);
+
+			} finally {
+				this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testSpParse");
+			}
+		}
+	}
+	
+	public void testOutParamsNoBodies() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			CallableStatement storedProc = null;
+
+			Properties props = new Properties();
+			props.setProperty("noAccessToProcedureBodies", "true");
+			
+			Connection spConn = getConnectionWithProps(props);
+			
+			try {
+				this.stmt
+						.executeUpdate("DROP PROCEDURE IF EXISTS testOutParam");
+				this.stmt
+						.executeUpdate("CREATE PROCEDURE testOutParam(x int, out y int)\n"
+								+ "begin\n"
+								+ "declare z int;\n"
+								+ "set z = x+1, y = z;\n" + "end\n");
+
+				storedProc = spConn.prepareCall("{call testOutParam(?, ?)}");
+
+				storedProc.setInt(1, 5);
+				storedProc.registerOutParameter(2, Types.INTEGER);
+
+				storedProc.execute();
+
+				int indexedOutParamToTest = storedProc.getInt(2);
+			
+				assertTrue("Output value not returned correctly",
+						indexedOutParamToTest == 6);
+
+				
+				storedProc.clearParameters();
+				storedProc.setInt(1, 32);
+				storedProc.registerOutParameter(2, Types.INTEGER);
+
+				storedProc.execute();
+
+				indexedOutParamToTest = storedProc.getInt(2);
+				
+				assertTrue("Output value not returned correctly",
+						indexedOutParamToTest == 33);
+			} finally {
+				this.stmt.executeUpdate("DROP PROCEDURE testOutParam");
+			}
+		}
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(CallableStatementTest.class);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/CharsetTests.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/CharsetTests.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/CharsetTests.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,293 @@
+/*
+    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 testsuite.simple;
+
+import java.sql.Connection;
+import java.sql.Statement;
+import java.util.ArrayList;
+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 testsuite.BaseTestCase;
+
+public class CharsetTests extends BaseTestCase {
+
+	public CharsetTests(String name) {
+		super(name);
+		// TODO Auto-generated constructor stub
+	}
+
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(CharsetTests.class);
+	}
+
+	public void testCP932Backport() throws Exception {
+		if (versionMeetsMinimum(4, 1, 12)) {
+			if (versionMeetsMinimum(5, 0)) {
+				if (!versionMeetsMinimum(5, 0, 3)) {
+					return;
+				}
+			}
+
+			Properties props = new Properties();
+			props.put("useUnicode", "true");
+			props.put("characterEncoding", "WINDOWS-31J");
+			getConnectionWithProps(props).close();
+		}
+	}
+
+	public void testNECExtendedCharsByEUCJPSolaris() throws Exception {
+		if (!isRunningOnJdk131()) {
+			if (versionMeetsMinimum(5, 0, 5)) {
+				char necExtendedChar = 0x3231; // 0x878A of WINDOWS-31J, NEC
+				// special(row13).
+				String necExtendedCharString = String.valueOf(necExtendedChar);
+	
+				Properties props = new Properties();
+				
+				props.put("useUnicode", "true");
+				props.put("characterEncoding", "EUC_JP_Solaris");
+	
+				Connection conn2 = getConnectionWithProps(props);
+				Statement stmt2 = conn2.createStatement();
+	
+				stmt2.executeUpdate("DROP TABLE IF EXISTS t_eucjpms");
+				createTable("t_eucjpms", "(c1 char(1))"
+						+ " default character set = eucjpms");
+				stmt2.executeUpdate("INSERT INTO t_eucjpms VALUES ('"
+						+ necExtendedCharString + "')");
+				this.rs = stmt2.executeQuery("SELECT c1 FROM t_eucjpms");
+				this.rs.next();
+				assertEquals(necExtendedCharString, this.rs.getString("c1"));
+	
+				this.rs.close();
+				stmt2.close();
+				conn2.close();
+	
+				props.put("characterSetResults", "EUC_JP_Solaris");
+				conn2 = getConnectionWithProps(props);
+				stmt2 = conn.createStatement();
+	
+				this.rs = stmt2.executeQuery("SELECT c1 FROM t_eucjpms");
+				this.rs.next();
+				assertEquals(necExtendedCharString, rs.getString("c1"));
+	
+				stmt2.executeUpdate("DROP TABLE t_eucjpms");
+				this.rs.close();
+				stmt2.close();
+				conn2.close();
+			}
+		}
+	}
+
+	/**
+	 * Test data of sjis. sjis consists of ASCII, JIS-Roman, JISX0201 and
+	 * JISX0208.
+	 */
+	public static final char[] SJIS_CHARS = new char[] { 0xFF71, // halfwidth
+			// katakana
+			// letter A,
+			// 0xB100 of
+			// SJIS, one
+			// of
+			// JISX0201.
+			0x65E5, // CJK unified ideograph, 0x93FA of SJIS, one of JISX0208.
+			0x8868, // CJK unified ideograph, 0x955C of SJIS, one of '5c'
+			// character.
+			0x2016 // 0x8161 of SJIS/WINDOWS-31J, converted to differently
+	// to/from ucs2
+	};
+
+	/**
+	 * Test data of cp932. WINDOWS-31J consists of ASCII, JIS-Roman, JISX0201,
+	 * JISX0208, NEC special characters(row13), NEC selected IBM special
+	 * characters, and IBM special characters.
+	 */
+	private static final char[] CP932_CHARS = new char[] { 0xFF71, // halfwidth
+			// katakana
+			// letter A,
+			// 0xB100 of
+			// WINDOWS-31J,
+			// one of
+			// JISX0201.
+			0x65E5, // CJK unified ideograph, 0x93FA of WINDOWS-31J, one of
+			// JISX0208.
+			0x3231, // parenthesized ideograph stok, 0x878B of WINDOWS-31J, one
+			// of NEC special characters(row13).
+			0x67BB, // CJK unified ideograph, 0xEDC6 of WINDOWS-31J, one of NEC
+			// selected IBM special characters.
+			0x6D6F, // CJK unified ideograph, 0xFAFC of WINDOWS-31J, one of IBM
+			// special characters.
+			0x8868, // one of CJK unified ideograph, 0x955C of WINDOWS-31J, one
+			// of '5c' characters.
+			0x2225 // 0x8161 of SJIS/WINDOWS-31J, converted to differently
+	// to/from ucs2
+	};
+
+	/**
+	 * Test data of ujis. ujis consists of ASCII, JIS-Roman, JISX0201, JISX0208,
+	 * JISX0212.
+	 */
+	public static final char[] UJIS_CHARS = new char[] { 0xFF71, // halfwidth
+			// katakana
+			// letter A,
+			// 0x8EB1 of
+			// ujis, one
+			// of
+			// JISX0201.
+			0x65E5, // CJK unified ideograph, 0xC6FC of ujis, one of JISX0208.
+			0x7B5D, // CJK unified ideograph, 0xE4B882 of ujis, one of JISX0212
+			0x301C // wave dash, 0xA1C1 of ujis, convertion rule is different
+	// from ujis
+	};
+
+	/**
+	 * Test data of eucjpms. ujis consists of ASCII, JIS-Roman, JISX0201,
+	 * JISX0208, JISX0212, NEC special characters(row13)
+	 */
+	public static final char[] EUCJPMS_CHARS = new char[] { 0xFF71, // halfwidth
+			// katakana
+			// letter A,
+			// 0x8EB1 of
+			// ujis, one
+			// of
+			// JISX0201.
+			0x65E5, // CJK unified ideograph, 0xC6FC of ujis, one of JISX0208.
+			0x7B5D, // CJK unified ideograph, 0xE4B882 of ujis, one of JISX0212
+			0x3231, // parenthesized ideograph stok, 0x878A of WINDOWS-31J, one
+			// of NEC special characters(row13).
+			0xFF5E // wave dash, 0xA1C1 of eucjpms, convertion rule is
+	// different from ujis
+	};
+
+	public void testInsertCharStatement() throws Exception {
+		if (!isRunningOnJdk131()) {
+			if (versionMeetsMinimum(4, 1, 12)) {
+				Map testDataMap = new HashMap();
+	
+				List charsetList = new ArrayList();
+	
+				Map connectionMap = new HashMap();
+	
+				Map connectionWithResultMap = new HashMap();
+	
+				Map statementMap = new HashMap();
+	
+				Map statementWithResultMap = new HashMap();
+	
+				Map javaToMysqlCharsetMap = new HashMap();
+	
+				charsetList.add("SJIS");
+				testDataMap.put("SJIS", SJIS_CHARS);
+				javaToMysqlCharsetMap.put("SJIS", "sjis");
+	
+				charsetList.add("Shift_JIS");
+				testDataMap.put("Shift_JIS", SJIS_CHARS);
+				javaToMysqlCharsetMap.put("Shift_JIS", "sjis");
+	
+				charsetList.add("CP943");
+				testDataMap.put("CP943", SJIS_CHARS);
+				javaToMysqlCharsetMap.put("CP943", "sjis");
+	
+				if (versionMeetsMinimum(5, 0, 3)) {
+					charsetList.add("WINDOWS-31J");
+					testDataMap.put("WINDOWS-31J", CP932_CHARS);
+					javaToMysqlCharsetMap.put("WINDOWS-31J", "cp932");
+	
+					charsetList.add("MS932");
+					testDataMap.put("MS932", CP932_CHARS);
+					javaToMysqlCharsetMap.put("MS932", "cp932");
+	
+					charsetList.add("EUC_JP");
+					testDataMap.put("EUC_JP", UJIS_CHARS);
+					// testDataHexMap.put("EUC_JP", UJIS_CHARS_HEX);
+					javaToMysqlCharsetMap.put("EUC_JP", "ujis");
+	
+					charsetList.add("EUC_JP_Solaris");
+					testDataMap.put("EUC_JP_Solaris", EUCJPMS_CHARS);
+					// testDataHexMap.put("EUC_JP_Solaris", EUCJPMS_CHARS_HEX);
+					javaToMysqlCharsetMap.put("EUC_JP_Solaris", "eucjpms");
+	
+				} else {
+					charsetList.add("EUC_JP");
+					testDataMap.put("EUC_JP", UJIS_CHARS);
+					javaToMysqlCharsetMap.put("EUC_JP", "ujis");
+				}
+	
+				Iterator charsetIterator = charsetList.iterator();
+	
+				while (charsetIterator.hasNext()) {
+					String charset = (String) charsetIterator.next();
+					Properties props = new Properties();
+					
+					props.put("useUnicode", "true");
+					props.put("characterEncoding", charset);
+					Connection conn2 = getConnectionWithProps(props);
+					connectionMap.put(charset.toLowerCase(Locale.ENGLISH), conn2);
+					statementMap.put(charset.toLowerCase(Locale.ENGLISH), conn2
+							.createStatement());
+	
+					props.put("characterSetResult", charset);
+					Connection connWithResult = getConnectionWithProps(props);
+					connectionWithResultMap.put(charset, connWithResult);
+					statementWithResultMap.put(charset, connWithResult
+							.createStatement());
+				}
+	
+				charsetIterator = charsetList.iterator();
+				while (charsetIterator.hasNext()) {
+					String charset = (String) charsetIterator.next();
+	
+					String mysqlCharset = (String) javaToMysqlCharsetMap
+							.get(charset);
+					Statement stmt2 = (Statement) statementMap.get(charset
+							.toLowerCase(Locale.ENGLISH));
+					String query1 = "DROP TABLE IF EXISTS t1";
+					String query2 = "CREATE TABLE t1 (c1 int, c2 char(1)) "
+							+ "DEFAULT CHARACTER SET = " + mysqlCharset;
+					stmt2.executeUpdate(query1);
+					stmt2.executeUpdate(query2);
+					char[] testData = (char[]) testDataMap.get(charset);
+					for (int i = 0; i < testData.length; i++) {
+						String query3 = "INSERT INTO t1 values(" + i + ", '"
+								+ testData[i] + "')";
+						stmt2.executeUpdate(query3);
+						String query4 = "SELECT c2 FROM t1 WHERE c1 = " + i;
+						this.rs = stmt2.executeQuery(query4);
+						this.rs.next();
+						String value = rs.getString(1);
+	
+						assertEquals("For character set " + charset + "/ "
+								+ mysqlCharset, String.valueOf(testData[i]), value);
+					}
+					String query5 = "DROP TABLE t1";
+					stmt2.executeUpdate(query5);
+				}
+			}
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/ConnectionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/ConnectionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/ConnectionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1203 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import testsuite.BaseTestCase;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Savepoint;
+import java.sql.Statement;
+
+import java.util.Properties;
+import java.util.StringTokenizer;
+
+import com.mysql.jdbc.ConnectionPropertiesTransform;
+import com.mysql.jdbc.NonRegisteringDriver;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.StringUtils;
+import com.mysql.jdbc.log.StandardLogger;
+
+/**
+ * Tests java.sql.Connection functionality ConnectionTest.java,v 1.1 2002/12/06
+ * 22:01:05 mmatthew Exp
+ * 
+ * @author Mark Matthews
+ */
+public class ConnectionTest extends BaseTestCase {
+	/**
+	 * Constructor for ConnectionTest.
+	 * 
+	 * @param name
+	 *            the name of the test to run
+	 */
+	public ConnectionTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(ConnectionTest.class);
+	}
+
+	/**
+	 * Tests catalog functionality
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testCatalog() throws Exception {
+		String currentCatalog = this.conn.getCatalog();
+		this.conn.setCatalog(currentCatalog);
+		assertTrue(currentCatalog.equals(this.conn.getCatalog()));
+	}
+
+	/**
+	 * Tests a cluster connection for failover, requires a two-node cluster URL
+	 * specfied in com.mysql.jdbc.testsuite.ClusterUrl system proeprty.
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testClusterConnection() throws Exception {
+		String url = System.getProperty("com.mysql.jdbc.testsuite.ClusterUrl");
+
+		if ((url != null) && (url.length() > 0)) {
+			Object versionNumObj = getSingleValueWithQuery("SHOW VARIABLES LIKE 'version'");
+
+			if ((versionNumObj != null)
+					&& (versionNumObj.toString().indexOf("cluster") != -1)) {
+				Connection clusterConn = null;
+				Statement clusterStmt = null;
+
+				try {
+					clusterConn = new NonRegisteringDriver().connect(url, null);
+
+					clusterStmt = clusterConn.createStatement();
+					clusterStmt
+							.executeQuery("DROP TABLE IF EXISTS testClusterConn");
+					clusterStmt
+							.executeQuery("CREATE TABLE testClusterConn (field1 INT) TYPE=ndbcluster");
+					clusterStmt
+							.executeQuery("INSERT INTO testClusterConn VALUES (1)");
+
+					clusterConn.setAutoCommit(false);
+
+					clusterStmt.executeQuery("SELECT * FROM testClusterConn");
+					clusterStmt
+							.executeUpdate("UPDATE testClusterConn SET field1=4");
+
+					// Kill the connection
+					String connectionId = getSingleValueWithQuery(
+							"SELECT CONNECTION_ID()").toString();
+
+					System.out
+							.println("Please kill the MySQL server now and press return...");
+					System.in.read();
+
+					System.out.println("Waiting for TCP/IP timeout...");
+					Thread.sleep(10);
+
+					System.out.println("Attempting auto reconnect");
+
+					try {
+						clusterConn.setAutoCommit(true);
+						clusterConn.setAutoCommit(false);
+					} catch (SQLException sqlEx) {
+						System.out.println(sqlEx);
+					}
+
+					//
+					// Test that this 'new' connection is not read-only
+					//
+					clusterStmt
+							.executeUpdate("UPDATE testClusterConn SET field1=5");
+
+					ResultSet rs = clusterStmt
+							.executeQuery("SELECT * FROM testClusterConn WHERE field1=5");
+
+					assertTrue("One row should be returned", rs.next());
+				} finally {
+					if (clusterStmt != null) {
+						clusterStmt
+								.executeQuery("DROP TABLE IF EXISTS testClusterConn");
+						clusterStmt.close();
+					}
+
+					if (clusterConn != null) {
+						clusterConn.close();
+					}
+				}
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testDeadlockDetection() throws Exception {
+		try {
+			this.rs = this.stmt
+					.executeQuery("SHOW VARIABLES LIKE 'innodb_lock_wait_timeout'");
+			this.rs.next();
+
+			int timeoutSecs = this.rs.getInt(2);
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS t1");
+			this.stmt
+					.executeUpdate("CREATE TABLE t1 (id INTEGER, x INTEGER) TYPE=INNODB");
+			this.stmt.executeUpdate("INSERT INTO t1 VALUES(0, 0)");
+			this.conn.setAutoCommit(false);
+			this.conn.createStatement().executeQuery(
+					"SELECT * FROM t1 WHERE id=0 FOR UPDATE");
+
+			Connection deadlockConn = getConnectionWithProps(new Properties());
+			deadlockConn.setAutoCommit(false);
+
+			// The following query should hang because con1 is locking the page
+			deadlockConn.createStatement().executeUpdate(
+					"UPDATE t1 SET x=2 WHERE id=0");
+			deadlockConn.commit();
+
+			Thread.sleep(timeoutSecs * 2 * 1000);
+		} catch (SQLException sqlEx) {
+			System.out
+					.println("Caught SQLException due to deadlock/lock timeout");
+			System.out.println("SQLState: " + sqlEx.getSQLState());
+			System.out.println("Vendor error: " + sqlEx.getErrorCode());
+			System.out.println("Message: " + sqlEx.getMessage());
+
+			//
+			// Check whether the driver thinks it really is deadlock...
+			//
+			assertTrue(SQLError.SQL_STATE_DEADLOCK.equals(sqlEx.getSQLState()));
+			assertTrue(sqlEx.getErrorCode() == 1205);
+		} finally {
+			this.conn.setAutoCommit(true);
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS t1");
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testCharsets() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			try {
+				Properties props = new Properties();
+				props.setProperty("useUnicode", "true");
+				props.setProperty("characterEncoding", "UTF-8");
+
+				Connection utfConn = getConnectionWithProps(props);
+
+				this.stmt = utfConn.createStatement();
+
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS t1");
+				// this.stmt.executeUpdate("SET CHARACTER SET latin1");
+
+				this.stmt.executeUpdate("CREATE TABLE t1 ("
+						+ "comment CHAR(32) ASCII NOT NULL,"
+						+ "koi8_ru_f CHAR(32) CHARACTER SET koi8r NOT NULL"
+						+ ") CHARSET=latin5");
+
+				this.stmt
+						.executeUpdate("ALTER TABLE t1 CHANGE comment comment CHAR(32) CHARACTER SET latin2 NOT NULL");
+				this.stmt
+						.executeUpdate("ALTER TABLE t1 ADD latin5_f CHAR(32) NOT NULL");
+				this.stmt.executeUpdate("ALTER TABLE t1 CHARSET=latin2");
+				this.stmt
+						.executeUpdate("ALTER TABLE t1 ADD latin2_f CHAR(32) NOT NULL");
+				this.stmt
+						.executeUpdate("ALTER TABLE t1 DROP latin2_f, DROP latin5_f");
+
+				this.stmt
+						.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) VALUES ('a','LAT SMALL A')");
+				/*
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('b','LAT SMALL B')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('c','LAT SMALL C')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('d','LAT SMALL D')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('e','LAT SMALL E')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('f','LAT SMALL F')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('g','LAT SMALL G')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('h','LAT SMALL H')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('i','LAT SMALL I')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('j','LAT SMALL J')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('k','LAT SMALL K')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('l','LAT SMALL L')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('m','LAT SMALL M')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('n','LAT SMALL N')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('o','LAT SMALL O')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('p','LAT SMALL P')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('q','LAT SMALL Q')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('r','LAT SMALL R')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('s','LAT SMALL S')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('t','LAT SMALL T')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('u','LAT SMALL U')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('v','LAT SMALL V')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('w','LAT SMALL W')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('x','LAT SMALL X')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('y','LAT SMALL Y')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('z','LAT SMALL Z')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('A','LAT CAPIT A')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('B','LAT CAPIT B')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('C','LAT CAPIT C')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('D','LAT CAPIT D')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('E','LAT CAPIT E')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('F','LAT CAPIT F')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('G','LAT CAPIT G')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('H','LAT CAPIT H')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('I','LAT CAPIT I')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('J','LAT CAPIT J')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('K','LAT CAPIT K')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('L','LAT CAPIT L')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('M','LAT CAPIT M')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('N','LAT CAPIT N')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('O','LAT CAPIT O')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('P','LAT CAPIT P')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('Q','LAT CAPIT Q')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('R','LAT CAPIT R')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('S','LAT CAPIT S')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('T','LAT CAPIT T')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('U','LAT CAPIT U')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('V','LAT CAPIT V')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('W','LAT CAPIT W')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('X','LAT CAPIT X')"); this.stmt.executeUpdate("INSERT
+				 * INTO t1 (koi8_ru_f,comment) VALUES ('Y','LAT CAPIT Y')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES ('Z','LAT CAPIT Z')");
+				 */
+
+				String cyrillicSmallA = "\u0430";
+				this.stmt
+						.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment) VALUES ('"
+								+ cyrillicSmallA + "','CYR SMALL A')");
+
+				/*
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL BE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL VE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL GE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL DE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL IE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL IO')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL ZHE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL ZE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL I')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL KA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL EL')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL EM')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL EN')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL O')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL PE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL ER')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL ES')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL TE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL U')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL EF')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL HA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL TSE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL CHE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL SHA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL SCHA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL HARD SIGN')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL YERU')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL SOFT SIGN')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL E')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL YU')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR SMALL YA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT A')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT BE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT VE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT GE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT DE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT IE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT IO')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT ZHE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT ZE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT I')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT KA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT EL')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT EM')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT EN')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT O')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT PE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT ER')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT ES')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT TE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT U')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT EF')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT HA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT TSE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT CHE')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT SHA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT SCHA')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT HARD SIGN')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT YERU')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT SOFT SIGN')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT E')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT YU')");
+				 * this.stmt.executeUpdate("INSERT INTO t1 (koi8_ru_f,comment)
+				 * VALUES (_koi8r'?¿½','CYR CAPIT YA')");
+				 */
+
+				this.stmt
+						.executeUpdate("ALTER TABLE t1 ADD utf8_f CHAR(32) CHARACTER SET utf8 NOT NULL");
+				this.stmt
+						.executeUpdate("UPDATE t1 SET utf8_f=CONVERT(koi8_ru_f USING utf8)");
+				this.stmt.executeUpdate("SET CHARACTER SET koi8r");
+				// this.stmt.executeUpdate("SET CHARACTER SET UTF8");
+				this.rs = this.stmt.executeQuery("SELECT * FROM t1");
+
+				ResultSetMetaData rsmd = this.rs.getMetaData();
+
+				int numColumns = rsmd.getColumnCount();
+
+				for (int i = 0; i < numColumns; i++) {
+					System.out.print(rsmd.getColumnName(i + 1));
+					System.out.print("\t\t");
+				}
+
+				System.out.println();
+
+				while (this.rs.next()) {
+					System.out.println(this.rs.getString(1) + "\t\t"
+							+ this.rs.getString(2) + "\t\t"
+							+ this.rs.getString(3));
+
+					if (this.rs.getString(1).equals("CYR SMALL A")) {
+						this.rs.getString(2);
+					}
+				}
+
+				System.out.println();
+
+				this.stmt.executeUpdate("SET NAMES utf8");
+				this.rs = this.stmt.executeQuery("SELECT _koi8r 0xC1;");
+
+				rsmd = this.rs.getMetaData();
+
+				numColumns = rsmd.getColumnCount();
+
+				for (int i = 0; i < numColumns; i++) {
+					System.out.print(rsmd.getColumnName(i + 1));
+					System.out.print("\t\t");
+				}
+
+				System.out.println();
+
+				while (this.rs.next()) {
+					System.out.println(this.rs.getString(1).equals("\u0430")
+							+ "\t\t");
+					System.out
+							.println(new String(this.rs.getBytes(1), "KOI8_R"));
+
+				}
+
+				char[] c = new char[] { 0xd0b0 };
+
+				System.out.println(new String(c));
+				System.out.println("\u0430");
+			} finally {
+				// this.stmt.executeUpdate("DROP TABLE IF EXISTS t1");
+			}
+		}
+	}
+
+	/**
+	 * Tests isolation level functionality
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testIsolationLevel() throws Exception {
+		if (versionMeetsMinimum(4, 0)) {
+			String[] isoLevelNames = new String[] {
+					"Connection.TRANSACTION_NONE",
+					"Connection.TRANSACTION_READ_COMMITTED",
+					"Connection.TRANSACTION_READ_UNCOMMITTED",
+					"Connection.TRANSACTION_REPEATABLE_READ",
+					"Connection.TRANSACTION_SERIALIZABLE" };
+
+			int[] isolationLevels = new int[] { Connection.TRANSACTION_NONE,
+					Connection.TRANSACTION_READ_COMMITTED,
+					Connection.TRANSACTION_READ_UNCOMMITTED,
+					Connection.TRANSACTION_REPEATABLE_READ,
+					Connection.TRANSACTION_SERIALIZABLE };
+
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+
+			for (int i = 0; i < isolationLevels.length; i++) {
+				if (dbmd.supportsTransactionIsolationLevel(isolationLevels[i])) {
+					this.conn.setTransactionIsolation(isolationLevels[i]);
+
+					assertTrue(
+							"Transaction isolation level that was set ("
+									+ isoLevelNames[i]
+									+ ") was not returned, nor was a more restrictive isolation level used by the server",
+							this.conn.getTransactionIsolation() == isolationLevels[i]
+									|| this.conn.getTransactionIsolation() > isolationLevels[i]);
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests the savepoint functionality in MySQL.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testSavepoint() throws Exception {
+		if (!isRunningOnJdk131()) {
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+	
+			if (dbmd.supportsSavepoints()) {
+				System.out.println("Testing SAVEPOINTs");
+	
+				try {
+					this.conn.setAutoCommit(true);
+	
+					this.stmt.executeUpdate("DROP TABLE IF EXISTS testSavepoints");
+					this.stmt
+							.executeUpdate("CREATE TABLE testSavepoints (field1 int) TYPE=InnoDB");
+	
+					// Try with named save points
+					this.conn.setAutoCommit(false);
+					this.stmt
+							.executeUpdate("INSERT INTO testSavepoints VALUES (1)");
+	
+					Savepoint afterInsert = this.conn.setSavepoint("afterInsert");
+					this.stmt.executeUpdate("UPDATE testSavepoints SET field1=2");
+	
+					Savepoint afterUpdate = this.conn.setSavepoint("afterUpdate");
+					this.stmt.executeUpdate("DELETE FROM testSavepoints");
+	
+					assertTrue("Row count should be 0",
+							getRowCount("testSavepoints") == 0);
+					this.conn.rollback(afterUpdate);
+					assertTrue("Row count should be 1",
+							getRowCount("testSavepoints") == 1);
+					assertTrue("Value should be 2", "2".equals(getSingleValue(
+							"testSavepoints", "field1", null).toString()));
+					this.conn.rollback(afterInsert);
+					assertTrue("Value should be 1", "1".equals(getSingleValue(
+							"testSavepoints", "field1", null).toString()));
+					this.conn.rollback();
+					assertTrue("Row count should be 0",
+							getRowCount("testSavepoints") == 0);
+	
+					// Try with 'anonymous' save points
+					this.conn.rollback();
+	
+					this.stmt
+							.executeUpdate("INSERT INTO testSavepoints VALUES (1)");
+					afterInsert = this.conn.setSavepoint();
+					this.stmt.executeUpdate("UPDATE testSavepoints SET field1=2");
+					afterUpdate = this.conn.setSavepoint();
+					this.stmt.executeUpdate("DELETE FROM testSavepoints");
+	
+					assertTrue("Row count should be 0",
+							getRowCount("testSavepoints") == 0);
+					this.conn.rollback(afterUpdate);
+					assertTrue("Row count should be 1",
+							getRowCount("testSavepoints") == 1);
+					assertTrue("Value should be 2", "2".equals(getSingleValue(
+							"testSavepoints", "field1", null).toString()));
+					this.conn.rollback(afterInsert);
+					assertTrue("Value should be 1", "1".equals(getSingleValue(
+							"testSavepoints", "field1", null).toString()));
+					this.conn.rollback();
+	
+					this.conn.releaseSavepoint(this.conn.setSavepoint());
+				} finally {
+					this.conn.setAutoCommit(true);
+					this.stmt.executeUpdate("DROP TABLE IF EXISTS testSavepoints");
+				}
+			} else {
+				System.out.println("MySQL version does not support SAVEPOINTs");
+			}
+		}
+	}
+
+	/**
+	 * Tests the ability to set the connection collation via properties.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs or the test fails
+	 */
+	public void testNonStandardConnectionCollation() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			String collationToSet = "utf8_bin";
+			String characterSet = "utf8";
+
+			Properties props = new Properties();
+			props.setProperty("connectionCollation", collationToSet);
+			props.setProperty("characterEncoding", characterSet);
+
+			Connection collConn = null;
+			Statement collStmt = null;
+			ResultSet collRs = null;
+
+			try {
+				collConn = getConnectionWithProps(props);
+
+				collStmt = collConn.createStatement();
+
+				collRs = collStmt
+						.executeQuery("SHOW VARIABLES LIKE 'collation_connection'");
+
+				assertTrue(collRs.next());
+				assertTrue(collationToSet.equalsIgnoreCase(collRs.getString(2)));
+			} finally {
+				if (collConn != null) {
+					collConn.close();
+				}
+			}
+		}
+	}
+
+	public void testDumpQueriesOnException() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("dumpQueriesOnException", "true");
+		String bogusSQL = "SELECT 1 TO BAZ";
+		Connection dumpConn = getConnectionWithProps(props);
+
+		try {
+			dumpConn.createStatement().executeQuery(bogusSQL);
+		} catch (SQLException sqlEx) {
+			assertTrue(sqlEx.getMessage().indexOf(bogusSQL) != -1);
+		}
+
+		try {
+			((com.mysql.jdbc.Connection) dumpConn).clientPrepareStatement(
+					bogusSQL).executeQuery();
+		} catch (SQLException sqlEx) {
+			assertTrue(sqlEx.getMessage().indexOf(bogusSQL) != -1);
+		}
+
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testDumpQueriesOnException");
+			this.stmt
+					.executeUpdate("CREATE TABLE testDumpQueriesOnException (field1 int UNIQUE)");
+			this.stmt
+					.executeUpdate("INSERT INTO testDumpQueriesOnException VALUES (1)");
+
+			PreparedStatement pStmt = dumpConn
+					.prepareStatement("INSERT INTO testDumpQueriesOnException VALUES (?)");
+			pStmt.setInt(1, 1);
+			pStmt.executeUpdate();
+		} catch (SQLException sqlEx) {
+			assertTrue(sqlEx.getMessage().indexOf(
+					"INSERT INTO testDumpQueriesOnException") != -1);
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testDumpQueriesOnException");
+		}
+
+		try {
+			dumpConn.prepareStatement(bogusSQL);
+		} catch (SQLException sqlEx) {
+			assertTrue(sqlEx.getMessage().indexOf(bogusSQL) != -1);
+		}
+	}
+
+	/**
+	 * Tests functionality of the ConnectionPropertiesTransform interface.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testConnectionPropertiesTransform() throws Exception {
+		String transformClassName = SimpleTransformer.class.getName();
+
+		Properties props = new Properties();
+
+		props.setProperty(NonRegisteringDriver.PROPERTIES_TRANSFORM_KEY,
+				transformClassName);
+
+		NonRegisteringDriver driver = new NonRegisteringDriver();
+
+		Properties transformedProps = driver
+				.parseURL(BaseTestCase.dbUrl, props);
+
+		assertTrue("albequerque".equals(transformedProps
+				.getProperty(NonRegisteringDriver.HOST_PROPERTY_KEY)));
+	}
+
+	/**
+	 * Tests functionality of using URLs in 'LOAD DATA LOCAL INFILE' statements.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testLocalInfileWithUrl() throws Exception {
+		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();
+
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testLocalInfileWithUrl");
+			this.stmt
+					.executeUpdate("CREATE TABLE testLocalInfileWithUrl (field1 LONGTEXT)");
+
+			Properties props = new Properties();
+			props.setProperty("allowUrlInLocalInfile", "true");
+
+			Connection loadConn = getConnectionWithProps(props);
+			Statement loadStmt = loadConn.createStatement();
+
+			try {
+				loadStmt.executeQuery("LOAD DATA LOCAL INFILE '" + url
+						+ "' INTO TABLE testLocalInfileWithUrl");
+			} catch (SQLException sqlEx) {
+				sqlEx.printStackTrace();
+
+				throw sqlEx;
+			}
+
+			this.rs = this.stmt
+					.executeQuery("SELECT * FROM testLocalInfileWithUrl");
+			assertTrue(this.rs.next());
+			assertTrue("Test".equals(this.rs.getString(1)));
+			int count = this.stmt
+					.executeUpdate("DELETE FROM testLocalInfileWithUrl");
+			assertTrue(count == 1);
+
+			StringBuffer escapedPath = new StringBuffer();
+			String path = infile.getCanonicalPath();
+
+			for (int i = 0; i < path.length(); i++) {
+				char c = path.charAt(i);
+
+				if (c == '\\') {
+					escapedPath.append('\\');
+				}
+
+				escapedPath.append(c);
+			}
+
+			loadStmt.executeQuery("LOAD DATA LOCAL INFILE '"
+					+ escapedPath.toString()
+					+ "' INTO TABLE testLocalInfileWithUrl");
+			this.rs = this.stmt
+					.executeQuery("SELECT * FROM testLocalInfileWithUrl");
+			assertTrue(this.rs.next());
+			assertTrue("Test".equals(this.rs.getString(1)));
+
+			try {
+				loadStmt
+						.executeQuery("LOAD DATA LOCAL INFILE 'foo:///' INTO TABLE testLocalInfileWithUrl");
+			} catch (SQLException sqlEx) {
+				assertTrue(sqlEx.getMessage() != null);
+				assertTrue(sqlEx.getMessage().indexOf("FileNotFoundException") != -1);
+			}
+
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testLocalInfileWithUrl");
+		}
+	}
+
+	public void testServerConfigurationCache() throws Exception {
+		Properties props = new Properties();
+
+		props.setProperty("cacheServerConfiguration", "true");
+		props.setProperty("profileSQL", "true");
+		props.setProperty("logFactory", "com.mysql.jdbc.log.StandardLogger");
+
+		Connection conn1 = getConnectionWithProps(props);
+
+		StandardLogger.saveLogsToBuffer();
+
+		Connection conn2 = getConnectionWithProps(props);
+
+		assertTrue("Configuration wasn't cached", StandardLogger.bufferedLog
+				.toString().indexOf("SHOW VARIABLES") == -1);
+
+		if (versionMeetsMinimum(4, 1)) {
+			assertTrue("Configuration wasn't cached",
+					StandardLogger.bufferedLog.toString().indexOf(
+							"SHOW COLLATION") == -1);
+
+		}
+	}
+
+	/**
+	 * Tests whether or not the configuration 'useLocalSessionState' actually
+	 * prevents non-needed 'set autocommit=', 'set session transaction isolation
+	 * ...' and 'show variables like tx_isolation' queries.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testUseLocalSessionState() throws Exception {
+		Properties props = new Properties();
+
+		props.setProperty("useLocalSessionState", "true");
+		props.setProperty("profileSQL", "true");
+		props.setProperty("logFactory", "com.mysql.jdbc.log.StandardLogger");
+
+		Connection conn1 = getConnectionWithProps(props);
+		conn1.setAutoCommit(true);
+		conn1.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
+
+		StandardLogger.saveLogsToBuffer();
+		StandardLogger.bufferedLog.setLength(0);
+
+		conn1.setAutoCommit(true);
+		conn1.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
+		conn1.getTransactionIsolation();
+
+		String logAsString = StandardLogger.bufferedLog.toString();
+
+		assertTrue(logAsString.indexOf("SET SESSION") == -1
+				&& logAsString.indexOf("SHOW VARIABLES LIKE 'tx_isolation'") == -1
+				&& logAsString.indexOf("SET autocommit=") == -1);
+
+	}
+
+	/**
+	 * Tests whether re-connect with non-read-only connection can happen.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testFailoverConnection() throws Exception {
+
+		if (!isServerRunningOnWindows()) { // windows sockets don't
+			                               // work for this test
+			Properties props = new Properties();
+			props.setProperty("autoReconnect", "true");
+			props.setProperty("failOverReadOnly", "false");
+	
+			// Re-build the connection information
+			int firstIndexOfHost = BaseTestCase.dbUrl.indexOf("//") + 2;
+			int lastIndexOfHost = BaseTestCase.dbUrl.indexOf("/", firstIndexOfHost);
+	
+			String hostPortPair = BaseTestCase.dbUrl.substring(firstIndexOfHost,
+					lastIndexOfHost);
+			System.out.println(hostPortPair);
+	
+			StringTokenizer st = new StringTokenizer(hostPortPair, ":");
+	
+			String host = null;
+			String port = null;
+	
+			if (st.hasMoreTokens()) {
+				String possibleHostOrPort = st.nextToken();
+	
+				if (Character.isDigit(possibleHostOrPort.charAt(0)) && 
+						(possibleHostOrPort.indexOf(".") == -1 /* IPV4 */)  &&
+						(possibleHostOrPort.indexOf("::") == -1 /* IPV6 */)) {
+					port = possibleHostOrPort;
+					host = "localhost";
+				} else {
+					host = possibleHostOrPort;
+				}
+			}
+	
+			if (host == null) {
+				host = "localhost"; 
+			}
+			
+			if (st.hasMoreTokens()) {
+				port = st.nextToken();
+			}
+	
+			StringBuffer newHostBuf = new StringBuffer();
+			newHostBuf.append(host);
+			if (port != null) {
+				newHostBuf.append(":");
+				newHostBuf.append(port);
+			}
+			newHostBuf.append(",");
+			newHostBuf.append(host);
+			if (port != null) {
+				newHostBuf.append(":");
+				newHostBuf.append(port);
+			}
+	
+			props
+					.put(NonRegisteringDriver.HOST_PROPERTY_KEY, newHostBuf
+							.toString());
+	
+			Connection failoverConnection = null;
+	
+			try {
+				failoverConnection = getConnectionWithProps(props);
+	
+				String originalConnectionId = getSingleIndexedValueWithQuery(
+						failoverConnection, 1, "SELECT connection_id()").toString();
+				System.out.println("Original Connection Id = "
+						+ originalConnectionId);
+	
+				assertTrue("Connection should not be in READ_ONLY state",
+						!failoverConnection.isReadOnly());
+	
+				// Kill the connection
+				this.stmt.executeUpdate("KILL " + originalConnectionId);
+	
+				// This takes a bit to occur
+	
+				Thread.sleep(3000);
+	
+				try {
+					failoverConnection.createStatement().executeQuery("SELECT 1");
+					fail("We expect an exception here, because the connection should be gone until the reconnect code picks it up again");
+				} catch (SQLException sqlEx) {
+					; // do-nothing
+				}
+	
+				// Tickle re-connect
+	
+				failoverConnection.setAutoCommit(true);
+	
+				String newConnectionId = getSingleIndexedValueWithQuery(
+						failoverConnection, 1, "SELECT connection_id()").toString();
+				System.out.println("new Connection Id = " + newConnectionId);
+	
+				assertTrue(
+						"We should have a new connection to the server in this case",
+						!newConnectionId.equals(originalConnectionId));
+				assertTrue("Connection should not be read-only",
+						!failoverConnection.isReadOnly());
+			} finally {
+				if (failoverConnection != null) {
+					failoverConnection.close();
+				}
+			}
+		}
+	}
+
+	public void testCannedConfigs() throws Exception {
+		String url = "jdbc:mysql:///?useConfigs=clusterBase";
+
+		Properties cannedProps = new NonRegisteringDriver().parseURL(url, null);
+
+		assertTrue("true".equals(cannedProps.getProperty("autoReconnect")));
+		assertTrue("false".equals(cannedProps.getProperty("failOverReadOnly")));
+		assertTrue("true".equals(cannedProps
+				.getProperty("roundRobinLoadBalance")));
+
+		// this will fail, but we test that too
+		url = "jdbc:mysql:///?useConfigs=clusterBase,clusterBase2";
+
+		try {
+			cannedProps = new NonRegisteringDriver().parseURL(url, null);
+			fail("should've bailed on that one!");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE
+					.equals(sqlEx.getSQLState()));
+		}
+	}
+
+	public void testUseOldUTF8Behavior() throws Exception {
+
+		Properties props = new Properties();
+		props.setProperty("useOldUTF8Behavior", "true");
+		props.setProperty("useUnicode", "true");
+		props.setProperty("characterEncoding", "UTF-8");
+		props.setProperty("logFactory", "com.mysql.jdbc.log.StandardLogger");
+		props.setProperty("profileSQL", "true");
+		StandardLogger.saveLogsToBuffer();
+		StandardLogger.bufferedLog.setLength(0);
+
+		try {
+			getConnectionWithProps(props);
+
+			assertTrue(StringUtils.indexOfIgnoreCase(StandardLogger.bufferedLog
+					.toString(), "SET NAMES utf8") == -1);
+		} finally {
+			StandardLogger.bufferedLog = null;
+		}
+	}
+
+	/**
+	 * Checks implementation of 'dontTrackOpenResources' property.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testDontTrackOpenResources() throws Exception {
+		Properties props = new Properties();
+
+		props.setProperty("dontTrackOpenResources", "true");
+		Connection noTrackConn = null;
+		Statement noTrackStatement = null;
+		PreparedStatement noTrackPstmt = null;
+		ResultSet rs2 = null;
+
+		try {
+			noTrackConn = getConnectionWithProps(props);
+			noTrackStatement = noTrackConn.createStatement();
+			noTrackPstmt = noTrackConn.prepareStatement("SELECT 1");
+			rs2 = noTrackPstmt.executeQuery();
+			rs2.next();
+
+			this.rs = noTrackStatement.executeQuery("SELECT 1");
+			this.rs.next();
+
+			noTrackConn.close();
+
+			// Under 'strict' JDBC requirements, these calls should fail
+			// (and _do_ if dontTrackOpenResources == false)
+
+			this.rs.getString(1);
+			rs2.getString(1);
+		} finally {
+			if (rs2 != null) {
+				rs2.close();
+			}
+
+			if (noTrackStatement != null) {
+				noTrackStatement.close();
+			}
+
+			if (noTrackConn != null & !noTrackConn.isClosed()) {
+				noTrackConn.close();
+			}
+		}
+	}
+
+	public void testPing() throws SQLException {
+		Connection conn2 = getConnectionWithProps(null);
+
+		((com.mysql.jdbc.Connection) conn2).ping();
+		conn2.close();
+
+		try {
+			((com.mysql.jdbc.Connection) conn2).ping();
+			fail("Should have failed with an exception");
+		} catch (SQLException sqlEx) {
+			// ignore for now
+		}
+
+		//
+		// This feature caused BUG#8975, so check for that too!
+
+		Properties props = new Properties();
+		props.setProperty("autoReconnect", "true");
+
+		getConnectionWithProps(props);
+	}
+
+	public void testSessionVariables() throws Exception {
+		String getInitialMaxAllowedPacket = getMysqlVariable("max_allowed_packet");
+
+		int newMaxAllowedPacket = Integer.parseInt(getInitialMaxAllowedPacket) + 1024;
+
+		Properties props = new Properties();
+		props.setProperty("sessionVariables", "max_allowed_packet="
+				+ newMaxAllowedPacket);
+		props.setProperty("profileSQL", "true");
+
+		Connection varConn = getConnectionWithProps(props);
+
+		assertTrue(!getInitialMaxAllowedPacket.equals(getMysqlVariable(varConn,
+				"max_allowed_packet")));
+	}
+
+	/**
+	 * Tests setting profileSql on/off in the span of one connection.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testSetProfileSql() throws Exception {
+		((com.mysql.jdbc.Connection) this.conn).setProfileSql(false);
+		stmt.executeQuery("SELECT 1");
+		((com.mysql.jdbc.Connection) this.conn).setProfileSql(true);
+		stmt.executeQuery("SELECT 1");
+	}
+
+	public void testCreateDatabaseIfNotExist() throws Exception {
+		if (isAdminConnectionConfigured()) {
+			Properties props = new Properties();
+			props.setProperty("createDatabaseIfNotExist", "true");
+			props.setProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY,
+					"testcreatedatabaseifnotexists");
+
+			Connection newConn = getAdminConnectionWithProps(props);
+			newConn.createStatement().executeUpdate(
+					"DROP DATABASE testcreatedatabaseifnotexists");
+		}
+	}
+    
+    /**
+     * Tests if gatherPerfMetrics works.
+     * 
+     * @throws Exception if the test fails
+     */
+    public void testGatherPerfMetrics() throws Exception {
+        if(versionMeetsMinimum(4, 1)) {
+            try {
+                Properties props = new Properties();
+                props.put("autoReconnect", "true");
+                props.put("relaxAutoCommit", "true");
+                props.put("logSlowQueries", "true");
+                props.put("slowQueryThresholdMillis", "2000");
+                // these properties were reported as the cause of NullPointerException
+                props.put("gatherPerfMetrics", "true"); 
+                props.put("reportMetricsIntervalMillis", "3000"); 
+                
+                Connection conn1 = getConnectionWithProps(props);
+                Statement stmt1 = conn1.createStatement();
+                ResultSet rs1 = stmt1.executeQuery("SELECT 1");
+                rs1.next();
+                conn1.close();
+            } catch (NullPointerException e) {
+                e.printStackTrace();
+                fail();
+            }
+        }
+    }
+
+    /**
+     * Tests if useCompress works.
+     * 
+     * @throws Exception if the test fails
+     */
+    public void testUseCompress() throws Exception {
+        Properties props = new Properties();
+        props.put("useCompression", "true");
+        props.put("traceProtocol", "true");
+        Connection conn1 = getConnectionWithProps(props);
+        Statement stmt1 = conn1.createStatement();
+        ResultSet rs1 = stmt1.executeQuery("SELECT VERSION()");
+        rs1.next();
+        rs1.getString(1);
+        stmt1.close();
+        conn1.close();
+    }
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/DataSourceTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/DataSourceTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/DataSourceTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,229 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import testsuite.BaseTestCase;
+
+import java.io.File;
+
+import java.sql.Connection;
+
+import java.util.Hashtable;
+
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.Name;
+import javax.naming.NameParser;
+import javax.naming.Reference;
+import javax.naming.spi.ObjectFactory;
+
+import javax.sql.DataSource;
+import javax.sql.PooledConnection;
+
+import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource;
+
+/**
+ * 
+ * @author Mark Matthews
+ * @version $Id: DataSourceTest.java 4251 2005-09-15 15:41:26Z mmatthews $
+ */
+public class DataSourceTest extends BaseTestCase {
+	// ~ Instance fields
+	// --------------------------------------------------------
+
+	private Context ctx;
+
+	private File tempDir;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new DataSourceTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public DataSourceTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(DataSourceTest.class);
+	}
+
+	/**
+	 * Sets up this test, calling registerDataSource() to bind a DataSource into
+	 * JNDI, using the FSContext JNDI provider from Sun
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		registerDataSource();
+	}
+
+	/**
+	 * Un-binds the DataSource, and cleans up the filesystem
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void tearDown() throws Exception {
+		this.ctx.unbind(this.tempDir.getAbsolutePath() + "/test");
+		this.ctx.close();
+		this.tempDir.delete();
+		super.tearDown();
+	}
+
+	/**
+	 * Tests that we can get a connection from the DataSource bound in JNDI
+	 * during test setup
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testDataSource() throws Exception {
+		NameParser nameParser = this.ctx.getNameParser("");
+		Name datasourceName = nameParser.parse(this.tempDir.getAbsolutePath()
+				+ "/test");
+		Object obj = this.ctx.lookup(datasourceName);
+		DataSource boundDs = null;
+
+		if (obj instanceof DataSource) {
+			boundDs = (DataSource) obj;
+		} else if (obj instanceof Reference) {
+			//
+			// For some reason, this comes back as a Reference
+			// instance under CruiseControl !?
+			//
+			Reference objAsRef = (Reference) obj;
+			ObjectFactory factory = (ObjectFactory) Class.forName(
+					objAsRef.getFactoryClassName()).newInstance();
+			boundDs = (DataSource) factory.getObjectInstance(objAsRef,
+					datasourceName, this.ctx, new Hashtable());
+		}
+
+		assertTrue("Datasource not bound", boundDs != null);
+
+		Connection con = boundDs.getConnection();
+		con.close();
+		assertTrue("Connection can not be obtained from data source",
+				con != null);
+	}
+
+	/**
+	 * Tests whether Connection.changeUser() (and thus pooled connections)
+	 * restore character set information correctly.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testChangeUserAndCharsets() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			MysqlConnectionPoolDataSource ds = new MysqlConnectionPoolDataSource();
+			ds.setURL(BaseTestCase.dbUrl);
+			ds.setCharacterEncoding("utf-8");
+			PooledConnection pooledConnection = ds.getPooledConnection();
+
+			Connection connToMySQL = pooledConnection.getConnection();
+			this.rs = connToMySQL.createStatement().executeQuery(
+					"SELECT @@character_set_results");
+			assertTrue(this.rs.next());
+			
+			String toCheck = null;
+			
+			if (versionMeetsMinimum(4, 1, 15)) {
+				if (versionMeetsMinimum(5, 0)) {
+					if (versionMeetsMinimum(5, 0, 13)) {
+						toCheck = null;
+					} else {
+						toCheck = "NULL";
+					}
+				} else {
+					toCheck = null;
+				}
+			} else {
+				toCheck = "NULL";
+			}
+			
+			assertEquals(toCheck, this.rs.getString(1));
+
+			this.rs = connToMySQL.createStatement().executeQuery(
+					"SHOW VARIABLES LIKE 'character_set_client'");
+			assertTrue(this.rs.next());
+			assertEquals("utf8", this.rs.getString(2));
+
+			connToMySQL.close();
+
+			connToMySQL = pooledConnection.getConnection();
+			this.rs = connToMySQL.createStatement().executeQuery(
+					"SELECT @@character_set_results");
+			assertTrue(this.rs.next());
+			assertEquals(toCheck, this.rs.getString(1));
+
+			this.rs = connToMySQL.createStatement().executeQuery(
+					"SHOW VARIABLES LIKE 'character_set_client'");
+			assertTrue(this.rs.next());
+			assertEquals("utf8", this.rs.getString(2));
+
+			pooledConnection.getConnection().close();
+		}
+	}
+
+	/**
+	 * 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.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	private void registerDataSource() throws Exception {
+		this.tempDir = File.createTempFile("jnditest", null);
+		this.tempDir.delete();
+		this.tempDir.mkdir();
+		this.tempDir.deleteOnExit();
+
+		com.mysql.jdbc.jdbc2.optional.MysqlDataSource ds;
+		Hashtable env = new Hashtable();
+		env.put(Context.INITIAL_CONTEXT_FACTORY,
+				"com.sun.jndi.fscontext.RefFSContextFactory");
+		this.ctx = new InitialContext(env);
+		assertTrue("Naming Context not created", this.ctx != null);
+		ds = new com.mysql.jdbc.jdbc2.optional.MysqlDataSource();
+		ds.setUrl(dbUrl); // from BaseTestCase
+		this.ctx.bind(this.tempDir.getAbsolutePath() + "/test", ds);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/DateTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/DateTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/DateTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,417 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import testsuite.BaseTestCase;
+
+import java.sql.Connection;
+import java.sql.Date;
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Time;
+import java.sql.Timestamp;
+
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+
+import java.util.Calendar;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import com.mysql.jdbc.SQLError;
+
+/**
+ * 
+ * @author Mark Matthews
+ * @version $Id: DateTest.java 5345 2006-06-01 20:18:04Z mmatthews $
+ */
+public class DateTest extends BaseTestCase {
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new DateTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public DateTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(DateTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		createTestTable();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testTimestamp() throws SQLException {
+		this.pstmt = this.conn
+				.prepareStatement("INSERT INTO DATETEST(tstamp, dt, dtime, tm) VALUES (?, ?, ?, ?)");
+
+		// TimeZone.setDefault(TimeZone.getTimeZone("GMT"));
+		Calendar cal = Calendar.getInstance();
+		cal.set(Calendar.MONTH, 6);
+		cal.set(Calendar.DAY_OF_MONTH, 3);
+		cal.set(Calendar.YEAR, 2002);
+		cal.set(Calendar.HOUR, 7);
+		cal.set(Calendar.MINUTE, 0);
+		cal.set(Calendar.SECOND, 0);
+		cal.set(Calendar.MILLISECOND, 0);
+		cal.set(Calendar.AM_PM, Calendar.AM);
+		cal.getTime();
+		System.out.println(cal);
+
+		// DateFormat df = SimpleDateFormat.getInstance();
+		DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss z");
+
+		Calendar calGMT = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
+		// df.setTimeZone(TimeZone.getTimeZone("GMT"));
+		Timestamp nowTstamp = new Timestamp(cal.getTime().getTime());
+		java.sql.Date nowDate = new java.sql.Date(cal.getTime().getTime());
+		Timestamp nowDatetime = new Timestamp(cal.getTime().getTime());
+		java.sql.Time nowTime = new java.sql.Time(cal.getTime().getTime());
+		System.out
+				.println("** Times with given calendar (before storing) **\n");
+		System.out.println("TIMESTAMP:\t" + nowTstamp.getTime() + " -> "
+				+ df.format(nowTstamp));
+		System.out.println("DATE:\t\t" + nowDate.getTime() + " -> "
+				+ df.format(nowDate));
+		System.out.println("DATETIME:\t" + nowDatetime.getTime() + " -> "
+				+ df.format(nowDatetime));
+		System.out.println("DATE:\t\t" + nowDate.getTime() + " -> "
+				+ df.format(nowDate));
+		System.out.println("TIME:\t\t" + nowTime.getTime() + " -> "
+				+ df.format(nowTime));
+		System.out.println("\n");
+		this.pstmt.setTimestamp(1, nowTstamp, calGMT);
+		// have to use the same TimeZone as used to create or there will be
+		// shift
+		this.pstmt.setDate(2, nowDate, cal);
+		this.pstmt.setTimestamp(3, nowDatetime, calGMT);
+		// have to use the same TimeZone as used to create or there will be
+		// shift
+		this.pstmt.setTime(4, nowTime, cal);
+		this.pstmt.execute();
+
+		this.pstmt.getUpdateCount();
+		this.pstmt.clearParameters();
+		this.rs = this.stmt.executeQuery("SELECT * from DATETEST");
+
+		java.sql.Date thenDate = null;
+
+		while (this.rs.next()) {
+			Timestamp thenTstamp = this.rs.getTimestamp(1, calGMT);
+			thenDate = this.rs.getDate(2, cal);
+
+			java.sql.Timestamp thenDatetime = this.rs.getTimestamp(3, calGMT);
+
+			java.sql.Time thenTime = this.rs.getTime(4, cal);
+			System.out
+					.println("** Times with given calendar (retrieved from database) **\n");
+			System.out.println("TIMESTAMP:\t" + thenTstamp.getTime() + " -> "
+					+ df.format(thenTstamp));
+			System.out.println("DATE:\t\t" + thenDate.getTime() + " -> "
+					+ df.format(thenDate));
+			System.out.println("DATETIME:\t" + thenDatetime.getTime() + " -> "
+					+ df.format(thenDatetime));
+			System.out.println("TIME:\t\t" + thenTime.getTime() + " -> "
+					+ df.format(thenTime));
+			System.out.println("\n");
+		}
+
+		this.rs.close();
+		this.rs = null;
+	}
+
+	public void testNanosParsing() throws SQLException {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testNanosParsing");
+			this.stmt
+					.executeUpdate("CREATE TABLE testNanosParsing (dateIndex int, field1 VARCHAR(32))");
+			this.stmt
+					.executeUpdate("INSERT INTO testNanosParsing VALUES (1, '1969-12-31 18:00:00.0'), "
+							+ "(2, '1969-12-31 18:00:00.90'), "
+							+ "(3, '1969-12-31 18:00:00.900'), "
+							+ "(4, '1969-12-31 18:00:00.9000'), "
+							+ "(5, '1969-12-31 18:00:00.90000'), "
+							+ "(6, '1969-12-31 18:00:00.900000'), "
+							+ "(7, '1969-12-31 18:00:00.')");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field1 FROM testNanosParsing ORDER BY dateIndex ASC");
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getTimestamp(1).getNanos() == 0);
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getTimestamp(1).getNanos() + " != 90", this.rs
+					.getTimestamp(1).getNanos() == 90);
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getTimestamp(1).getNanos() + " != 900", this.rs
+					.getTimestamp(1).getNanos() == 900);
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getTimestamp(1).getNanos() + " != 9000", this.rs
+					.getTimestamp(1).getNanos() == 9000);
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getTimestamp(1).getNanos() + " != 90000",
+					this.rs.getTimestamp(1).getNanos() == 90000);
+			assertTrue(this.rs.next());
+			assertTrue(this.rs.getTimestamp(1).getNanos() + " != 900000",
+					this.rs.getTimestamp(1).getNanos() == 900000);
+			assertTrue(this.rs.next());
+
+			try {
+				this.rs.getTimestamp(1);
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+						.getSQLState()));
+			}
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testNanosParsing");
+		}
+	}
+
+	private void createTestTable() throws SQLException {
+		//
+		// Catch the error, the table might exist
+		//
+		try {
+			this.stmt.executeUpdate("DROP TABLE DATETEST");
+		} catch (SQLException SQLE) {
+			;
+		}
+
+		this.stmt
+				.executeUpdate("CREATE TABLE DATETEST (tstamp TIMESTAMP, dt DATE, dtime DATETIME, tm TIME)");
+	}
+
+	/**
+	 * Tests the configurability of all-zero date/datetime/timestamp handling in
+	 * the driver.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testZeroDateBehavior() throws Exception {
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testZeroDateBehavior");
+			this.stmt
+					.executeUpdate("CREATE TABLE testZeroDateBehavior(fieldAsString VARCHAR(32), fieldAsDateTime DATETIME)");
+			this.stmt
+					.executeUpdate("INSERT INTO testZeroDateBehavior VALUES ('0000-00-00 00:00:00', '0000-00-00 00:00:00')");
+			Properties props = new Properties();
+			props.setProperty("zeroDateTimeBehavior", "round");
+			Connection roundConn = getConnectionWithProps(props);
+			Statement roundStmt = roundConn.createStatement();
+			this.rs = roundStmt
+					.executeQuery("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior");
+			this.rs.next();
+
+			assertEquals("0001-01-01", this.rs.getDate(1).toString());
+			assertEquals("0001-01-01 00:00:00.0", 
+					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0", Locale.US).format(this.rs.getTimestamp(1)));
+			assertEquals("0001-01-01", this.rs.getDate(2).toString());
+			assertEquals("0001-01-01 00:00:00.0", 
+					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0", Locale.US).format(this.rs.getTimestamp(2)));
+
+			PreparedStatement roundPrepStmt = roundConn
+					.prepareStatement("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior");
+			this.rs = roundPrepStmt.executeQuery();
+			this.rs.next();
+
+			assertEquals("0001-01-01", this.rs.getDate(1).toString());
+			assertEquals("0001-01-01 00:00:00.0", 
+					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0", Locale.US).format(this.rs.getTimestamp(1)));
+			assertEquals("0001-01-01", this.rs.getDate(2).toString());
+			assertEquals("0001-01-01 00:00:00.0", 
+					new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.0", Locale.US).format(this.rs.getTimestamp(2)));
+
+			props = new Properties();
+			props.setProperty("zeroDateTimeBehavior", "convertToNull");
+			Connection nullConn = getConnectionWithProps(props);
+			Statement nullStmt = nullConn.createStatement();
+			this.rs = nullStmt
+					.executeQuery("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior");
+
+			this.rs.next();
+
+			assertTrue(null == this.rs.getDate(1));
+			assertTrue(null == this.rs.getTimestamp(1));
+			assertTrue(null == this.rs.getDate(2));
+			assertTrue(null == this.rs.getTimestamp(2));
+
+			PreparedStatement nullPrepStmt = nullConn
+					.prepareStatement("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior");
+			this.rs = nullPrepStmt.executeQuery();
+
+			this.rs.next();
+
+			assertTrue(null == this.rs.getDate(1));
+			assertTrue(null == this.rs.getTimestamp(1));
+			assertTrue(null == this.rs.getDate(2));
+			assertTrue(null == this.rs.getTimestamp(2));
+			assertTrue(null == this.rs.getString(2));
+
+			props = new Properties();
+			props.setProperty("zeroDateTimeBehavior", "exception");
+			Connection exceptionConn = getConnectionWithProps(props);
+			Statement exceptionStmt = exceptionConn.createStatement();
+			this.rs = exceptionStmt
+					.executeQuery("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior");
+
+			this.rs.next();
+
+			try {
+				this.rs.getDate(1);
+				fail("Exception should have been thrown when trying to retrieve invalid date");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.getTimestamp(1);
+				fail("Exception should have been thrown when trying to retrieve invalid date");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.getDate(2);
+				fail("Exception should have been thrown when trying to retrieve invalid date");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+						.getSQLState()));
+			}
+
+			try {
+				this.rs.getTimestamp(2);
+				fail("Exception should have been thrown when trying to retrieve invalid date");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+						.getSQLState()));
+			}
+
+			PreparedStatement exceptionPrepStmt = exceptionConn
+					.prepareStatement("SELECT fieldAsString, fieldAsDateTime FROM testZeroDateBehavior");
+
+			try {
+				this.rs = exceptionPrepStmt.executeQuery();
+				this.rs.next();
+				this.rs.getDate(2);
+				fail("Exception should have been thrown when trying to retrieve invalid date");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_ILLEGAL_ARGUMENT.equals(sqlEx
+						.getSQLState()));
+			}
+
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testZeroDateBehavior");
+		}
+	}
+
+	public void testReggieBug() throws Exception {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testReggieBug");
+			this.stmt.executeUpdate("CREATE TABLE testReggieBug (field1 DATE)");
+
+			PreparedStatement pStmt = this.conn
+					.prepareStatement("INSERT INTO testReggieBug VALUES (?)");
+			pStmt.setDate(1, new Date(2004 - 1900, 07, 28));
+			pStmt.executeUpdate();
+			this.rs = this.stmt.executeQuery("SELECT * FROM testReggieBug");
+			this.rs.next();
+			System.out.println(this.rs.getDate(1));
+			this.rs = this.conn.prepareStatement("SELECT * FROM testReggieBug")
+					.executeQuery();
+			this.rs.next();
+			System.out.println(this.rs.getDate(1));
+
+		} finally {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS testReggieBug");
+		}
+	}
+	
+	public void testNativeConversions() throws Exception {
+		Timestamp ts = new Timestamp(System.currentTimeMillis());
+		Date dt = new Date(ts.getTime());
+		Time tm = new Time(ts.getTime());
+		
+		createTable("testNativeConversions", "(time_field TIME, date_field DATE, datetime_field DATETIME, timestamp_field TIMESTAMP)");
+		this.pstmt = this.conn.prepareStatement("INSERT INTO testNativeConversions VALUES (?,?,?,?)");
+		this.pstmt.setTime(1, tm);
+		this.pstmt.setDate(2, dt);
+		this.pstmt.setTimestamp(3, ts);
+		this.pstmt.setTimestamp(4, ts);
+		this.pstmt.execute();
+		this.pstmt.close();
+		
+		this.pstmt = this.conn.prepareStatement("SELECT time_field, date_field, datetime_field, timestamp_field FROM testNativeConversions");
+		this.rs = this.pstmt.executeQuery();
+		assertTrue(this.rs.next());
+		System.out.println(this.rs.getTime(1));
+		System.out.println(this.rs.getTime(2));
+		System.out.println(this.rs.getTime(3));
+		System.out.println(this.rs.getTime(4));
+		System.out.println();
+		System.out.println(this.rs.getDate(1));
+		System.out.println(this.rs.getDate(2));
+		System.out.println(this.rs.getDate(3));
+		System.out.println(this.rs.getDate(4));
+		System.out.println();
+		System.out.println(this.rs.getTimestamp(1));
+		System.out.println(this.rs.getTimestamp(2));
+		System.out.println(this.rs.getTimestamp(3));
+		System.out.println(this.rs.getTimestamp(4));
+	}
+		
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/EscapeProcessingTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/EscapeProcessingTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/EscapeProcessingTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,136 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import java.sql.Connection;
+import java.util.Properties;
+import java.util.TimeZone;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests escape processing
+ * 
+ * @author Mark Matthews
+ */
+public class EscapeProcessingTest extends BaseTestCase {
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Constructor for EscapeProcessingTest.
+	 * 
+	 * @param name
+	 *            the test to run
+	 */
+	public EscapeProcessingTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Tests the escape processing functionality
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testEscapeProcessing() throws Exception {
+		String results = "select dayname (abs(now())),   -- Today    \n"
+				+ "           '1997-05-24',  -- a date                    \n"
+				+ "           '10:30:29',  -- a time                     \n"
+				+ "           '1997-05-24 10:30:29', -- a timestamp  \n"
+				+ "          '{string data with { or } will not be altered'   \n"
+				+ "--  Also note that you can safely include { and } in comments";
+
+		String exSql = "select {fn dayname ({fn abs({fn now()})})},   -- Today    \n"
+				+ "           {d '1997-05-24'},  -- a date                    \n"
+				+ "           {t '10:30:29' },  -- a time                     \n"
+				+ "           {ts '1997-05-24 10:30:29.123'}, -- a timestamp  \n"
+				+ "          '{string data with { or } will not be altered'   \n"
+				+ "--  Also note that you can safely include { and } in comments";
+
+		String escapedSql = this.conn.nativeSQL(exSql);
+
+		assertTrue(results.equals(escapedSql));
+
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(EscapeProcessingTest.class);
+	}
+
+	/**
+	 * JDBC-4.0 spec will allow either SQL_ or not for type in {fn convert ...}
+	 * 
+	 * @throws Exception
+	 *             if the test fails
+	 */
+	public void testConvertEscape() throws Exception {
+		assertEquals(conn.nativeSQL("{fn convert(abcd, SQL_INTEGER)}"), conn
+				.nativeSQL("{fn convert(abcd, INTEGER)}"));
+	}
+	
+	/**
+	 * Tests that the escape tokenizer converts timestamp values
+	 * wrt. timezones when useTimezone=true.
+	 * 
+	 * @throws Exception if the test fails.
+	 */
+	public void testTimestampConversion() throws Exception {
+		TimeZone currentTimezone = TimeZone.getDefault();
+		String[] availableIds = TimeZone.getAvailableIDs(currentTimezone.getRawOffset() + (3600 * 1000 * 2));
+		String newTimezone = null;
+		
+		if (availableIds.length > 0) {
+			newTimezone = availableIds[0];
+		} else {
+			newTimezone = "UTC"; // punt
+		}
+		
+		Properties props = new Properties();
+		
+		props.setProperty("useTimezone", "true");
+		props.setProperty("serverTimezone", newTimezone);
+		Connection tzConn = null;
+		
+		try {
+			String escapeToken = "SELECT {ts '2002-11-12 10:00:00'} {t '05:11:02'}";
+			tzConn = getConnectionWithProps(props);
+			assertTrue(!tzConn.nativeSQL(escapeToken).equals(this.conn.nativeSQL(escapeToken)));
+		} finally {
+			if (tzConn != null) {
+				tzConn.close();
+			}
+		}
+		
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/MetadataTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/MetadataTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/MetadataTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,818 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Properties;
+import java.util.Set;
+
+import com.mysql.jdbc.StringUtils;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests DatabaseMetaData methods.
+ * 
+ * @author Mark Matthews
+ * @version $Id: MetadataTest.java 5828 2006-10-05 16:51:31Z mmatthews $
+ */
+public class MetadataTest extends BaseTestCase {
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new MetadataTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public MetadataTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(MetadataTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		createTestTable();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testForeignKeys() throws SQLException {
+		try {
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+			this.rs = dbmd.getImportedKeys(null, null, "child");
+
+			while (this.rs.next()) {
+				String pkColumnName = this.rs.getString("PKCOLUMN_NAME");
+				String fkColumnName = this.rs.getString("FKCOLUMN_NAME");
+				assertTrue("Primary Key not returned correctly ('"
+						+ pkColumnName + "' != 'parent_id')", pkColumnName
+						.equalsIgnoreCase("parent_id"));
+				assertTrue("Foreign Key not returned correctly ('"
+						+ fkColumnName + "' != 'parent_id_fk')", fkColumnName
+						.equalsIgnoreCase("parent_id_fk"));
+			}
+
+			this.rs.close();
+			this.rs = dbmd.getExportedKeys(null, null, "parent");
+
+			while (this.rs.next()) {
+				String pkColumnName = this.rs.getString("PKCOLUMN_NAME");
+				String fkColumnName = this.rs.getString("FKCOLUMN_NAME");
+				String fkTableName = this.rs.getString("FKTABLE_NAME");
+				assertTrue("Primary Key not returned correctly ('"
+						+ pkColumnName + "' != 'parent_id')", pkColumnName
+						.equalsIgnoreCase("parent_id"));
+				assertTrue(
+						"Foreign Key table not returned correctly for getExportedKeys ('"
+								+ fkTableName + "' != 'child')", fkTableName
+								.equalsIgnoreCase("child"));
+				assertTrue(
+						"Foreign Key not returned correctly for getExportedKeys ('"
+								+ fkColumnName + "' != 'parent_id_fk')",
+						fkColumnName.equalsIgnoreCase("parent_id_fk"));
+			}
+
+			this.rs.close();
+
+			this.rs = dbmd.getCrossReference(null, null, "cpd_foreign_3", null,
+					null, "cpd_foreign_4");
+
+			assertTrue(this.rs.next());
+
+			String pkColumnName = this.rs.getString("PKCOLUMN_NAME");
+			String pkTableName = this.rs.getString("PKTABLE_NAME");
+			String fkColumnName = this.rs.getString("FKCOLUMN_NAME");
+			String fkTableName = this.rs.getString("FKTABLE_NAME");
+			String deleteAction = cascadeOptionToString(this.rs
+					.getInt("DELETE_RULE"));
+			String updateAction = cascadeOptionToString(this.rs
+					.getInt("UPDATE_RULE"));
+
+			assertEquals(pkColumnName, "cpd_foreign_1_id");
+			assertEquals(pkTableName, "cpd_foreign_3");
+			assertEquals(fkColumnName, "cpd_foreign_1_id");
+			assertEquals(fkTableName, "cpd_foreign_4");
+			assertEquals(deleteAction, "NO ACTION");
+			assertEquals(updateAction, "CASCADE");
+
+			this.rs.close();
+			this.rs = null;
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+		}
+
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testGetPrimaryKeys() throws SQLException {
+		try {
+			DatabaseMetaData dbmd = this.conn.getMetaData();
+			this.rs = dbmd.getPrimaryKeys(this.conn.getCatalog(), "",
+					"multikey");
+
+			short[] keySeqs = new short[4];
+			String[] columnNames = new String[4];
+			int i = 0;
+
+			while (this.rs.next()) {
+				this.rs.getString("TABLE_NAME");
+				columnNames[i] = this.rs.getString("COLUMN_NAME");
+
+				this.rs.getString("PK_NAME");
+				keySeqs[i] = this.rs.getShort("KEY_SEQ");
+				i++;
+			}
+
+			if ((keySeqs[0] != 3) && (keySeqs[1] != 2) && (keySeqs[2] != 4)
+					&& (keySeqs[4] != 1)) {
+				fail("Keys returned in wrong order");
+			}
+		} finally {
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (SQLException sqlEx) {
+					/* ignore */
+				}
+			}
+		}
+	}
+
+	private static String cascadeOptionToString(int option) {
+		switch (option) {
+		case DatabaseMetaData.importedKeyCascade:
+			return "CASCADE";
+
+		case DatabaseMetaData.importedKeySetNull:
+			return "SET NULL";
+
+		case DatabaseMetaData.importedKeyRestrict:
+			return "RESTRICT";
+
+		case DatabaseMetaData.importedKeyNoAction:
+			return "NO ACTION";
+		}
+
+		return "SET DEFAULT";
+	}
+
+	private void createTestTable() throws SQLException {
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS parent");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS multikey");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_4");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_3");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_2");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS cpd_foreign_1");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS fktable2");
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS fktable1");
+
+		this.stmt
+				.executeUpdate("CREATE TABLE parent(parent_id INT NOT NULL, PRIMARY KEY (parent_id)) TYPE=INNODB");
+		this.stmt
+				.executeUpdate("CREATE TABLE child(child_id INT, parent_id_fk INT, INDEX par_ind (parent_id_fk), "
+						+ "FOREIGN KEY (parent_id_fk) REFERENCES parent(parent_id)) TYPE=INNODB");
+		this.stmt
+				.executeUpdate("CREATE TABLE multikey(d INT NOT NULL, b INT NOT NULL, a INT NOT NULL, c INT NOT NULL, PRIMARY KEY (d, b, a, c))");
+
+		// Test compound foreign keys
+		this.stmt.executeUpdate("create table cpd_foreign_1("
+				+ "id int(8) not null auto_increment primary key,"
+				+ "name varchar(255) not null unique," + "key (id)"
+				+ ") type=InnoDB");
+		this.stmt.executeUpdate("create table cpd_foreign_2("
+				+ "id int(8) not null auto_increment primary key,"
+				+ "key (id)," + "name varchar(255)" + ") type=InnoDB");
+		this.stmt
+				.executeUpdate("create table cpd_foreign_3("
+						+ "cpd_foreign_1_id int(8) not null,"
+						+ "cpd_foreign_2_id int(8) not null,"
+						+ "key(cpd_foreign_1_id),"
+						+ "key(cpd_foreign_2_id),"
+						+ "primary key (cpd_foreign_1_id, cpd_foreign_2_id),"
+						+ "foreign key (cpd_foreign_1_id) references cpd_foreign_1(id),"
+						+ "foreign key (cpd_foreign_2_id) references cpd_foreign_2(id)"
+						+ ") type=InnoDB");
+		this.stmt
+				.executeUpdate("create table cpd_foreign_4("
+						+ "cpd_foreign_1_id int(8) not null,"
+						+ "cpd_foreign_2_id int(8) not null,"
+						+ "key(cpd_foreign_1_id),"
+						+ "key(cpd_foreign_2_id),"
+						+ "primary key (cpd_foreign_1_id, cpd_foreign_2_id),"
+						+ "foreign key (cpd_foreign_1_id, cpd_foreign_2_id) "
+						+ "references cpd_foreign_3(cpd_foreign_1_id, cpd_foreign_2_id) "
+						+ "ON DELETE RESTRICT ON UPDATE CASCADE"
+						+ ") type=InnoDB");
+
+		this.stmt
+				.executeUpdate("create table fktable1 (TYPE_ID int not null, TYPE_DESC varchar(32), primary key(TYPE_ID)) TYPE=InnoDB");
+		this.stmt
+				.executeUpdate("create table fktable2 (KEY_ID int not null, COF_NAME varchar(32), PRICE float, TYPE_ID int, primary key(KEY_ID), "
+						+ "index(TYPE_ID), foreign key(TYPE_ID) references fktable1(TYPE_ID)) TYPE=InnoDB");
+	}
+
+	/**
+	 * Tests the implementation of metadata for views.
+	 * 
+	 * This test automatically detects whether or not the server it is running
+	 * against supports the creation of views.
+	 * 
+	 * @throws SQLException
+	 *             if the test fails.
+	 */
+	public void testViewMetaData() throws SQLException {
+		try {
+			this.rs = this.conn.getMetaData().getTableTypes();
+
+			while (this.rs.next()) {
+				if ("VIEW".equalsIgnoreCase(this.rs.getString(1))) {
+
+					this.stmt
+							.executeUpdate("DROP VIEW IF EXISTS vTestViewMetaData");
+					this.stmt
+							.executeUpdate("DROP TABLE IF EXISTS testViewMetaData");
+					this.stmt
+							.executeUpdate("CREATE TABLE testViewMetaData (field1 INT)");
+					this.stmt
+							.executeUpdate("CREATE VIEW vTestViewMetaData AS SELECT field1 FROM testViewMetaData");
+
+					ResultSet tablesRs = null;
+
+					try {
+						tablesRs = this.conn.getMetaData().getTables(
+								this.conn.getCatalog(), null, "%ViewMetaData",
+								new String[] { "TABLE", "VIEW" });
+						assertTrue(tablesRs.next());
+						assertTrue("testViewMetaData".equalsIgnoreCase(tablesRs
+								.getString(3)));
+						assertTrue(tablesRs.next());
+						assertTrue("vTestViewMetaData"
+								.equalsIgnoreCase(tablesRs.getString(3)));
+
+					} finally {
+						if (tablesRs != null) {
+							tablesRs.close();
+						}
+					}
+
+					try {
+						tablesRs = this.conn.getMetaData().getTables(
+								this.conn.getCatalog(), null, "%ViewMetaData",
+								new String[] { "TABLE" });
+						assertTrue(tablesRs.next());
+						assertTrue("testViewMetaData".equalsIgnoreCase(tablesRs
+								.getString(3)));
+						assertTrue(!tablesRs.next());
+					} finally {
+						if (tablesRs != null) {
+							tablesRs.close();
+						}
+					}
+					break;
+				}
+			}
+
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests detection of read-only fields when the server is 4.1.0 or newer.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testRSMDIsReadOnly() throws Exception {
+		try {
+			this.rs = this.stmt.executeQuery("SELECT 1");
+
+			ResultSetMetaData rsmd = this.rs.getMetaData();
+
+			if (versionMeetsMinimum(4, 1)) {
+				assertTrue(rsmd.isReadOnly(1));
+
+				try {
+					this.stmt
+							.executeUpdate("DROP TABLE IF EXISTS testRSMDIsReadOnly");
+					this.stmt
+							.executeUpdate("CREATE TABLE testRSMDIsReadOnly (field1 INT)");
+					this.stmt
+							.executeUpdate("INSERT INTO testRSMDIsReadOnly VALUES (1)");
+
+					this.rs = this.stmt
+							.executeQuery("SELECT 1, field1 + 1, field1 FROM testRSMDIsReadOnly");
+					rsmd = this.rs.getMetaData();
+
+					assertTrue(rsmd.isReadOnly(1));
+					assertTrue(rsmd.isReadOnly(2));
+					assertTrue(!rsmd.isReadOnly(3));
+				} finally {
+					this.stmt
+							.executeUpdate("DROP TABLE IF EXISTS testRSMDIsReadOnly");
+				}
+			} else {
+				assertTrue(rsmd.isReadOnly(1) == false);
+			}
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+			}
+		}
+	}
+
+	public void testBitType() throws Exception {
+		if (versionMeetsMinimum(5, 0, 3)) {
+			try {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBitType");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBitType (field1 BIT, field2 BIT, field3 BIT)");
+				this.stmt
+						.executeUpdate("INSERT INTO testBitType VALUES (1, 0, NULL)");
+				this.rs = this.stmt
+						.executeQuery("SELECT field1, field2, field3 FROM testBitType");
+				this.rs.next();
+
+				assertTrue(((Boolean) this.rs.getObject(1)).booleanValue());
+				assertTrue(!((Boolean) this.rs.getObject(2)).booleanValue());
+				assertEquals(this.rs.getObject(3), null);
+
+				System.out.println(this.rs.getObject(1) + ", "
+						+ this.rs.getObject(2) + ", " + this.rs.getObject(3));
+
+				this.rs = this.conn.prepareStatement(
+						"SELECT field1, field2, field3 FROM testBitType")
+						.executeQuery();
+				this.rs.next();
+
+				assertTrue(((Boolean) this.rs.getObject(1)).booleanValue());
+				assertTrue(!((Boolean) this.rs.getObject(2)).booleanValue());
+
+				assertEquals(this.rs.getObject(3), null);
+				byte[] asBytesTrue = this.rs.getBytes(1);
+				byte[] asBytesFalse = this.rs.getBytes(2);
+				byte[] asBytesNull = this.rs.getBytes(3);
+
+				assertEquals(asBytesTrue[0], 1);
+				assertEquals(asBytesFalse[0], 0);
+				assertEquals(asBytesNull, null);
+
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBitField");
+				this.stmt
+						.executeUpdate("CREATE TABLE testBitField(field1 BIT(9))");
+				this.rs = this.stmt
+						.executeQuery("SELECT field1 FROM testBitField");
+				System.out.println(this.rs.getMetaData().getColumnClassName(1));
+			} finally {
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS testBitType");
+			}
+		}
+	}
+
+	public void testSupportsSelectForUpdate() throws Exception {
+		boolean supportsForUpdate = this.conn.getMetaData()
+				.supportsSelectForUpdate();
+
+		if (this.versionMeetsMinimum(4, 0)) {
+			assertTrue(supportsForUpdate);
+		} else {
+			assertTrue(!supportsForUpdate);
+		}
+	}
+
+	public void testTinyint1IsBit() throws Exception {
+		String tableName = "testTinyint1IsBit";
+		// Can't use 'BIT' or boolean
+		createTable(tableName, "(field1 TINYINT(1))");
+		this.stmt.executeUpdate("INSERT INTO " + tableName + " VALUES (1)");
+
+		Properties props = new Properties();
+		props.setProperty("tinyint1IsBit", "true");
+		props.setProperty("transformedBitIsBoolean", "true");
+		Connection boolConn = getConnectionWithProps(props);
+
+		this.rs = boolConn.createStatement().executeQuery(
+				"SELECT field1 FROM " + tableName);
+		checkBitOrBooleanType(false);
+
+		this.rs = boolConn.prepareStatement("SELECT field1 FROM " + tableName)
+				.executeQuery();
+		checkBitOrBooleanType(false);
+
+		this.rs = boolConn.getMetaData().getColumns(boolConn.getCatalog(),
+				null, tableName, "field1");
+		assertTrue(this.rs.next());
+
+		if (versionMeetsMinimum(4, 1)) {
+			assertEquals(Types.BOOLEAN, this.rs.getInt("DATA_TYPE"));
+		} else {
+			assertEquals(Types.BIT, this.rs.getInt("DATA_TYPE"));
+		}
+
+		if (versionMeetsMinimum(4, 1)) {
+			assertEquals("BOOLEAN", this.rs.getString("TYPE_NAME"));
+		} else {
+			assertEquals("BIT", this.rs.getString("TYPE_NAME"));
+		}
+
+		props.clear();
+		props.setProperty("transformedBitIsBoolean", "false");
+		props.setProperty("tinyint1IsBit", "true");
+
+		Connection bitConn = getConnectionWithProps(props);
+
+		this.rs = bitConn.createStatement().executeQuery(
+				"SELECT field1 FROM " + tableName);
+		checkBitOrBooleanType(true);
+
+		this.rs = bitConn.prepareStatement("SELECT field1 FROM " + tableName)
+				.executeQuery();
+		checkBitOrBooleanType(true);
+
+		this.rs = bitConn.getMetaData().getColumns(boolConn.getCatalog(), null,
+				tableName, "field1");
+		assertTrue(this.rs.next());
+
+		assertEquals(Types.BIT, this.rs.getInt("DATA_TYPE"));
+
+		assertEquals("BIT", this.rs.getString("TYPE_NAME"));
+	}
+
+	private void checkBitOrBooleanType(boolean usingBit) throws SQLException {
+
+		assertTrue(this.rs.next());
+		assertEquals("java.lang.Boolean", this.rs.getObject(1).getClass()
+				.getName());
+		if (!usingBit) {
+			if (versionMeetsMinimum(4, 1)) {
+				assertEquals(Types.BOOLEAN, this.rs.getMetaData()
+						.getColumnType(1));
+			} else {
+				assertEquals(Types.BIT, this.rs.getMetaData().getColumnType(1));
+			}
+		} else {
+			assertEquals(Types.BIT, this.rs.getMetaData().getColumnType(1));
+		}
+
+		assertEquals("java.lang.Boolean", this.rs.getMetaData()
+				.getColumnClassName(1));
+	}
+    
+    /**
+     * Tests the implementation of Information Schema for primary keys.
+     */
+    public void testGetPrimaryKeysUsingInfoShcema() throws Exception {
+        if (versionMeetsMinimum(5, 0, 7)) {
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS t1");
+            this.stmt.executeUpdate("CREATE TABLE t1 (c1 int(1) primary key)");
+            Properties props = new Properties();
+            props.put("useInformationSchema", "true");
+            Connection conn1 = null;
+            try {
+                conn1 = getConnectionWithProps(props);
+                DatabaseMetaData metaData = conn1.getMetaData();
+                this.rs = metaData.getPrimaryKeys(null, null, "t1");
+                this.rs.next();
+                assertEquals("t1", this.rs.getString("TABLE_NAME"));
+                assertEquals("c1", this.rs.getString("COLUMN_NAME"));
+            } finally {
+                conn1.close();
+            }
+        }
+    }
+    
+    /**
+     * Tests the implementation of Information Schema for index info.
+     */
+    public void testGetIndexInfoUsingInfoSchema() throws Exception {
+        if (versionMeetsMinimum(5, 0, 7)) {
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS t1");
+            this.stmt.executeUpdate("CREATE TABLE t1 (c1 int(1))");
+            this.stmt.executeUpdate("CREATE INDEX index1 ON t1 (c1)");
+            Properties props = new Properties();
+            props.put("useInformationSchema", "true");
+            Connection conn1 = null;
+            try {
+                conn1 = getConnectionWithProps(props);
+                DatabaseMetaData metaData = conn1.getMetaData();
+                this.rs = metaData.getIndexInfo("test", null, "t1", false, true);
+                this.rs.next();
+                assertEquals("t1", this.rs.getString("TABLE_NAME"));
+                assertEquals("c1", this.rs.getString("COLUMN_NAME"));
+                assertEquals("1", this.rs.getString("NON_UNIQUE"));
+                assertEquals("index1", this.rs.getString("INDEX_NAME"));
+            } finally {
+                conn1.close();
+            }
+        }
+    }
+    
+    /**
+     * Tests the implementation of Information Schema for columns.
+     */
+    public void testGetColumnsUsingInfoSchema() throws Exception {
+        if (versionMeetsMinimum(5, 0, 7)) {
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS t1");
+            this.stmt.executeUpdate("CREATE TABLE t1 (c1 char(1))");
+            Properties props = new Properties();
+            props.put("useInformationSchema", "true");
+            Connection conn1 = null;
+            try {
+            conn1 = getConnectionWithProps(props);
+                DatabaseMetaData metaData = conn1.getMetaData();
+                this.rs = metaData.getColumns(null, null, "t1", null);
+                this.rs.next();
+                assertEquals("t1", this.rs.getString("TABLE_NAME"));
+                assertEquals("c1", this.rs.getString("COLUMN_NAME"));
+                assertEquals("char", this.rs.getString("TYPE_NAME"));
+                assertEquals("1", this.rs.getString("COLUMN_SIZE"));
+            } finally {
+                conn1.close();
+            }
+        }
+    }
+    
+    /**
+     * Tests the implementation of Information Schema for tables.
+     */
+    public void testGetTablesUsingInfoSchema() throws Exception {
+        if (versionMeetsMinimum(5, 0, 7)) {
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS `t1-1`");
+            this.stmt.executeUpdate("CREATE TABLE `t1-1` (c1 char(1))");
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS `t1-2`");
+            this.stmt.executeUpdate("CREATE TABLE `t1-2` (c1 char(1))");
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS `t2`");
+            this.stmt.executeUpdate("CREATE TABLE `t2` (c1 char(1))");
+            Set tableNames = new HashSet();
+            tableNames.add("t1-1");
+            tableNames.add("t1-2");
+            Properties props = new Properties();
+            props.put("useInformationSchema", "true");
+            Connection conn1 = null;
+            try {
+                conn1 = getConnectionWithProps(props);
+                DatabaseMetaData metaData = conn1.getMetaData();
+                // pattern matching for table name
+                this.rs = metaData.getTables(null, null, "t1-_", null);
+                while (this.rs.next()) {
+                    assertTrue(tableNames.remove(this.rs.getString("TABLE_NAME")));
+                }
+                assertTrue(tableNames.isEmpty());
+            } finally {
+                conn1.close();
+            }
+        }
+    }
+    
+    /**
+     * Tests the implementation of Information Schema for column privileges.
+     */
+    public void testGetColumnPrivilegesUsingInfoSchema() throws Exception {
+    	String dontRunPropertyName = "com.mysql.jdbc.testsuite.cantGrant";
+    	
+    	if (!runTestIfSysPropDefined(dontRunPropertyName)) {
+	        if (versionMeetsMinimum(5, 0, 7)) {
+	            Properties props = new Properties();
+	            
+	            props.put("useInformationSchema", "true");
+	            Connection conn1 = null;
+	            Statement stmt1 = null;
+	            String userHostQuoted = null;
+	            
+	            boolean grantFailed = true;
+	            
+	            try {
+	                conn1 = getConnectionWithProps(props);
+	                stmt1 = conn1.createStatement();
+	                stmt1.executeUpdate("DROP TABLE IF EXISTS t1");
+	                stmt1.executeUpdate("CREATE TABLE t1 (c1 int)");
+	                this.rs = stmt1.executeQuery("SELECT USER()");
+	                this.rs.next();
+	                String user = this.rs.getString(1);
+	                List userHost = StringUtils.split(user, "@", false);
+	                userHostQuoted = "'" + userHost.get(0) + "'@'" + userHost.get(1) + "'";
+	                
+	                try {
+	                	stmt1.executeUpdate("GRANT update (c1) on t1 to " + userHostQuoted);
+	                	
+	                	grantFailed = false;
+	                	
+	                } catch (SQLException sqlEx) {
+	                	logDebug("This testcase needs to be run with a URL that allows the user to issue GRANTs "
+	                			+ " in the current database. You can skip this test by setting the system property \""
+	                			+ dontRunPropertyName + "\".");
+	                	
+	                	grantFailed = true;
+	                }
+	                
+	                if (!grantFailed) {
+		                DatabaseMetaData metaData = conn1.getMetaData();
+		                this.rs = metaData.getColumnPrivileges(null, null, "t1", null);
+		                this.rs.next();
+		                assertEquals("t1", this.rs.getString("TABLE_NAME"));
+		                assertEquals("c1", this.rs.getString("COLUMN_NAME"));
+		                assertEquals(userHostQuoted, this.rs.getString("GRANTEE"));
+		                assertEquals("UPDATE", this.rs.getString("PRIVILEGE"));
+	                }
+	            } finally {
+		            if (stmt1 != null) {
+		       
+		            	stmt1.executeUpdate("DROP TABLE IF EXISTS t1");
+		            	
+		            	if (!grantFailed) {
+		            		stmt1.executeUpdate("REVOKE UPDATE (c1) ON t1 FROM " + userHostQuoted);
+		            	}
+		            	
+		            	stmt1.close();
+	            	}
+	            	
+	            	if (conn1 != null) {
+	            		conn1.close();
+	            	}
+	            }
+	        }
+    	}
+    }
+    
+    /**
+     * Tests the implementation of Information Schema for description
+     * of stored procedures available in a catalog.
+     */
+    public void testGetProceduresUsingInfoSchema() throws Exception {
+        if (versionMeetsMinimum(5, 0, 7)) {
+            this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS sp1");
+            this.stmt.executeUpdate("CREATE PROCEDURE sp1()\n BEGIN\n" + "SELECT 1;" + "end\n");
+            Properties props = new Properties();
+            props.put("useInformationSchema", "true");
+            Connection conn1 = null;
+            try {
+                conn1 = getConnectionWithProps(props);
+                DatabaseMetaData metaData = conn1.getMetaData();
+                this.rs = metaData.getProcedures(null, null, "sp1");
+                this.rs.next();
+                assertEquals("sp1", this.rs.getString("PROCEDURE_NAME"));
+                assertEquals("1", this.rs.getString("PROCEDURE_TYPE"));
+            } finally {
+                conn1.close();
+                this.stmt.executeUpdate("DROP PROCEDURE sp1");
+            }
+        }
+    }
+    
+    /**
+     * Tests the implementation of Information Schema for foreign key.
+     */
+    public void testGetCrossReferenceUsingInfoSchema() throws Exception {
+        if (versionMeetsMinimum(5, 0, 7)) {
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
+            this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
+            this.stmt.executeUpdate("CREATE TABLE parent(id INT NOT NULL, "
+                + "PRIMARY KEY (id)) ENGINE=INNODB");
+            this.stmt.executeUpdate("CREATE TABLE child(id INT, parent_id INT, "
+                + "FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET NULL) ENGINE=INNODB");
+            Properties props = new Properties();
+            props.put("useInformationSchema", "true");
+            Connection conn1 = null;
+            try {
+                conn1 = getConnectionWithProps(props);
+                DatabaseMetaData metaData = conn1.getMetaData();
+                this.rs = metaData.getCrossReference(null, null, "parent", null, null, "child");
+                this.rs.next();
+                assertEquals("parent", this.rs.getString("PKTABLE_NAME"));
+                assertEquals("id", this.rs.getString("PKCOLUMN_NAME"));
+                assertEquals("child", this.rs.getString("FKTABLE_NAME"));
+                assertEquals("parent_id", this.rs.getString("FKCOLUMN_NAME"));
+            } finally {
+                this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
+                this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
+                conn1.close();
+            }
+        }
+    }
+    
+    /**
+     * Tests the implementation of Information Schema for foreign key.
+     */
+    public void testGetExportedKeysUsingInfoSchema() throws Exception {
+        if (versionMeetsMinimum(5, 0, 7)) {
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
+            this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
+            this.stmt.executeUpdate("CREATE TABLE parent(id INT NOT NULL, "
+                + "PRIMARY KEY (id)) ENGINE=INNODB");
+            this.stmt.executeUpdate("CREATE TABLE child(id INT, parent_id INT, "
+                + "FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET NULL) ENGINE=INNODB");
+            Properties props = new Properties();
+            props.put("useInformationSchema", "true");
+            Connection conn1 = null;
+            try {
+                conn1 = getConnectionWithProps(props);
+                DatabaseMetaData metaData = conn1.getMetaData();
+                this.rs = metaData.getExportedKeys(null, null, "parent");
+                this.rs.next();
+                assertEquals("parent", this.rs.getString("PKTABLE_NAME"));
+                assertEquals("id", this.rs.getString("PKCOLUMN_NAME"));
+                assertEquals("child", this.rs.getString("FKTABLE_NAME"));
+                assertEquals("parent_id", this.rs.getString("FKCOLUMN_NAME"));
+            } finally {
+                this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
+                this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
+                conn1.close();
+            }
+        }
+    }
+    
+    /**
+     * Tests the implementation of Information Schema for foreign key.
+     */
+    public void testGetImportedKeysUsingInfoSchema() throws Exception {
+        if (versionMeetsMinimum(5, 0, 7)) {
+            this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
+            this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
+            this.stmt.executeUpdate("CREATE TABLE parent(id INT NOT NULL, "
+                + "PRIMARY KEY (id)) ENGINE=INNODB");
+            this.stmt.executeUpdate("CREATE TABLE child(id INT, parent_id INT, "
+                + "FOREIGN KEY (parent_id) REFERENCES parent(id) ON DELETE SET NULL) ENGINE=INNODB");
+            Properties props = new Properties();
+            props.put("useInformationSchema", "true");
+            Connection conn1 = null;
+            try {
+                conn1 = getConnectionWithProps(props);
+                DatabaseMetaData metaData = conn1.getMetaData();
+                this.rs = metaData.getImportedKeys(null, null, "child");
+                this.rs.next();
+                assertEquals("parent", this.rs.getString("PKTABLE_NAME"));
+                assertEquals("id", this.rs.getString("PKCOLUMN_NAME"));
+                assertEquals("child", this.rs.getString("FKTABLE_NAME"));
+                assertEquals("parent_id", this.rs.getString("FKCOLUMN_NAME"));
+            } finally {
+                this.stmt.executeUpdate("DROP TABLE IF EXISTS child");
+                this.stmt.executeUpdate("DROP TABLE If EXISTS parent");
+                conn1.close();
+            }
+        }
+    }
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/MiniAdminTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/MiniAdminTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/MiniAdminTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,93 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import com.mysql.jdbc.MiniAdmin;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Testsuite for MiniAdmin functionality.
+ * 
+ * @author Mark Matthews
+ */
+public class MiniAdminTest extends BaseTestCase {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	/**
+	 * The system property that must exist to run the shutdown test
+	 */
+	private static final String SHUTDOWN_PROP = "com.mysql.jdbc.testsuite.MiniAdminTest.runShutdown";
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new test case
+	 * 
+	 * @param name
+	 *            the test to run
+	 */
+	public MiniAdminTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(MiniAdminTest.class);
+	}
+
+	/**
+	 * Tests whether or not you can shutdown the server with MiniAdmin.
+	 * 
+	 * Only runs if SHUTDOWN_PROP is defined.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testShutdown() throws Exception {
+		if (runTestIfSysPropDefined(SHUTDOWN_PROP)) {
+			new MiniAdmin(this.conn).shutdown();
+		}
+	}
+
+	/**
+	 * Tests whether or not you can construct a MiniAdmin with a JDBC URL.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testUrlConstructor() throws Exception {
+		new MiniAdmin(dbUrl);
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/NumbersTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/NumbersTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/NumbersTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,117 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import testsuite.BaseTestCase;
+
+import java.sql.SQLException;
+
+/**
+ * 
+ * @author Mark Matthews
+ * @version $Id: NumbersTest.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class NumbersTest extends BaseTestCase {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	private static final long TEST_BIGINT_VALUE = 6147483647L;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new NumbersTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public NumbersTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(NumbersTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		createTestTable();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testNumbers() throws SQLException {
+		this.rs = this.stmt.executeQuery("SELECT * from number_test");
+
+		while (this.rs.next()) {
+			long minBigInt = this.rs.getLong(1);
+			long maxBigInt = this.rs.getLong(2);
+			long testBigInt = this.rs.getLong(3);
+			assertTrue("Minimum bigint not stored correctly",
+					(minBigInt == Long.MIN_VALUE));
+			assertTrue("Maximum bigint not stored correctly",
+					(maxBigInt == Long.MAX_VALUE));
+			assertTrue("Test bigint not stored correctly",
+					(TEST_BIGINT_VALUE == testBigInt));
+		}
+	}
+
+	private void createTestTable() throws SQLException {
+		try {
+			this.stmt.executeUpdate("DROP TABLE number_test");
+		} catch (SQLException sqlEx) {
+			;
+		}
+
+		this.stmt
+				.executeUpdate("CREATE TABLE number_test (minBigInt bigint, maxBigInt bigint, testBigInt bigint)");
+		this.stmt
+				.executeUpdate("INSERT INTO number_test (minBigInt,maxBigInt,testBigInt) values ("
+						+ Long.MIN_VALUE
+						+ ","
+						+ Long.MAX_VALUE
+						+ ","
+						+ TEST_BIGINT_VALUE + ")");
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/SSLTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/SSLTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/SSLTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,81 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests SSL functionality in the driver.
+ * 
+ * @author Mark Matthews
+ */
+public class SSLTest extends BaseTestCase {
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Constructor for SSLTest.
+	 * 
+	 * @param name
+	 *            the name of the test to run.
+	 */
+	public SSLTest(String name) {
+		super(name);
+
+		System.setProperty("javax.net.debug", "all");
+
+		StringBuffer sslUrl = new StringBuffer(dbUrl);
+
+		if (dbUrl.indexOf("?") == -1) {
+			sslUrl.append("?");
+		} else {
+			sslUrl.append("&");
+		}
+
+		sslUrl.append("useSSL=true");
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(SSLTest.class);
+	}
+
+	/**
+	 * Tests SSL Connection
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testConnect() throws Exception {
+		System.out.println("<<<<<<<<<<< Look for SSL debug output >>>>>>>>>>>");
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/ServerControllerTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/ServerControllerTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/ServerControllerTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,86 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import com.mysql.jdbc.util.ServerController;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Demonstrates usage of the ServerController class.
+ * 
+ * @author Mark Matthews
+ * @version $Id: ServerControllerTest.java,v 1.1.2.1 2005/05/13 18:58:37
+ *          mmatthews Exp $
+ */
+public class ServerControllerTest extends BaseTestCase {
+
+	private String baseDir;
+
+	/**
+	 * Creates a ServerControllerTest testcase.
+	 * 
+	 * @param name
+	 *            the name of the test to run.
+	 */
+	public ServerControllerTest(String name) {
+		super(name);
+
+		this.baseDir = System
+				.getProperty("com.mysql.jdbc.test.ServerController.basedir");
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(ServerControllerTest.class);
+	}
+
+	/**
+	 * Demonstrates usage of the ServerController class.
+	 * 
+	 * This test is only run if the property
+	 * 'com.mysql.jdbc.test.ServerController.basedir' is set.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testServerController() throws Exception {
+
+		if (this.baseDir != null) {
+			System.out.println("Starting server @ " + this.baseDir);
+
+			ServerController controller = new ServerController(this.baseDir);
+			System.out.println(controller.start());
+			System.out.println("Hit enter to stop server....");
+			System.in.read();
+			controller.stop(true);
+
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/SimpleTransformer.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/SimpleTransformer.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/SimpleTransformer.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,54 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+package testsuite.simple;
+
+import java.sql.SQLException;
+import java.util.Properties;
+
+import com.mysql.jdbc.ConnectionPropertiesTransform;
+import com.mysql.jdbc.NonRegisteringDriver;
+
+/**
+ * Used for testing the ConnectionPropertiesTransform functionality.
+ * 
+ * @author Mark Matthews
+ * 
+ * @version $Id: SimpleTransformer.java,v 1.1.2.1 2005/05/13 18:58:37 mmatthews
+ *          Exp $
+ */
+public class SimpleTransformer implements ConnectionPropertiesTransform {
+
+	/*
+	 * (non-Javadoc)
+	 * 
+	 * @see com.mysql.jdbc.ConnectionPropertiesTransform#transformProperties(java.util.Properties)
+	 */
+	public Properties transformProperties(Properties props) throws SQLException {
+		props
+				.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY,
+						"albequerque");
+
+		return props;
+	}
+
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/StatementsTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/StatementsTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/StatementsTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,1588 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import java.io.ByteArrayInputStream;
+import java.io.CharArrayReader;
+import java.io.Reader;
+import java.rmi.server.UID;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.sql.Types;
+import java.util.Properties;
+
+import testsuite.BaseTestCase;
+
+import com.mysql.jdbc.NotImplemented;
+import com.mysql.jdbc.SQLError;
+
+/**
+ * DOCUMENT ME!
+ * 
+ * @author Mark Matthews
+ * @version $Id: StatementsTest.java 4494 2005-10-31 22:30:34 -0600 (Mon, 31 Oct
+ *          2005) mmatthews $
+ */
+public class StatementsTest extends BaseTestCase {
+	private static final int MAX_COLUMN_LENGTH = 255;
+
+	private static final int MAX_COLUMNS_TO_TEST = 40;
+
+	private static final int MIN_COLUMN_LENGTH = 10;
+
+	private static final int STEP = 8;
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(StatementsTest.class);
+	}
+
+	/**
+	 * Creates a new StatementsTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public StatementsTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS statement_test");
+
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS statement_batch_test");
+
+		this.stmt
+				.executeUpdate("CREATE TABLE statement_test (id int not null primary key auto_increment, strdata1 varchar(255) not null, strdata2 varchar(255))");
+
+		this.stmt.executeUpdate("CREATE TABLE statement_batch_test "
+				+ "(id int not null primary key auto_increment, "
+				+ "strdata1 varchar(255) not null, strdata2 varchar(255), "
+				+ "UNIQUE INDEX (strdata1))");
+
+		for (int i = 6; i < MAX_COLUMNS_TO_TEST; i += STEP) {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS statement_col_test_"
+					+ i);
+
+			StringBuffer insertBuf = new StringBuffer(
+					"INSERT INTO statement_col_test_");
+			StringBuffer stmtBuf = new StringBuffer(
+					"CREATE TABLE IF NOT EXISTS statement_col_test_");
+			stmtBuf.append(i);
+			insertBuf.append(i);
+			stmtBuf.append(" (");
+			insertBuf.append(" VALUES (");
+
+			boolean firstTime = true;
+
+			for (int j = 0; j < i; j++) {
+				if (!firstTime) {
+					stmtBuf.append(",");
+					insertBuf.append(",");
+				} else {
+					firstTime = false;
+				}
+
+				stmtBuf.append("col_");
+				stmtBuf.append(j);
+				stmtBuf.append(" VARCHAR(");
+				stmtBuf.append(MAX_COLUMN_LENGTH);
+				stmtBuf.append(")");
+				insertBuf.append("'");
+
+				int numChars = 16;
+
+				for (int k = 0; k < numChars; k++) {
+					insertBuf.append("A");
+				}
+
+				insertBuf.append("'");
+			}
+
+			stmtBuf.append(")");
+			insertBuf.append(")");
+			this.stmt.executeUpdate(stmtBuf.toString());
+			this.stmt.executeUpdate(insertBuf.toString());
+		}
+
+		// explicitly set the catalog to exercise code in execute(),
+		// executeQuery() and
+		// executeUpdate()
+		// FIXME: Only works on Windows!
+		// this.conn.setCatalog(this.conn.getCatalog().toUpperCase());
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void tearDown() throws Exception {
+		this.stmt.executeUpdate("DROP TABLE statement_test");
+
+		for (int i = 0; i < MAX_COLUMNS_TO_TEST; i += STEP) {
+			StringBuffer stmtBuf = new StringBuffer(
+					"DROP TABLE IF EXISTS statement_col_test_");
+			stmtBuf.append(i);
+			this.stmt.executeUpdate(stmtBuf.toString());
+		}
+
+		try {
+			this.stmt.executeUpdate("DROP TABLE statement_batch_test");
+		} catch (SQLException sqlEx) {
+			;
+		}
+
+		super.tearDown();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testAccessorsAndMutators() throws SQLException {
+		assertTrue("Connection can not be null, and must be same connection",
+				this.stmt.getConnection() == this.conn);
+
+		// Set max rows, to exercise code in execute(), executeQuery() and
+		// executeUpdate()
+		Statement accessorStmt = null;
+
+		try {
+			accessorStmt = this.conn.createStatement();
+			accessorStmt.setMaxRows(1);
+			accessorStmt.setMaxRows(0); // FIXME, test that this actually
+			// affects rows returned
+			accessorStmt.setMaxFieldSize(255);
+			assertTrue("Max field size should match what was set", accessorStmt
+					.getMaxFieldSize() == 255);
+
+			try {
+				accessorStmt.setMaxFieldSize(Integer.MAX_VALUE);
+				fail("Should not be able to set max field size > max_packet_size");
+			} catch (SQLException sqlEx) {
+				;
+			}
+
+			accessorStmt.setCursorName("undef");
+			accessorStmt.setEscapeProcessing(true);
+			accessorStmt.setFetchDirection(java.sql.ResultSet.FETCH_FORWARD);
+
+			int fetchDirection = accessorStmt.getFetchDirection();
+			assertTrue("Set fetch direction != get fetch direction",
+					fetchDirection == java.sql.ResultSet.FETCH_FORWARD);
+
+			try {
+				accessorStmt.setFetchDirection(Integer.MAX_VALUE);
+				fail("Should not be able to set fetch direction to invalid value");
+			} catch (SQLException sqlEx) {
+				;
+			}
+
+			try {
+				accessorStmt.setMaxRows(50000000 + 10);
+				fail("Should not be able to set max rows > 50000000");
+			} catch (SQLException sqlEx) {
+				;
+			}
+
+			try {
+				accessorStmt.setMaxRows(Integer.MIN_VALUE);
+				fail("Should not be able to set max rows < 0");
+			} catch (SQLException sqlEx) {
+				;
+			}
+
+			int fetchSize = this.stmt.getFetchSize();
+
+			try {
+				accessorStmt.setMaxRows(4);
+				accessorStmt.setFetchSize(Integer.MAX_VALUE);
+				fail("Should not be able to set FetchSize > max rows");
+			} catch (SQLException sqlEx) {
+				;
+			}
+
+			try {
+				accessorStmt.setFetchSize(-2);
+				fail("Should not be able to set FetchSize < 0");
+			} catch (SQLException sqlEx) {
+				;
+			}
+
+			assertTrue(
+					"Fetch size before invalid setFetchSize() calls should match fetch size now",
+					fetchSize == this.stmt.getFetchSize());
+		} finally {
+			if (accessorStmt != null) {
+				try {
+					accessorStmt.close();
+				} catch (SQLException sqlEx) {
+					;
+				}
+
+				accessorStmt = null;
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testAutoIncrement() throws SQLException {
+		if (!isRunningOnJdk131()) {
+			try {
+				this.stmt = this.conn.createStatement(
+						java.sql.ResultSet.TYPE_FORWARD_ONLY,
+						java.sql.ResultSet.CONCUR_READ_ONLY);
+				this.stmt.setFetchSize(Integer.MIN_VALUE);
+				this.stmt
+						.executeUpdate("INSERT INTO statement_test (strdata1) values ('blah')");
+
+				int autoIncKeyFromApi = -1;
+				this.rs = this.stmt.getGeneratedKeys();
+
+				if (this.rs.next()) {
+					autoIncKeyFromApi = this.rs.getInt(1);
+				} else {
+					fail("Failed to retrieve AUTO_INCREMENT using Statement.getGeneratedKeys()");
+				}
+
+				this.rs.close();
+
+				int autoIncKeyFromFunc = -1;
+				this.rs = this.stmt.executeQuery("SELECT LAST_INSERT_ID()");
+
+				if (this.rs.next()) {
+					autoIncKeyFromFunc = this.rs.getInt(1);
+				} else {
+					fail("Failed to retrieve AUTO_INCREMENT using LAST_INSERT_ID()");
+				}
+
+				if ((autoIncKeyFromApi != -1) && (autoIncKeyFromFunc != -1)) {
+					assertTrue(
+							"Key retrieved from API ("
+									+ autoIncKeyFromApi
+									+ ") does not match key retrieved from LAST_INSERT_ID() "
+									+ autoIncKeyFromFunc + ") function",
+							autoIncKeyFromApi == autoIncKeyFromFunc);
+				} else {
+					fail("AutoIncrement keys were '0'");
+				}
+			} finally {
+				if (this.rs != null) {
+					try {
+						this.rs.close();
+					} catch (Exception ex) { /* ignore */
+						;
+					}
+				}
+
+				this.rs = null;
+			}
+		}
+	}
+
+	/**
+	 * 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 {
+		/*
+		 * TINYINT 1 -128 127 SMALLINT 2 -32768 32767 MEDIUMINT 3 -8388608
+		 * 8388607 INT 4 -2147483648 2147483647 BIGINT 8 -9223372036854775808
+		 * 9223372036854775807
+		 */
+
+		String unsignedMinimum = "0";
+
+		String tiMinimum = "-128";
+		String tiMaximum = "127";
+		String utiMaximum = "255";
+
+		String siMinimum = "-32768";
+		String siMaximum = "32767";
+		String usiMaximum = "65535";
+
+		String miMinimum = "-8388608";
+		String miMaximum = "8388607";
+		String umiMaximum = "16777215";
+
+		String iMinimum = "-2147483648";
+		String iMaximum = "2147483647";
+		String uiMaximum = "4294967295";
+
+		String biMinimum = "-9223372036854775808";
+		String biMaximum = "9223372036854775807";
+		String ubiMaximum = "18446744073709551615";
+
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testBinaryResultSetNumericTypes");
+			this.stmt
+					.executeUpdate("CREATE TABLE testBinaryResultSetNumericTypes(rowOrder TINYINT, ti TINYINT,"
+							+ "uti TINYINT UNSIGNED, si SMALLINT,"
+							+ "usi SMALLINT UNSIGNED, mi MEDIUMINT,"
+							+ "umi MEDIUMINT UNSIGNED, i INT, ui INT UNSIGNED,"
+							+ "bi BIGINT, ubi BIGINT UNSIGNED)");
+			PreparedStatement inserter = this.conn
+					.prepareStatement("INSERT INTO testBinaryResultSetNumericTypes VALUES (?,?,?,?,?,?,?,?,?,?,?)");
+			inserter.setInt(1, 0);
+			inserter.setString(2, tiMinimum);
+			inserter.setString(3, unsignedMinimum);
+			inserter.setString(4, siMinimum);
+			inserter.setString(5, unsignedMinimum);
+			inserter.setString(6, miMinimum);
+			inserter.setString(7, unsignedMinimum);
+			inserter.setString(8, iMinimum);
+			inserter.setString(9, unsignedMinimum);
+			inserter.setString(10, biMinimum);
+			inserter.setString(11, unsignedMinimum);
+			inserter.executeUpdate();
+
+			inserter.setInt(1, 1);
+			inserter.setString(2, tiMaximum);
+			inserter.setString(3, utiMaximum);
+			inserter.setString(4, siMaximum);
+			inserter.setString(5, usiMaximum);
+			inserter.setString(6, miMaximum);
+			inserter.setString(7, umiMaximum);
+			inserter.setString(8, iMaximum);
+			inserter.setString(9, uiMaximum);
+			inserter.setString(10, biMaximum);
+			inserter.setString(11, ubiMaximum);
+			inserter.executeUpdate();
+
+			PreparedStatement selector = this.conn
+					.prepareStatement("SELECT * FROM testBinaryResultSetNumericTypes ORDER by rowOrder ASC");
+			this.rs = selector.executeQuery();
+
+			assertTrue(this.rs.next());
+
+			assertTrue(this.rs.getString(2).equals(tiMinimum));
+			assertTrue(this.rs.getString(3).equals(unsignedMinimum));
+			assertTrue(this.rs.getString(4).equals(siMinimum));
+			assertTrue(this.rs.getString(5).equals(unsignedMinimum));
+			assertTrue(this.rs.getString(6).equals(miMinimum));
+			assertTrue(this.rs.getString(7).equals(unsignedMinimum));
+			assertTrue(this.rs.getString(8).equals(iMinimum));
+			assertTrue(this.rs.getString(9).equals(unsignedMinimum));
+			assertTrue(this.rs.getString(10).equals(biMinimum));
+			assertTrue(this.rs.getString(11).equals(unsignedMinimum));
+
+			assertTrue(this.rs.next());
+
+			assertTrue(this.rs.getString(2) + " != " + tiMaximum, this.rs
+					.getString(2).equals(tiMaximum));
+			assertTrue(this.rs.getString(3) + " != " + utiMaximum, this.rs
+					.getString(3).equals(utiMaximum));
+			assertTrue(this.rs.getString(4) + " != " + siMaximum, this.rs
+					.getString(4).equals(siMaximum));
+			assertTrue(this.rs.getString(5) + " != " + usiMaximum, this.rs
+					.getString(5).equals(usiMaximum));
+			assertTrue(this.rs.getString(6) + " != " + miMaximum, this.rs
+					.getString(6).equals(miMaximum));
+			assertTrue(this.rs.getString(7) + " != " + umiMaximum, this.rs
+					.getString(7).equals(umiMaximum));
+			assertTrue(this.rs.getString(8) + " != " + iMaximum, this.rs
+					.getString(8).equals(iMaximum));
+			assertTrue(this.rs.getString(9) + " != " + uiMaximum, this.rs
+					.getString(9).equals(uiMaximum));
+			assertTrue(this.rs.getString(10) + " != " + biMaximum, this.rs
+					.getString(10).equals(biMaximum));
+			assertTrue(this.rs.getString(11) + " != " + ubiMaximum, this.rs
+					.getString(11).equals(ubiMaximum));
+
+			assertTrue(!this.rs.next());
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testBinaryResultSetNumericTypes");
+		}
+	}
+
+	/**
+	 * Tests stored procedure functionality
+	 * 
+	 * @throws Exception
+	 *             if an error occurs.
+	 */
+	public void testCallableStatement() throws Exception {
+		if (versionMeetsMinimum(5, 0)) {
+			CallableStatement cStmt = null;
+			String stringVal = "abcdefg";
+			int intVal = 42;
+
+			try {
+				try {
+					this.stmt.executeUpdate("DROP PROCEDURE testCallStmt");
+				} catch (SQLException sqlEx) {
+					if (sqlEx.getMessage().indexOf("does not exist") == -1) {
+						throw sqlEx;
+					}
+				}
+
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS callStmtTbl");
+				this.stmt
+						.executeUpdate("CREATE TABLE callStmtTbl (x CHAR(16), y INT)");
+
+				this.stmt
+						.executeUpdate("CREATE PROCEDURE testCallStmt(n INT, x CHAR(16), y INT)"
+								+ " WHILE n DO"
+								+ "    SET n = n - 1;"
+								+ "    INSERT INTO callStmtTbl VALUES (x, y);"
+								+ " END WHILE;");
+
+				int rowsToCheck = 15;
+
+				cStmt = this.conn.prepareCall("{call testCallStmt(?,?,?)}");
+				cStmt.setInt(1, rowsToCheck);
+				cStmt.setString(2, stringVal);
+				cStmt.setInt(3, intVal);
+				cStmt.execute();
+
+				this.rs = this.stmt.executeQuery("SELECT x,y FROM callStmtTbl");
+
+				int numRows = 0;
+
+				while (this.rs.next()) {
+					assertTrue(this.rs.getString(1).equals(stringVal)
+							&& (this.rs.getInt(2) == intVal));
+
+					numRows++;
+				}
+
+				this.rs.close();
+				this.rs = null;
+
+				cStmt.close();
+				cStmt = null;
+
+				System.out.println(rowsToCheck + " rows returned");
+
+				assertTrue(numRows == rowsToCheck);
+			} finally {
+				try {
+					this.stmt.executeUpdate("DROP PROCEDURE testCallStmt");
+				} catch (SQLException sqlEx) {
+					if (sqlEx.getMessage().indexOf("does not exist") == -1) {
+						throw sqlEx;
+					}
+				}
+
+				this.stmt.executeUpdate("DROP TABLE IF EXISTS callStmtTbl");
+
+				if (cStmt != null) {
+					cStmt.close();
+				}
+			}
+		}
+	}
+
+	public void testCancelStatement() throws Exception {
+
+		if (versionMeetsMinimum(5, 0)) {
+			Connection cancelConn = null;
+	
+			try {
+				cancelConn = getConnectionWithProps(null);
+				final Statement cancelStmt = cancelConn.createStatement();
+			
+				cancelStmt.setQueryTimeout(1);
+	
+				long begin = System.currentTimeMillis();
+	
+				try {
+					cancelStmt.execute("SELECT SLEEP(30)");
+				} catch (SQLException sqlEx) {
+					assertTrue("Probably wasn't actually cancelled", System
+							.currentTimeMillis()
+							- begin < 30000);
+				}
+	
+				for (int i = 0; i < 1000; i++) {
+					try {
+						cancelStmt.executeQuery("SELECT 1");
+					} catch (SQLException timedOutEx) {
+						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 {
+								sleep(5000);
+							} 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) {
+					assertTrue("Probably wasn't actually cancelled", System
+							.currentTimeMillis()
+							- begin < 30000);
+				}
+	
+				for (int i = 0; i < 1000; i++) {
+					try {
+						cancelStmt.executeQuery("SELECT 1");
+					} catch (SQLException timedOutEx) {
+						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) {
+					assertTrue("Probably wasn't actually cancelled", System
+							.currentTimeMillis()
+							- begin < 30000);
+				}
+	
+				for (int i = 0; i < 1000; i++) {
+					try {
+						cancelPstmt.executeQuery("SELECT 1");
+					} catch (SQLException timedOutEx) {
+						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 {
+								sleep(5000);
+							} catch (InterruptedException iEx) {
+								// ignore
+							}
+	
+							
+							cancelPstmt.cancel();
+						} catch (SQLException sqlEx) {
+							throw new RuntimeException(sqlEx.toString());
+						}
+					}
+	
+				}.start();
+	
+				begin = System.currentTimeMillis();
+	
+				try {
+					cancelPstmt.execute();
+				} catch (SQLException sqlEx) {
+					assertTrue("Probably wasn't actually cancelled", System
+							.currentTimeMillis()
+							- begin < 30000);
+				}
+	
+				for (int i = 0; i < 1000; i++) {
+					try {
+						cancelPstmt.executeQuery("SELECT 1");
+					} catch (SQLException timedOutEx) {
+						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) {
+					assertTrue("Probably wasn't actually cancelled", System
+							.currentTimeMillis()
+							- begin < 30000);
+				}
+	
+				for (int i = 0; i < 1000; i++) {
+					try {
+						cancelStmt.executeQuery("SELECT 1");
+					} catch (SQLException timedOutEx) {
+						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 {
+								sleep(5000);
+							} catch (InterruptedException iEx) {
+								// ignore
+							}
+	
+							
+							cancelClientPstmt.cancel();
+						} catch (SQLException sqlEx) {
+							throw new RuntimeException(sqlEx.toString());
+						}
+					}
+	
+				}.start();
+	
+				begin = System.currentTimeMillis();
+	
+				try {
+					cancelClientPstmt.execute();
+				} catch (SQLException sqlEx) {
+					assertTrue("Probably wasn't actually cancelled", System
+							.currentTimeMillis()
+							- begin < 30000);
+				}
+	
+				for (int i = 0; i < 1000; i++) {
+					try {
+						cancelClientPstmt.executeQuery("SELECT 1");
+					} catch (SQLException timedOutEx) {
+						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 {
+				if (this.rs != null) {
+					ResultSet toClose = this.rs;
+					this.rs = null;
+					toClose.close();
+				}
+	
+				if (cancelConn != null) {
+					cancelConn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testClose() throws SQLException {
+		Statement closeStmt = null;
+		boolean exceptionAfterClosed = false;
+
+		try {
+			closeStmt = this.conn.createStatement();
+			closeStmt.close();
+
+			try {
+				closeStmt.executeQuery("SELECT 1");
+			} catch (SQLException sqlEx) {
+				exceptionAfterClosed = true;
+			}
+		} finally {
+			if (closeStmt != null) {
+				try {
+					closeStmt.close();
+				} catch (SQLException sqlEx) {
+					/* ignore */
+				}
+			}
+
+			closeStmt = null;
+		}
+
+		assertTrue(
+				"Operations not allowed on Statement after .close() is called!",
+				exceptionAfterClosed);
+	}
+
+	public void testEnableStreamingResults() throws Exception {
+		Statement streamStmt = this.conn.createStatement();
+		((com.mysql.jdbc.Statement) streamStmt).enableStreamingResults();
+		assertEquals(streamStmt.getFetchSize(), Integer.MIN_VALUE);
+		assertEquals(streamStmt.getResultSetType(), ResultSet.TYPE_FORWARD_ONLY);
+	}
+
+	public void testHoldingResultSetsOverClose() throws Exception {
+		Properties props = new Properties();
+		props.setProperty("holdResultsOpenOverStatementClose", "true");
+
+		Connection conn2 = getConnectionWithProps(props);
+
+		Statement stmt2 = null;
+		PreparedStatement pstmt2 = null;
+
+		try {
+			stmt2 = conn2.createStatement();
+
+			this.rs = stmt2.executeQuery("SELECT 1");
+			this.rs.next();
+			this.rs.getInt(1);
+			stmt2.close();
+			this.rs.getInt(1);
+
+			stmt2 = conn2.createStatement();
+			stmt2.execute("SELECT 1");
+			this.rs = stmt2.getResultSet();
+			this.rs.next();
+			this.rs.getInt(1);
+			stmt2.execute("SELECT 2");
+			this.rs.getInt(1);
+
+			pstmt2 = conn2.prepareStatement("SELECT 1");
+			this.rs = pstmt2.executeQuery();
+			this.rs.next();
+			this.rs.getInt(1);
+			pstmt2.close();
+			this.rs.getInt(1);
+
+			pstmt2 = conn2.prepareStatement("SELECT 1");
+			this.rs = pstmt2.executeQuery();
+			this.rs.next();
+			this.rs.getInt(1);
+			pstmt2.executeQuery();
+			this.rs.getInt(1);
+			pstmt2.execute();
+			this.rs.getInt(1);
+
+			pstmt2 = ((com.mysql.jdbc.Connection) conn2)
+					.clientPrepareStatement("SELECT 1");
+			this.rs = pstmt2.executeQuery();
+			this.rs.next();
+			this.rs.getInt(1);
+			pstmt2.close();
+			this.rs.getInt(1);
+
+			pstmt2 = ((com.mysql.jdbc.Connection) conn2)
+					.clientPrepareStatement("SELECT 1");
+			this.rs = pstmt2.executeQuery();
+			this.rs.next();
+			this.rs.getInt(1);
+			pstmt2.executeQuery();
+			this.rs.getInt(1);
+			pstmt2.execute();
+			this.rs.getInt(1);
+
+			stmt2 = conn2.createStatement();
+			this.rs = stmt2.executeQuery("SELECT 1");
+			this.rs.next();
+			this.rs.getInt(1);
+			stmt2.executeQuery("SELECT 2");
+			this.rs.getInt(1);
+			this.rs = stmt2.executeQuery("SELECT 1");
+			this.rs.next();
+			this.rs.getInt(1);
+			stmt2.executeUpdate("SET @var=1");
+			this.rs.getInt(1);
+			stmt2.execute("SET @var=2");
+			this.rs.getInt(1);
+		} finally {
+			if (stmt2 != null) {
+				stmt2.close();
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testInsert() throws SQLException {
+		try {
+			boolean autoCommit = this.conn.getAutoCommit();
+
+			// Test running a query for an update. It should fail.
+			try {
+				this.conn.setAutoCommit(false);
+				this.stmt.executeUpdate("SELECT * FROM statement_test");
+			} catch (SQLException sqlEx) {
+				assertTrue("Exception thrown for unknown reason", sqlEx
+						.getSQLState().equalsIgnoreCase("01S03"));
+			} finally {
+				this.conn.setAutoCommit(autoCommit);
+			}
+
+			// Test running a update for an query. It should fail.
+			try {
+				this.conn.setAutoCommit(false);
+				this.stmt
+						.executeQuery("UPDATE statement_test SET strdata1='blah' WHERE 1=0");
+			} catch (SQLException sqlEx) {
+				assertTrue("Exception thrown for unknown reason", sqlEx
+						.getSQLState().equalsIgnoreCase(
+								SQLError.SQL_STATE_ILLEGAL_ARGUMENT));
+			} finally {
+				this.conn.setAutoCommit(autoCommit);
+			}
+
+			for (int i = 0; i < 10; i++) {
+				int updateCount = this.stmt
+						.executeUpdate("INSERT INTO statement_test (strdata1,strdata2) values ('abcdefg', 'poi')");
+				assertTrue("Update count must be '1', was '" + updateCount
+						+ "'", (updateCount == 1));
+			}
+
+			if (!isRunningOnJdk131()) {
+				int insertIdFromGeneratedKeys = Integer.MIN_VALUE;
+
+				this.stmt
+						.executeUpdate("INSERT INTO statement_test (strdata1, strdata2) values ('a', 'a'), ('b', 'b'), ('c', 'c')");
+				this.rs = this.stmt.getGeneratedKeys();
+
+				if (this.rs.next()) {
+					insertIdFromGeneratedKeys = this.rs.getInt(1);
+				}
+
+				this.rs.close();
+				this.rs = this.stmt.executeQuery("SELECT LAST_INSERT_ID()");
+
+				int insertIdFromServer = Integer.MIN_VALUE;
+
+				if (this.rs.next()) {
+					insertIdFromServer = this.rs.getInt(1);
+				}
+
+				assertEquals(insertIdFromGeneratedKeys, insertIdFromServer);
+			}
+		} finally {
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (Exception ex) { /* ignore */
+					;
+				}
+			}
+
+			this.rs = null;
+		}
+	}
+
+	/**
+	 * Tests multiple statement support
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void testMultiStatements() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			Connection multiStmtConn = null;
+			Statement multiStmt = null;
+
+			try {
+				Properties props = new Properties();
+				props.setProperty("allowMultiQueries", "true");
+
+				multiStmtConn = getConnectionWithProps(props);
+
+				multiStmt = multiStmtConn.createStatement();
+
+				multiStmt
+						.executeUpdate("DROP TABLE IF EXISTS testMultiStatements");
+				multiStmt
+						.executeUpdate("CREATE TABLE testMultiStatements (field1 VARCHAR(255), field2 INT, field3 DOUBLE)");
+				multiStmt
+						.executeUpdate("INSERT INTO testMultiStatements VALUES ('abcd', 1, 2)");
+
+				multiStmt
+						.execute("SELECT field1 FROM testMultiStatements WHERE field1='abcd';"
+								+ "UPDATE testMultiStatements SET field3=3;"
+								+ "SELECT field3 FROM testMultiStatements WHERE field3=3");
+
+				this.rs = multiStmt.getResultSet();
+
+				assertTrue(this.rs.next());
+
+				assertTrue("abcd".equals(this.rs.getString(1)));
+				this.rs.close();
+
+				// Next should be an update count...
+				assertTrue(!multiStmt.getMoreResults());
+
+				assertTrue("Update count was " + multiStmt.getUpdateCount()
+						+ ", expected 1", multiStmt.getUpdateCount() == 1);
+
+				assertTrue(multiStmt.getMoreResults());
+
+				this.rs = multiStmt.getResultSet();
+
+				assertTrue(this.rs.next());
+
+				assertTrue(this.rs.getDouble(1) == 3);
+
+				// End of multi results
+				assertTrue(!multiStmt.getMoreResults());
+				assertTrue(multiStmt.getUpdateCount() == -1);
+			} finally {
+				if (multiStmt != null) {
+					multiStmt
+							.executeUpdate("DROP TABLE IF EXISTS testMultiStatements");
+
+					multiStmt.close();
+				}
+
+				if (multiStmtConn != null) {
+					multiStmtConn.close();
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests that NULLs and '' work correctly.
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public void testNulls() throws SQLException {
+		try {
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS nullTest");
+			this.stmt
+					.executeUpdate("CREATE TABLE IF NOT EXISTS nullTest (field_1 CHAR(20), rowOrder INT)");
+			this.stmt
+					.executeUpdate("INSERT INTO nullTest VALUES (null, 1), ('', 2)");
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field_1 FROM nullTest ORDER BY rowOrder");
+
+			this.rs.next();
+
+			assertTrue("NULL field not returned as NULL", (this.rs
+					.getString("field_1") == null)
+					&& this.rs.wasNull());
+
+			this.rs.next();
+
+			assertTrue("Empty field not returned as \"\"", this.rs.getString(
+					"field_1").equals("")
+					&& !this.rs.wasNull());
+
+			this.rs.close();
+		} finally {
+			if (this.rs != null) {
+				try {
+					this.rs.close();
+				} catch (Exception ex) {
+					// ignore
+				}
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS nullTest");
+		}
+	}
+
+	public void testParsedConversionWarning() throws Exception {
+		if (versionMeetsMinimum(4, 1)) {
+			try {
+				Properties props = new Properties();
+				props.setProperty("useUsageAdvisor", "true");
+				Connection warnConn = getConnectionWithProps(props);
+
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testParsedConversionWarning");
+				this.stmt
+						.executeUpdate("CREATE TABLE testParsedConversionWarning(field1 VARCHAR(255))");
+				this.stmt
+						.executeUpdate("INSERT INTO testParsedConversionWarning VALUES ('1.0')");
+
+				PreparedStatement badStmt = warnConn
+						.prepareStatement("SELECT field1 FROM testParsedConversionWarning");
+
+				this.rs = badStmt.executeQuery();
+				assertTrue(this.rs.next());
+				this.rs.getFloat(1);
+			} finally {
+				this.stmt
+						.executeUpdate("DROP TABLE IF EXISTS testParsedConversionWarning");
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testPreparedStatement() throws SQLException {
+		this.stmt
+				.executeUpdate("INSERT INTO statement_test (id, strdata1,strdata2) values (999,'abcdefg', 'poi')");
+		this.pstmt = this.conn
+				.prepareStatement("UPDATE statement_test SET strdata1=?, strdata2=? where id=999");
+		this.pstmt.setString(1, "iop");
+		this.pstmt.setString(2, "higjklmn");
+
+		// pstmt.setInt(3, 999);
+		int updateCount = this.pstmt.executeUpdate();
+		assertTrue("Update count must be '1', was '" + updateCount + "'",
+				(updateCount == 1));
+
+		this.pstmt.clearParameters();
+
+		this.pstmt.close();
+
+		this.rs = this.stmt
+				.executeQuery("SELECT id, strdata1, strdata2 FROM statement_test");
+
+		assertTrue(this.rs.next());
+		assertTrue(this.rs.getInt(1) == 999);
+		assertTrue("Expected 'iop', received '" + this.rs.getString(2) + "'",
+				"iop".equals(this.rs.getString(2)));
+		assertTrue("Expected 'higjklmn', received '" + this.rs.getString(3)
+				+ "'", "higjklmn".equals(this.rs.getString(3)));
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testPreparedStatementBatch() throws SQLException {
+		this.pstmt = this.conn.prepareStatement("INSERT INTO "
+				+ "statement_batch_test (strdata1, strdata2) VALUES (?,?)");
+
+		for (int i = 0; i < 1000; i++) {
+			this.pstmt.setString(1, "batch_" + i);
+			this.pstmt.setString(2, "batch_" + i);
+			this.pstmt.addBatch();
+		}
+
+		int[] updateCounts = this.pstmt.executeBatch();
+
+		for (int i = 0; i < updateCounts.length; i++) {
+			assertTrue("Update count must be '1', was '" + updateCounts[i]
+					+ "'", (updateCounts[i] == 1));
+		}
+	}
+
+	public void testRowFetch() throws Exception {
+		if (versionMeetsMinimum(5, 0, 5)) {
+			createTable("testRowFetch", "(field1 int)");
+
+			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());
+				assertEquals(2, this.rs.getInt(1));
+				assertTrue(this.rs.next());
+				assertEquals(3, this.rs.getInt(1));
+				assertEquals(false, this.rs.next());
+	
+				fetchStmt.executeQuery();
+			} finally {
+				if (fetchConn != null) {
+					fetchConn.close();
+				}
+			}
+
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testSelectColumns() throws SQLException {
+		for (int i = 6; i < MAX_COLUMNS_TO_TEST; i += STEP) {
+			long start = System.currentTimeMillis();
+			this.rs = this.stmt
+					.executeQuery("SELECT * from statement_col_test_" + i);
+
+			if (this.rs.next()) {
+				;
+			}
+
+			long end = System.currentTimeMillis();
+			System.out.println(i + " columns = " + (end - start) + " ms");
+		}
+	}
+
+	/**
+	 * Tests for PreparedStatement.setObject()
+	 * 
+	 * @throws Exception
+	 */
+	public void testSetObject() throws Exception {
+		Properties props = new Properties();
+		props.put("noDatetimeStringSync", "true"); // value=true for #5
+		Connection conn1 = getConnectionWithProps(props);
+		Statement stmt1 = conn1.createStatement();
+		stmt1.executeUpdate("DROP TABLE IF EXISTS t1");
+		stmt1.executeUpdate("CREATE TABLE t1 (" + "c1 DECIMAL," // instance of
+																// String
+				+ "c2 VARCHAR(255)," // instance of String
+				+ "c3 BLOB," // instance of byte[]
+				+ "c4 DATE," // instance of java.util.Date
+				+ "c5 TIMESTAMP," // instance of String
+				+ "c6 TIME," // instance of String
+				+ "c7 TIME)"); // instance of java.sql.Timestamp
+
+		this.pstmt = conn1
+				.prepareStatement("INSERT INTO t1 VALUES (?, ?, ?, ?, ?, ?, ?)");
+
+		long currentTime = System.currentTimeMillis();
+
+		this.pstmt.setObject(1, "1000", Types.DECIMAL);
+		this.pstmt.setObject(2, "2000", Types.VARCHAR);
+		this.pstmt.setObject(3, new byte[] { 0 }, Types.BLOB);
+		this.pstmt.setObject(4, new java.util.Date(currentTime), Types.DATE);
+		this.pstmt.setObject(5, "2000-01-01 23-59-59", Types.TIMESTAMP);
+		this.pstmt.setObject(6, "11:22:33", Types.TIME);
+		this.pstmt
+				.setObject(7, new java.sql.Timestamp(currentTime), Types.TIME);
+		this.pstmt.execute();
+		this.rs = stmt1.executeQuery("SELECT * FROM t1");
+		this.rs.next();
+
+		assertEquals("1000", this.rs.getString(1));
+		assertEquals("2000", this.rs.getString(2));
+		assertEquals(1, ((byte[]) this.rs.getObject(3)).length);
+		assertEquals(0, ((byte[]) this.rs.getObject(3))[0]);
+		assertEquals(new java.sql.Date(currentTime).toString(), this.rs
+				.getDate(4).toString());
+
+		if (versionMeetsMinimum(4, 1)) {
+			assertEquals("2000-01-01 23:59:59", this.rs.getString(5));
+		} else {
+			assertEquals("20000101235959", this.rs.getString(5));
+		}
+
+		assertEquals("11:22:33", this.rs.getString(6));
+		assertEquals(new java.sql.Time(currentTime).toString(), this.rs
+				.getString(7));
+	}
+
+	// Server-side prepared statements can only reset streamed data
+	// in-toto, not piecemiel.
+
+	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));
+		}
+		
+		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();
+		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 {
+		createTable("testStreamChange",
+				"(field1 varchar(32), field2 int, field3 TEXT, field4 BLOB)");
+		this.pstmt = this.conn
+				.prepareStatement("INSERT INTO testStreamChange VALUES (?, ?, ?, ?)");
+
+		try {
+			this.pstmt.setString(1, "A");
+			this.pstmt.setInt(2, 1);
+
+			char[] cArray = { 'A', 'B', 'C' };
+			Reader r = new CharArrayReader(cArray);
+			this.pstmt.setCharacterStream(3, r, cArray.length);
+
+			byte[] bArray = { 'D', 'E', 'F' };
+			ByteArrayInputStream bais = new ByteArrayInputStream(bArray);
+			this.pstmt.setBinaryStream(4, bais, bArray.length);
+
+			assertEquals(1, this.pstmt.executeUpdate());
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field3, field4 from testStreamChange where field1='A'");
+			this.rs.next();
+			assertEquals("ABC", this.rs.getString(1));
+			assertEquals("DEF", this.rs.getString(2));
+
+			char[] ucArray = { 'C', 'E', 'S', 'U' };
+			this.pstmt.setString(1, "CESU");
+			this.pstmt.setInt(2, 3);
+			Reader ucReader = new CharArrayReader(ucArray);
+			this.pstmt.setCharacterStream(3, ucReader, ucArray.length);
+			this.pstmt.setBinaryStream(4, null, 0);
+			assertEquals(1, this.pstmt.executeUpdate());
+
+			this.rs = this.stmt
+					.executeQuery("SELECT field3, field4 from testStreamChange where field1='CESU'");
+			this.rs.next();
+			assertEquals("CESU", this.rs.getString(1));
+			assertEquals(null, this.rs.getString(2));
+		} finally {
+			if (this.rs != null) {
+				this.rs.close();
+				this.rs = null;
+			}
+
+			if (this.pstmt != null) {
+				this.pstmt.close();
+				this.pstmt = null;
+			}
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testStubbed() throws SQLException {
+		if (!isRunningOnJdk131()) {
+			try {
+				this.stmt.getResultSetHoldability();
+			} catch (NotImplemented notImplEx) {
+				;
+			}
+		}
+	}
+	
+	public void testTruncationOnRead() throws Exception {
+		this.rs = this.stmt.executeQuery("SELECT '" + Long.MAX_VALUE + "'");
+		this.rs.next();
+
+		try {
+			this.rs.getByte(1);
+			fail("Should've thrown an out-of-range exception");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+					.equals(sqlEx.getSQLState()));
+		}
+
+		try {
+			this.rs.getShort(1);
+			fail("Should've thrown an out-of-range exception");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+					.equals(sqlEx.getSQLState()));
+		}
+
+		try {
+			this.rs.getInt(1);
+			fail("Should've thrown an out-of-range exception");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+					.equals(sqlEx.getSQLState()));
+		}
+
+		this.rs = this.stmt.executeQuery("SELECT '" + Double.MAX_VALUE + "'");
+
+		this.rs.next();
+
+		try {
+			this.rs.getByte(1);
+			fail("Should've thrown an out-of-range exception");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+					.equals(sqlEx.getSQLState()));
+		}
+
+		try {
+			this.rs.getShort(1);
+			fail("Should've thrown an out-of-range exception");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+					.equals(sqlEx.getSQLState()));
+		}
+
+		try {
+			this.rs.getInt(1);
+			fail("Should've thrown an out-of-range exception");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+					.equals(sqlEx.getSQLState()));
+		}
+
+		try {
+			this.rs.getLong(1);
+			fail("Should've thrown an out-of-range exception");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+					.equals(sqlEx.getSQLState()));
+		}
+
+		try {
+			this.rs.getLong(1);
+			fail("Should've thrown an out-of-range exception");
+		} catch (SQLException sqlEx) {
+			assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+					.equals(sqlEx.getSQLState()));
+		}
+
+		PreparedStatement pStmt = null;
+
+		System.out
+				.println("Testing prepared statements with binary result sets now");
+
+		try {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testTruncationOnRead");
+			this.stmt
+					.executeUpdate("CREATE TABLE testTruncationOnRead(intField INTEGER, bigintField BIGINT, doubleField DOUBLE)");
+			this.stmt.executeUpdate("INSERT INTO testTruncationOnRead VALUES ("
+					+ Integer.MAX_VALUE + ", " + Long.MAX_VALUE + ", "
+					+ Double.MAX_VALUE + ")");
+			this.stmt.executeUpdate("INSERT INTO testTruncationOnRead VALUES ("
+					+ Integer.MIN_VALUE + ", " + Long.MIN_VALUE + ", "
+					+ Double.MIN_VALUE + ")");
+
+			pStmt = this.conn
+					.prepareStatement("SELECT intField, bigintField, doubleField FROM testTruncationOnRead ORDER BY intField DESC");
+			this.rs = pStmt.executeQuery();
+
+			this.rs.next();
+
+			try {
+				this.rs.getByte(1);
+				fail("Should've thrown an out-of-range exception");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+						.equals(sqlEx.getSQLState()));
+			}
+
+			try {
+				this.rs.getInt(2);
+				fail("Should've thrown an out-of-range exception");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+						.equals(sqlEx.getSQLState()));
+			}
+
+			try {
+				this.rs.getLong(3);
+				fail("Should've thrown an out-of-range exception");
+			} catch (SQLException sqlEx) {
+				assertTrue(SQLError.SQL_STATE_NUMERIC_VALUE_OUT_OF_RANGE
+						.equals(sqlEx.getSQLState()));
+			}
+		} finally {
+			this.stmt
+					.executeUpdate("DROP TABLE IF EXISTS testTruncationOnRead");
+		}
+
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/TransactionTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/TransactionTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/TransactionTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,129 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import testsuite.BaseTestCase;
+
+import java.sql.SQLException;
+
+/**
+ * 
+ * @author Mark Matthews
+ * @version $Id: TransactionTest.java,v 1.1.2.1 2005/05/13 18:58:37 mmatthews
+ *          Exp $
+ */
+public class TransactionTest extends BaseTestCase {
+	// ~ Static fields/initializers
+	// ---------------------------------------------
+
+	private static final double DOUBLE_CONST = 25.4312;
+
+	private static final double EPSILON = .0000001;
+
+	// ~ Constructors
+	// -----------------------------------------------------------
+
+	/**
+	 * Creates a new TransactionTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public TransactionTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods
+	// ----------------------------------------------------------------
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(TransactionTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		createTestTable();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testTransaction() throws SQLException {
+		try {
+			this.conn.setAutoCommit(false);
+			this.stmt
+					.executeUpdate("INSERT INTO trans_test (id, decdata) VALUES (1, 1.0)");
+			this.conn.rollback();
+			this.rs = this.stmt.executeQuery("SELECT * from trans_test");
+
+			boolean hasResults = this.rs.next();
+			assertTrue("Results returned, rollback to empty table failed",
+					(hasResults != true));
+			this.stmt
+					.executeUpdate("INSERT INTO trans_test (id, decdata) VALUES (2, "
+							+ DOUBLE_CONST + ")");
+			this.conn.commit();
+			this.rs = this.stmt
+					.executeQuery("SELECT * from trans_test where id=2");
+			hasResults = this.rs.next();
+			assertTrue("No rows in table after INSERT", hasResults);
+
+			double doubleVal = this.rs.getDouble(2);
+			double delta = Math.abs(DOUBLE_CONST - doubleVal);
+			assertTrue("Double value returned != " + DOUBLE_CONST,
+					(delta < EPSILON));
+		} finally {
+			this.conn.setAutoCommit(true);
+		}
+	}
+
+	private void createTestTable() throws SQLException {
+		//
+		// Catch the error, the table might exist
+		//
+		try {
+			this.stmt.executeUpdate("DROP TABLE trans_test");
+		} catch (SQLException sqlEx) {
+			;
+		}
+
+		this.stmt
+				.executeUpdate("CREATE TABLE trans_test (id INT NOT NULL PRIMARY KEY, decdata DOUBLE) TYPE=InnoDB");
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/TraversalTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/TraversalTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/TraversalTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,222 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+ 
+ */
+package testsuite.simple;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Tests result set traversal methods.
+ * 
+ * @author Mark Matthews
+ * @version $Id: TraversalTest.java 3726 2005-05-19 15:52:24Z mmatthews $
+ */
+public class TraversalTest extends BaseTestCase {
+
+	// ~ Constructors ..........................................................
+
+	/**
+	 * Creates a new TraversalTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public TraversalTest(String name) {
+		super(name);
+	}
+
+	// ~ Methods ...............................................................
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(TraversalTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		createTestTable();
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testTraversal() throws SQLException {
+
+		Statement scrollableStmt = null;
+
+		try {
+			scrollableStmt = this.conn
+					.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE,
+							ResultSet.CONCUR_READ_ONLY);
+			this.rs = scrollableStmt
+					.executeQuery("SELECT * FROM TRAVERSAL ORDER BY pos");
+
+			// Test isFirst()
+			if (this.rs.first()) {
+				assertTrue("ResultSet.isFirst() failed", this.rs.isFirst());
+				this.rs.relative(-1);
+				assertTrue("ResultSet.isBeforeFirst() failed", this.rs
+						.isBeforeFirst());
+			}
+
+			// Test isLast()
+			if (this.rs.last()) {
+				assertTrue("ResultSet.isLast() failed", this.rs.isLast());
+				this.rs.relative(1);
+				assertTrue("ResultSet.isAfterLast() failed", this.rs
+						.isAfterLast());
+			}
+
+			int count = 0;
+			this.rs.beforeFirst();
+
+			boolean forwardOk = true;
+
+			while (this.rs.next()) {
+
+				int pos = this.rs.getInt("POS");
+
+				// test case-sensitive column names
+				pos = this.rs.getInt("pos");
+				pos = this.rs.getInt("Pos");
+				pos = this.rs.getInt("POs");
+				pos = this.rs.getInt("PoS");
+				pos = this.rs.getInt("pOS");
+				pos = this.rs.getInt("pOs");
+				pos = this.rs.getInt("poS");
+
+				if (pos != count) {
+					forwardOk = false;
+				}
+
+				assertTrue("ResultSet.getRow() failed.", pos == (this.rs
+						.getRow() - 1));
+
+				count++;
+
+			}
+
+			assertTrue("Only traversed " + count + " / 100 rows", forwardOk);
+
+			boolean isAfterLast = this.rs.isAfterLast();
+			assertTrue("ResultSet.isAfterLast() failed", isAfterLast);
+			this.rs.afterLast();
+
+			// Scroll backwards
+			count = 99;
+
+			boolean reverseOk = true;
+
+			while (this.rs.previous()) {
+
+				int pos = this.rs.getInt("pos");
+
+				if (pos != count) {
+					reverseOk = false;
+				}
+
+				count--;
+			}
+
+			assertTrue("ResultSet.previous() failed", reverseOk);
+
+			boolean isBeforeFirst = this.rs.isBeforeFirst();
+			assertTrue("ResultSet.isBeforeFirst() failed", isBeforeFirst);
+
+			this.rs.next();
+			boolean isFirst = this.rs.isFirst();
+			assertTrue("ResultSet.isFirst() failed", isFirst);
+
+			// Test absolute positioning
+			this.rs.absolute(50);
+			int pos = this.rs.getInt("pos");
+			assertTrue("ResultSet.absolute() failed", pos == 49);
+
+			// Test relative positioning
+			this.rs.relative(-1);
+			pos = this.rs.getInt("pos");
+			assertTrue("ResultSet.relative(-1) failed", pos == 48);
+
+			// Test bogus absolute index
+			boolean onResultSet = this.rs.absolute(200);
+			assertTrue("ResultSet.absolute() to point off result set failed",
+					onResultSet == false);
+			onResultSet = this.rs.absolute(100);
+			assertTrue(
+					"ResultSet.absolute() from off this.rs to on this.rs failed",
+					onResultSet);
+
+			onResultSet = this.rs.absolute(-99);
+			assertTrue("ResultSet.absolute(-99) failed", onResultSet);
+			assertTrue("ResultSet absolute(-99) failed", this.rs.getInt(1) == 1);
+		} finally {
+
+			if (scrollableStmt != null) {
+
+				try {
+					scrollableStmt.close();
+				} catch (SQLException sqlEx) {
+					;
+				}
+			}
+		}
+	}
+
+	private void createTestTable() throws SQLException {
+
+		//
+		// Catch the error, the table might exist
+		//
+		try {
+			this.stmt.executeUpdate("DROP TABLE TRAVERSAL");
+		} catch (SQLException SQLE) {
+			;
+		}
+
+		this.stmt
+				.executeUpdate("CREATE TABLE TRAVERSAL (pos int PRIMARY KEY, stringdata CHAR(32))");
+
+		for (int i = 0; i < 100; i++) {
+			this.stmt.executeUpdate("INSERT INTO TRAVERSAL VALUES (" + i
+					+ ", 'StringData')");
+		}
+	}
+}
\ No newline at end of file

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/UpdatabilityTest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/UpdatabilityTest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/UpdatabilityTest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,360 @@
+/*
+ Copyright (C) 2002-2004 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as 
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL 
+ as it is applied to this software. View the full text of the 
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this 
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+
+ */
+package testsuite.simple;
+
+import com.mysql.jdbc.NotUpdatable;
+
+import testsuite.BaseTestCase;
+
+import java.sql.DatabaseMetaData;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ * Tests for updatable result sets
+ * 
+ * @author Mark Matthews
+ * @version $Id: UpdatabilityTest.java,v 1.1.2.1 2005/05/13 18:58:37 mmatthews
+ *          Exp $
+ */
+public class UpdatabilityTest extends BaseTestCase {
+	/**
+	 * Creates a new UpdatabilityTest object.
+	 * 
+	 * @param name
+	 *            DOCUMENT ME!
+	 */
+	public UpdatabilityTest(String name) {
+		super(name);
+	}
+
+	/**
+	 * Runs all test cases in this test suite
+	 * 
+	 * @param args
+	 */
+	public static void main(String[] args) {
+		junit.textui.TestRunner.run(UpdatabilityTest.class);
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws Exception
+	 *             DOCUMENT ME!
+	 */
+	public void setUp() throws Exception {
+		super.setUp();
+		createTestTable();
+	}
+
+	/**
+	 * If using MySQL-4.1, tests if aliased tables work as updatable result
+	 * sets.
+	 * 
+	 * @throws Exception
+	 *             if an error occurs
+	 */
+	public void testAliasedTables() throws Exception {
+		DatabaseMetaData dbmd = this.conn.getMetaData();
+
+		if (versionMeetsMinimum(4, 1)) {
+			Statement scrollableStmt = null;
+
+			try {
+				scrollableStmt = this.conn.createStatement(
+						ResultSet.TYPE_SCROLL_INSENSITIVE,
+						ResultSet.CONCUR_UPDATABLE);
+				this.rs = scrollableStmt
+						.executeQuery("SELECT pos1 AS p1, pos2 AS P2, char_field AS cf FROM UPDATABLE AS UPD LIMIT 1");
+				this.rs.next();
+				this.rs.close();
+				this.rs = null;
+
+				scrollableStmt.close();
+				scrollableStmt = null;
+			} finally {
+				if (this.rs != null) {
+					try {
+						this.rs.close();
+					} catch (SQLException sqlEx) {
+						; // ignore
+					}
+
+					this.rs = null;
+				}
+
+				if (scrollableStmt != null) {
+					try {
+						scrollableStmt.close();
+					} catch (SQLException sqlEx) {
+						; // ignore
+					}
+
+					scrollableStmt = null;
+				}
+			}
+		}
+	}
+
+	/**
+	 * Tests that the driver does not let you update result sets that come from
+	 * tables that don't have primary keys
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public void testBogusTable() throws SQLException {
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS BOGUS_UPDATABLE");
+		this.stmt.executeUpdate("CREATE TABLE BOGUS_UPDATABLE (field1 int)");
+
+		Statement scrollableStmt = null;
+
+		try {
+			scrollableStmt = this.conn.createStatement(
+					ResultSet.TYPE_SCROLL_INSENSITIVE,
+					ResultSet.CONCUR_UPDATABLE);
+			this.rs = scrollableStmt
+					.executeQuery("SELECT * FROM BOGUS_UPDATABLE");
+
+			try {
+				this.rs.moveToInsertRow();
+				fail("ResultSet.moveToInsertRow() should not succeed on non-updatable table");
+			} catch (NotUpdatable noUpdate) {
+				// ignore
+			}
+		} finally {
+			if (scrollableStmt != null) {
+				try {
+					scrollableStmt.close();
+				} catch (SQLException sqlEx) {
+					;
+				}
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS BOGUS_UPDATABLE");
+		}
+	}
+
+	/**
+	 * Tests that the driver does not let you update result sets that come from
+	 * queries that haven't selected all primary keys
+	 * 
+	 * @throws SQLException
+	 *             if an error occurs
+	 */
+	public void testMultiKeyTable() throws SQLException {
+		this.stmt.executeUpdate("DROP TABLE IF EXISTS MULTI_UPDATABLE");
+		this.stmt
+				.executeUpdate("CREATE TABLE MULTI_UPDATABLE (field1 int NOT NULL, field2 int NOT NULL, PRIMARY KEY (field1, field2))");
+
+		Statement scrollableStmt = null;
+
+		try {
+			scrollableStmt = this.conn.createStatement(
+					ResultSet.TYPE_SCROLL_INSENSITIVE,
+					ResultSet.CONCUR_UPDATABLE);
+			this.rs = scrollableStmt
+					.executeQuery("SELECT field1 FROM MULTI_UPDATABLE");
+
+			try {
+				this.rs.moveToInsertRow();
+				fail("ResultSet.moveToInsertRow() should not succeed on query that does not select all primary keys");
+			} catch (NotUpdatable noUpdate) {
+				// ignore
+			}
+		} finally {
+			if (scrollableStmt != null) {
+				try {
+					scrollableStmt.close();
+				} catch (SQLException sqlEx) {
+					// ignore
+				}
+			}
+
+			this.stmt.executeUpdate("DROP TABLE IF EXISTS MULTI_UPDATABLE");
+		}
+	}
+
+	/**
+	 * DOCUMENT ME!
+	 * 
+	 * @throws SQLException
+	 *             DOCUMENT ME!
+	 */
+	public void testUpdatability() throws SQLException {
+		Statement scrollableStmt = null;
+
+		try {
+			scrollableStmt = this.conn.createStatement(
+					ResultSet.TYPE_SCROLL_INSENSITIVE,
+					ResultSet.CONCUR_UPDATABLE);
+			this.rs = scrollableStmt
+					.executeQuery("SELECT * FROM UPDATABLE ORDER BY pos1");
+
+			this.rs.getMetaData().getColumnCount();
+
+			while (this.rs.next()) {
+				int rowPos = this.rs.getInt(1);
+				this.rs.updateString(3, "New Data" + (100 - rowPos));
+				this.rs.updateRow();
+			}
+
+			//
+			// Insert a new row
+			//
+			this.rs.moveToInsertRow();
+			this.rs.updateInt(1, 400);
+			this.rs.updateInt(2, 400);
+			this.rs.updateString(3, "New Data" + (100 - 400));
+			this.rs.insertRow();
+
+			// Test moveToCurrentRow
+			int rememberedPosition = this.rs.getRow();
+			this.rs.moveToInsertRow();
+			this.rs.moveToCurrentRow();
+			assertTrue("ResultSet.moveToCurrentRow() failed",
+					this.rs.getRow() == rememberedPosition);
+			this.rs.close();
+			this.rs = scrollableStmt
+					.executeQuery("SELECT * FROM UPDATABLE ORDER BY pos1");
+
+			boolean dataGood = true;
+
+			while (this.rs.next()) {
+				int rowPos = this.rs.getInt(1);
+
+				if (!this.rs.getString(3).equals("New Data" + (100 - rowPos))) {
+					dataGood = false;
+				}
+			}
+
+			assertTrue("Updates failed", dataGood);
+
+			// move back, and change the primary key
+			// This should work
+			int newPrimaryKeyId = 99999;
+			this.rs.absolute(1);
+			this.rs.updateInt(1, newPrimaryKeyId);
+			this.rs.updateRow();
+
+			int savedPrimaryKeyId = this.rs.getInt(1);
+			assertTrue("Updated primary key does not match",
+					(newPrimaryKeyId == savedPrimaryKeyId));
+
+			// Check cancelRowUpdates()
+			this.rs.absolute(1);
+
+			int primaryKey = this.rs.getInt(1);
+			int originalValue = this.rs.getInt(2);
+			this.rs.updateInt(2, -3);
+			this.rs.cancelRowUpdates();
+
+			int newValue = this.rs.getInt(2);
+			assertTrue("ResultSet.cancelRowUpdates() failed",
+					newValue == originalValue);
+
+			// Now check refreshRow()
+			// Check cancelRowUpdates()
+			this.rs.absolute(1);
+			primaryKey = this.rs.getInt(1);
+			this.stmt
+					.executeUpdate("UPDATE UPDATABLE SET char_field='foo' WHERE pos1="
+							+ primaryKey);
+			this.rs.refreshRow();
+			assertTrue("ResultSet.refreshRow failed", this.rs.getString(
+					"char_field").equals("foo"));
+
+			// Now check deleteRow()
+			this.rs.last();
+
+			int oldLastRow = this.rs.getRow();
+			this.rs.deleteRow();
+			this.rs.last();
+			assertTrue("ResultSet.deleteRow() failed",
+					this.rs.getRow() == (oldLastRow - 1));
+			this.rs.close();
+
+			/*
+			 * FIXME: Move to regression
+			 * 
+			 * scrollableStmt.executeUpdate("DROP TABLE IF EXISTS test");
+			 * scrollableStmt.executeUpdate("CREATE TABLE test (ident INTEGER
+			 * PRIMARY KEY, name TINYTEXT, expiry DATETIME default null)");
+			 * scrollableStmt.executeUpdate("INSERT INTO test SET ident=1,
+			 * name='original'");
+			 * 
+			 * //Select to get a resultset to work on ResultSet this.rs =
+			 * this.stmt.executeQuery("SELECT ident, name, expiry FROM test");
+			 * 
+			 * //Check that the expiry field was null before we did our update
+			 * this.rs.first();
+			 * 
+			 * java.sql.Date before = this.rs.getDate("expiry");
+			 * 
+			 * if (this.rs.wasNull()) { System.out.println("Expiry was correctly
+			 * SQL null before update"); }
+			 * 
+			 * //Update a different field this.rs.updateString("name",
+			 * "Updated"); this.rs.updateRow();
+			 * 
+			 * //Test to see if field has been altered java.sql.Date after =
+			 * this.rs.getDate(3);
+			 * 
+			 * if (this.rs.wasNull()) System.out.println("Bug disproved - expiry
+			 * SQL null after update"); else System.out.println("Bug proved -
+			 * expiry corrupted to '" + after + "'");
+			 */
+		} finally {
+			if (scrollableStmt != null) {
+				try {
+					scrollableStmt.close();
+				} catch (SQLException sqlEx) {
+					;
+				}
+			}
+		}
+	}
+
+	private void createTestTable() throws SQLException {
+		//
+		// Catch the error, the table might exist
+		//
+		try {
+			this.stmt.executeUpdate("DROP TABLE UPDATABLE");
+		} catch (SQLException SQLE) {
+			;
+		}
+
+		this.stmt
+				.executeUpdate("CREATE TABLE UPDATABLE (pos1 int not null, pos2 int not null, char_field VARCHAR(32), PRIMARY KEY (pos1, pos2))");
+
+		for (int i = 0; i < 100; i++) {
+			this.stmt.executeUpdate("INSERT INTO UPDATABLE VALUES (" + i + ", "
+					+ i + ",'StringData" + i + "')");
+		}
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/XATest.java
===================================================================
--- branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/XATest.java	2006-12-21 13:06:06 UTC (rev 2879)
+++ branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/XATest.java	2006-12-22 10:43:08 UTC (rev 2880)
@@ -0,0 +1,475 @@
+/*
+ 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 testsuite.simple;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.rmi.server.UID;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Savepoint;
+
+import javax.sql.XAConnection;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+
+import com.mysql.jdbc.jdbc2.optional.MysqlXADataSource;
+import com.mysql.jdbc.jdbc2.optional.MysqlXid;
+
+import testsuite.BaseTestCase;
+
+/**
+ * Unit tests for our XA implementation.
+ * 
+ * @version $Id: $
+ */
+public class XATest extends BaseTestCase {
+    MysqlXADataSource xaDs;
+    
+	public XATest(String name) {
+		super(name);
+		
+		this.xaDs = new MysqlXADataSource();
+		this.xaDs.setUrl(BaseTestCase.dbUrl);
+		this.xaDs.setRollbackOnPooledClose(true);
+	}
+
+	/**
+	 * Tests that simple distributed transaction processing works as expected.
+	 * 
+	 * @throws Exception
+	 *             if the test fails.
+	 */
+	public void testCoordination() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+		
+		createTable("testCoordination", "(field1 int) ENGINE=InnoDB");
+		
+		Connection conn1 = null;
+		Connection conn2 = null;
+		XAConnection xaConn1 = null;
+		XAConnection xaConn2 = null;
+		
+		try {
+			xaConn1 = getXAConnection();
+			XAResource xaRes1 = xaConn1.getXAResource();
+			conn1 = xaConn1.getConnection();
+			
+			xaConn2 = getXAConnection();
+			XAResource xaRes2 = xaConn2.getXAResource();
+			conn2 = xaConn2.getConnection();
+			
+			Xid xid1 = createXid();
+			Xid xid2 = createXid(xid1);
+			
+			xaRes1.start(xid1, XAResource.TMNOFLAGS);
+			xaRes2.start(xid2, XAResource.TMNOFLAGS);
+			conn1.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (1)");
+			conn2.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (2)");
+			xaRes1.end(xid1, XAResource.TMSUCCESS);
+			xaRes2.end(xid2, XAResource.TMSUCCESS);
+			
+			xaRes1.prepare(xid1);
+			xaRes2.prepare(xid2);
+			
+			xaRes1.commit(xid1, false);
+			xaRes2.commit(xid2, false);
+			
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testCoordination ORDER BY field1");
+			
+			assertTrue(this.rs.next());
+			assertEquals(1, this.rs.getInt(1));
+			
+			assertTrue(this.rs.next());
+			assertEquals(2, this.rs.getInt(1));
+			
+			this.stmt.executeUpdate("TRUNCATE TABLE testCoordination");
+			
+			//
+			// Now test rollback
+			//
+			
+			xid1 = createXid();
+			xid2 = createXid(xid1);
+			
+			xaRes1.start(xid1, XAResource.TMNOFLAGS);
+			xaRes2.start(xid2, XAResource.TMNOFLAGS);
+			conn1.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (1)");
+			
+			// ensure visibility
+			assertEquals("1", getSingleIndexedValueWithQuery(conn1, 1, "SELECT field1 FROM testCoordination WHERE field1=1").toString());
+			
+			conn2.createStatement().executeUpdate("INSERT INTO testCoordination VALUES (2)");
+			
+			// ensure visibility
+			assertEquals("2", getSingleIndexedValueWithQuery(conn2, 1, "SELECT field1 FROM testCoordination WHERE field1=2").toString());
+			
+			xaRes1.end(xid1, XAResource.TMSUCCESS);
+			xaRes2.end(xid2, XAResource.TMSUCCESS);
+			
+			xaRes1.prepare(xid1);
+			xaRes2.prepare(xid2);
+			
+			xaRes1.rollback(xid1);
+			xaRes2.rollback(xid2);
+			
+			this.rs = this.stmt.executeQuery("SELECT field1 FROM testCoordination ORDER BY field1");
+			
+			assertTrue(!this.rs.next());
+		} finally {
+			if (conn1 != null) {
+				conn1.close();
+			}
+			
+			if (conn2 != null) {
+				conn2.close();
+			}
+			
+			if (xaConn1 != null) {
+				xaConn1.close();
+			}
+			
+			if (xaConn2 != null) {
+				xaConn2.close();
+			}
+		}
+	}
+	
+	protected XAConnection getXAConnection() throws Exception {
+		return this.xaDs.getXAConnection();
+	}
+	
+	/**
+	 * Tests that XA RECOVER works as expected.
+	 * 
+	 * @throws Exception
+	 *             if test fails
+	 */
+	public void testRecover() throws Exception {
+		if (!versionMeetsMinimum(5, 0)) {
+			return;
+		}
+
+		XAConnection xaConn = null, recoverConn = null;
+		
+		try {			
+			xaConn = getXAConnection();
+			
+			Connection c = xaConn.getConnection();
+			Xid xid = createXid();
+			
+			XAResource xaRes = xaConn.getXAResource();
+			xaRes.start(xid, XAResource.TMNOFLAGS);
+			c.createStatement().executeQuery("SELECT 1");
+			xaRes.end(xid, XAResource.TMSUCCESS);
+			xaRes.prepare(xid);
+			
+			// Now try and recover
+			recoverConn = getXAConnection();
+			
+			XAResource recoverRes = recoverConn.getXAResource();
+			
+			Xid[] recoveredXids = recoverRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
+			
+			assertTrue(recoveredXids != null);
+			assertTrue(recoveredXids.length > 0);
+			
+			boolean xidFound = false;
+			
+			for (int i = 0; i < recoveredXids.length; i++) {
+				if (recoveredXids[i] != null &&
+					recoveredXids[i].equals(xid)) {
+					xidFound = true;
+					
+					break;
+				}
+			}
+			
+			assertTrue(xidFound);
+
+			recoverRes = recoverConn.getXAResource();
+
+			recoveredXids = recoverRes.recover(XAResource.TMSTARTRSCAN);
+
+			assertTrue(recoveredXids != null);
+			assertTrue(recoveredXids.length > 0);
+
+			xidFound = false;
+
+			for (int i = 0; i < recoveredXids.length; i++) {
+				if (recoveredXids[i] != null &&
+						recoveredXids[i].equals(xid)) {
+					xidFound = true;
+
+					break;
+				}
+			}
+
+			assertTrue(xidFound);
+				
+			// Test flags
+			recoverRes.recover(XAResource.TMSTARTRSCAN);
+			recoverRes.recover(XAResource.TMENDRSCAN);
+			recoverRes.recover(XAResource.TMSTARTRSCAN | XAResource.TMENDRSCAN);
+			
+			// This should fail
+			try {
+				recoverRes.recover(XAResource.TMSUCCESS);
+				fail("XAException should have been thrown");
+			} catch (XAException xaEx) {
+				assertEquals(XAException.XAER_INVAL, xaEx.errorCode);
+			}
+		} finally {
+			if (xaConn != null) {
+				xaConn.close();
+			}
+			
+			if (recoverConn != null) {
+				recoverConn.close();
+			}
+		}
+	}
+
+	/**
+	 * Tests operation of local transactions on XAConnections when global
+	 * transactions are in or not in progress (follows from BUG#17401).
+	 * 
+	 * @throws Exception
+	 *             if the testcase fails
+	 */
+	public void testLocalTransaction() throws Exception {
+
+		if (!versionMeetsMinimum(5, 0) || isRunningOnJdk131()) {
+			return;
+		}
+
+		createTable("testLocalTransaction", "(field1 int) ENGINE=InnoDB");
+
+		Connection conn1 = null;
+
+		XAConnection xaConn1 = null;
+
+		try {
+			xaConn1 = getXAConnection();
+			XAResource xaRes1 = xaConn1.getXAResource();
+			conn1 = xaConn1.getConnection();
+			assertEquals(false, conn1.getAutoCommit());
+			conn1.setAutoCommit(true);
+			conn1.createStatement().executeUpdate(
+					"INSERT INTO testLocalTransaction VALUES (1)");
+			assertEquals("1", getSingleIndexedValueWithQuery(conn1, 1,
+					"SELECT field1 FROM testLocalTransaction").toString());
+
+			conn1.createStatement().executeUpdate(
+					"TRUNCATE TABLE testLocalTransaction");
+			conn1.setAutoCommit(false);
+			conn1.createStatement().executeUpdate(
+					"INSERT INTO testLocalTransaction VALUES (2)");
+			assertEquals("2", getSingleIndexedValueWithQuery(conn1, 1,
+					"SELECT field1 FROM testLocalTransaction").toString());
+			conn1.rollback();
+			assertEquals(0, getRowCount("testLocalTransaction"));
+
+			conn1.createStatement().executeUpdate(
+					"INSERT INTO testLocalTransaction VALUES (3)");
+			assertEquals("3", getSingleIndexedValueWithQuery(conn1, 1,
+					"SELECT field1 FROM testLocalTransaction").toString());
+			conn1.commit();
+			assertEquals("3", getSingleIndexedValueWithQuery(conn1, 1,
+					"SELECT field1 FROM testLocalTransaction").toString());
+			conn1.commit();
+			
+			Savepoint sp = conn1.setSavepoint();
+			conn1.rollback(sp);
+			sp = conn1.setSavepoint("abcd");
+			conn1.rollback(sp);
+			Savepoint spSaved = sp;
+			
+			Xid xid = createXid();
+			xaRes1.start(xid, XAResource.TMNOFLAGS);
+
+			try {
+				try {
+					conn1.setAutoCommit(true);
+				} catch (SQLException sqlEx) {
+					// we expect an exception here
+					assertEquals("2D000", sqlEx.getSQLState());
+				}
+
+				try {
+					conn1.commit();
+				} catch (SQLException sqlEx) {
+					// we expect an exception here
+					assertEquals("2D000", sqlEx.getSQLState());
+				}
+
+				try {
+					conn1.rollback();
+				} catch (SQLException sqlEx) {
+					// we expect an exception here
+					assertEquals("2D000", sqlEx.getSQLState());
+				}
+				
+				try {
+					sp = conn1.setSavepoint();
+				} catch (SQLException sqlEx) {
+					// we expect an exception here
+					assertEquals("2D000", sqlEx.getSQLState());
+				}
+			
+				try {
+					conn1.rollback(spSaved);
+				} catch (SQLException sqlEx) {
+					// we expect an exception here
+					assertEquals("2D000", sqlEx.getSQLState());
+				}
+				
+				try {
+					sp = conn1.setSavepoint("abcd");
+				} catch (SQLException sqlEx) {
+					// we expect an exception here
+					assertEquals("2D000", sqlEx.getSQLState());
+				}
+				
+				try {
+					conn1.rollback(spSaved);
+				} catch (SQLException sqlEx) {
+					// we expect an exception here
+					assertEquals("2D000", sqlEx.getSQLState());
+				}
+			} finally {
+				xaRes1.forget(xid);
+			}
+		} finally {
+			if (xaConn1 != null) {
+				xaConn1.close();
+			}
+		}
+	}
+	
+	public void testSuspendableTx() throws Exception {
+		if (!versionMeetsMinimum(5, 0) || isRunningOnJdk131()) {
+			return;
+		}
+		
+		Connection conn1 = null;
+
+		MysqlXADataSource suspXaDs = new MysqlXADataSource();
+		suspXaDs.setUrl(BaseTestCase.dbUrl);
+		suspXaDs.setPinGlobalTxToPhysicalConnection(true);
+		suspXaDs.setRollbackOnPooledClose(true);
+		
+		XAConnection xaConn1 = null;
+		
+		Xid xid = createXid();
+		
+		try {
+			/*
+			  	-- works using RESUME
+				xa start 0x123,0x456;
+				select * from foo;
+				xa end 0x123,0x456;
+				xa start 0x123,0x456 resume;
+				select * from foo;
+				xa end 0x123,0x456;
+				xa commit 0x123,0x456 one phase;
+			 */
+			
+			xaConn1 = suspXaDs.getXAConnection();
+			XAResource xaRes1 = xaConn1.getXAResource();
+			conn1 = xaConn1.getConnection();
+			xaRes1.start(xid, XAResource.TMNOFLAGS);
+			conn1.createStatement().executeQuery("SELECT 1");
+			xaRes1.end(xid, XAResource.TMSUCCESS);
+			xaRes1.start(xid, XAResource.TMRESUME);
+			conn1.createStatement().executeQuery("SELECT 1");
+			xaRes1.end(xid, XAResource.TMSUCCESS);
+			xaRes1.commit(xid, true);
+			
+			xaConn1.close();
+			
+			/*
+
+				-- fails using JOIN
+				xa start 0x123,0x456;
+				select * from foo;
+				xa end 0x123,0x456;
+				xa start 0x123,0x456 join;
+				select * from foo;
+				xa end 0x123,0x456;
+				xa commit 0x123,0x456 one phase;
+				*/
+		
+			xaConn1 = suspXaDs.getXAConnection();
+			xaRes1 = xaConn1.getXAResource();
+			conn1 = xaConn1.getConnection();
+			xaRes1.start(xid, XAResource.TMNOFLAGS);
+			conn1.createStatement().executeQuery("SELECT 1");
+			xaRes1.end(xid, XAResource.TMSUCCESS);
+			xaRes1.start(xid, XAResource.TMJOIN);
+			conn1.createStatement().executeQuery("SELECT 1");
+			xaRes1.end(xid, XAResource.TMSUCCESS);
+			xaRes1.commit(xid, true);
+		} finally {
+			if (xaConn1 != null) {
+				xaConn1.close();
+			}
+		}
+	}
+
+	private Xid createXid() throws IOException {
+		ByteArrayOutputStream gtridOut = new ByteArrayOutputStream();
+		DataOutputStream dataOut = new DataOutputStream(gtridOut);
+		new UID().write(dataOut);
+		
+		final byte[] gtrid = gtridOut.toByteArray();
+		
+		ByteArrayOutputStream bqualOut = new ByteArrayOutputStream();
+		dataOut = new DataOutputStream(bqualOut);
+		
+		new UID().write(dataOut);
+		
+		final byte[] bqual = bqualOut.toByteArray();
+		
+		Xid xid = new MysqlXid(gtrid, bqual, 3306);
+		return xid;
+	}
+
+	private Xid createXid(Xid xidToBranch) throws IOException {
+		ByteArrayOutputStream bqualOut = new ByteArrayOutputStream();
+		DataOutputStream dataOut = new DataOutputStream(bqualOut);
+		
+		new UID().write(dataOut);
+		
+		final byte[] bqual = bqualOut.toByteArray();
+		
+		Xid xid = new MysqlXid(xidToBranch.getGlobalTransactionId(), bqual, 3306);
+		
+		return xid;
+	}
+}

Added: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/tb2-data.txt.gz
===================================================================
(Binary files differ)


Property changes on: branches/mysql-connector-java/upstream/5.0.4/src/testsuite/simple/tb2-data.txt.gz
___________________________________________________________________
Name: svn:mime-type
   + application/octet-stream




More information about the pkg-java-commits mailing list