diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..95832e8e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/build/ +/dist/ \ No newline at end of file diff --git a/Delete/delete0_DataHandler.java b/Delete/delete0_DataHandler.java new file mode 100644 index 00000000..e002f70f --- /dev/null +++ b/Delete/delete0_DataHandler.java @@ -0,0 +1,26 @@ +// Bagian dari DataHandler class + +//Method untuk mengidentifikasi record post +//pilih/set data (yg nanti akan dihapus) + + public Post findPostById(int id) throws SQLException { } + + Post selectedPst = new Post(); + + getDBConnection(); + + stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); + query = "SELECT * FROM post_table WHERE post_id = " + id; + System.out.println("\nExecuting: " + query); + + rset = stmt.executeQuery(query); + + while (rset.next()) { + selectedPst.setPostId(new Integer(rset.getInt("post_id"))); + selectedPst.setTitle(rset.getString("title")); + selectedPst.setDate(rset.getString("date")); + selectedPst.setContent(rset.getString("content")); + } + + return selectedPst; + diff --git a/Delete/delete1_DataHandler.java b/Delete/delete1_DataHandler.java new file mode 100644 index 00000000..c54965d1 --- /dev/null +++ b/Delete/delete1_DataHandler.java @@ -0,0 +1,20 @@ +//Delete +//DataHandler.java + +//Method untuk men-delete data + + +public class DataHandler { + public String deletePostById(int id) throws SQLException { } + + getDBConnection(); + + stmt = conn.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY); + sqlString = "DELETE FROM post_table WHERE post_id = " + id; + System.out.println("\nExecuting: " + sqlString); + + stmt.execute(sqlString); + + return "success"; + +} diff --git a/Delete/delete2_idx.jsp b/Delete/delete2_idx.jsp new file mode 100644 index 00000000..e09b2836 --- /dev/null +++ b/Delete/delete2_idx.jsp @@ -0,0 +1,15 @@ +//Bagian dari index.jsp + +//Menambahkan link ke 'page' delete data + + + while (rset.next ()) { + out.println( + rset.getString("title") + + rset.getString("date") + + rset.getString("content") + + Edit + Delete"); + ); + } + diff --git a/Delete/delete3_action.jsp b/Delete/delete3_action.jsp new file mode 100644 index 00000000..ec5920ca --- /dev/null +++ b/Delete/delete3_action.jsp @@ -0,0 +1,17 @@ +//delete_action.jsp + +//Halaman JSP untuk meng-handle aksi delete + + +//Add a jsp:usebean tag. +//As before, enterempsbean as the ID, and hr.DataHandler as the Class. +//Set the Scope to session, and click OK. + +//Add a Scriptlet to the page. Enter the following code into the Insert Scriptlet dialog box: + +Integer post_id = new Integer(request.getParameter("pstid")); +PostBean.deletePostById(post_id.intValue()); + +//Drag Forward from the Component Palette to add a jsp:forward tag to the page. +//In the Insert Forward dialog box, enter employees.jsp. + diff --git a/Login/InvalidLogin.jsp.jsp b/Login/InvalidLogin.jsp.jsp new file mode 100644 index 00000000..db771477 --- /dev/null +++ b/Login/InvalidLogin.jsp.jsp @@ -0,0 +1,32 @@ + + + +<%@ page language="java" + contentType="text/html; charset=windows-1256" + pageEncoding="windows-1256" + %> + <% + response.setHeader("Cache-Control","no-store,must-revalidate"); + response.setHeader("Pragma","no-cache"); + response.setDateHeader ("Expires", -1); + new java.util.Date(); + %> + + + + + +
+ ++ 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, + Spring's JDBC + templates or Ibatis + SQL Maps 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 — A tutorial from Sun covering beginner + topics in JDBC +
+ JDBC + Short Course — A more in-depth tutorial from Sun + and JGuru +
+ Key topics: +
+ For help with connection strings, connection options setting up + your connection through JDBC, see + Section 1.4.1, “Driver/Datasource Class Names, URL Syntax and Configuration Properties + for Connector/J”. +
+ For tips on using Connector/J and JDBC with generic J2EE + toolkits, see Section 1.5.2, “Using Connector/J with J2EE and Other Java Frameworks”. +
+ Developers using the Tomcat server platform, see + Section 1.5.2.2, “Using Connector/J with Tomcat”. +
+ Developers using JBoss, see + Section 1.5.2.3, “Using Connector/J with JBoss”. +
+ Developers using Spring, see + Section 1.5.2.4, “Using Connector/J with Spring”. +
MySQL Enterprise + MySQL Enterprise subscribers will find more information about + using JDBC with MySQL in the Knowledge Base articles about + + JDBC. Access to the MySQL Knowledge Base collection of + articles is one of the advantages of subscribing to MySQL + Enterprise. For more information see + http://www.mysql.com/products/enterprise/advisors.html. +
+ There are currently four versions of MySQL Connector/J available: +
+ Connector/J 5.1 is current in alpha status. It provides
+ compatibility with all the functionality of MySQL, including
+ 4.1, 5.0, 5.1 and the 6.0 alpha release featuring the new
+ Falcon storage engine. Connector/J 5.1 provides ease of
+ development features, including auto-registration with the
+ Driver Manager, standardized validity checks, categorized
+ SQLExceptions, support for the JDBC-4.0 XML processing, per
+ connection client information, NCHAR
,
+ NVARCHAR
and NCLOB
+ types. This release also includes all bug fixes up to and
+ including Connector/J 5.0.6.
+
+ Connector/J 5.0 provides support for all the functionality + offered by Connector/J 3.1 and includes distributed + transaction (XA) support. +
+ Connector/J 3.1 was designed for connectivity to MySQL 4.1 and + MySQL 5.0 servers and provides support for all the + functionality in MySQL 5.0 except distributed transaction (XA) + support. +
+ Connector/J 3.0 provides core functionality and was designed + with connectivity to MySQL 3.x or MySQL 4.1 servers, although + it will provide basic compatibility 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. +
+ 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. +
+ 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 1.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.
+
+ 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 either solution, you must manually add the Connector/J
+ location to your Java CLASSPATH
.
+
+ 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 1.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-
,
+ and starting with Connector/J 3.1.8 a debug build of the driver
+ in a file named
+ [version]
-bin.jarmysql-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 or a bug to MySQL
+ AB, as it is not designed to be run in production environments,
+ and will have adverse performance impact when used. The debug
+ binary also depends on the Aspect/J runtime library, which is
+ located in the src/lib/aspectjrt.jar
file
+ that comes with the Connector/J distribution.
+
+ You will need to use the appropriate graphical or command-line + utility to extract the distribution (for example, WinZip for the + .zip archive, and tar for the .tar.gz + archive). Because there are potentially long filenames in the + distribution, we use the GNU tar archive format. You will need + to use GNU tar (or an application that understands the GNU tar + archive format) to unpack the .tar.gz variant of the + distribution. +
+ 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
+ variable under UNIX, Linux or Mac OS X either locally for a user
+ within their .profile
,
+ .login
or other login file. You can also set
+ it globally by editing the global
+ /etc/profile
file.
+
+ For example, under a C shell (csh, tcsh) you would add the
+ Connector/J driver to your CLASSPATH
using
+ the following:
+
shell> setenv CLASSPATH /path/mysql-connector-java-[ver]-bin.jar:$CLASSPATH
+ Or with a Bourne-compatible shell (sh, ksh, bash): +
export set CLASSPATH=/path/mysql-connector-java-[ver]-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 1.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 +
+ 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. +
+ 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
+ Character Set Support, 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-
+ is shipped alongside the normal binary jar file that is
+ named
+ [version]
-bin-g.jarmysql-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.
+
+ 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
+
+ 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: +
+ 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]
_[minor]
/connector-j
+ This creates a connector-j
subdirectory
+ in the current directory that contains the latest sources
+ for the requested branch.
+
+ Change location to the connector-j
+ directory to make it your current working directory:
+
shell> cd connector-j
+ 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
+ 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 1.2.2, “Installing the Driver and Configuring the CLASSPATH
”.
+
+ Examples of using Connector/J are located throughout this + document, this section provides a summary and links to these + examples. +
+ Example 1, “Obtaining a connection from the DriverManager
”
+
+ Example 2, “Using java.sql.Statement to execute a SELECT
query”
+
+ Example 7, “Retrieving results and output parameter values” +
+ Example 8, “Retrieving AUTO_INCREMENT
column values using
+ Statement.getGeneratedKeys()
”
+
+ Example 9, “Retrieving AUTO_INCREMENT
column values using
+ SELECT LAST_INSERT_ID()
”
+
+ Example 10, “Retrieving AUTO_INCREMENT
column values in
+ Updatable ResultSets
”
+
+ Example 11, “Using a connection pool with a J2EE application server” +
+ This section of the manual contains reference material for MySQL + Connector/J, some of which is automatically generated during the + Connector/J build process. +
+ 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.MysqlConnectionPoolDataSource +
+ 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.
+
+ If the mechanism you use to configure a JDBC URL is + XML-based, you will need to use the XML character literal + & 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 versions | |
password | The password to use when connecting | all versions | |
socketFactory | The name of the class that the driver should use for creating socket + connections to the server. This class must implement the + interface 'com.mysql.jdbc.SocketFactory' 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 |
connectionLifecycleInterceptors | A comma-delimited list of classes that implement + "com.mysql.jdbc.ConnectionLifecycleInterceptor" that + should notified of connection lifecycle events + (creation, destruction, commit, rollback, setCatalog and + setAutoCommit) and potentially alter the execution of + these commands. ConnectionLifecycleInterceptors are + "stackable", more than one interceptor may be specified + via the configuration property as a comma-delimited + list, with the interceptors executed in order from left + to right. | 5.1.4 | |
useConfigs | Load the comma-delimited list of configuration properties before parsing + the URL or applying user-specified properties. These + configurations are explained in the 'Configurations' of + the documentation. | 3.1.5 | |
interactiveClient | Set the CLIENT_INTERACTIVE flag, which tells MySQL to timeout + connections based on INTERACTIVE_TIMEOUT instead of + WAIT_TIMEOUT | false | 3.1.0 |
localSocketAddress | Hostname or IP address given to explicitly configure the interface that + the driver will bind the client side of the TCP/IP + connection to when connecting. | 5.0.5 | |
propertiesTransform | An implementation of com.mysql.jdbc.ConnectionPropertiesTransform that + the driver will use to modify URL properties passed to + the driver before attempting a connection | 3.1.4 | |
useCompression | Use zlib compression when communicating with the server (true/false)? + Defaults to 'false'. | false | 3.0.17 |
+
Networking. +
Property Name | Definition | Default Value | Since Version |
tcpKeepAlive | If connecting using TCP/IP, should the driver set SO_KEEPALIVE? | true | 5.0.7 |
tcpNoDelay | If connecting using TCP/IP, should the driver set SO_TCP_NODELAY + (disabling the Nagle Algorithm)? | true | 5.0.7 |
tcpRcvBuf | If connecting using TCP/IP, should the driver set SO_RCV_BUF to the + given value? The default value of '0', means use the + platform default value for this property) | 0 | 5.0.7 |
tcpSndBuf | If connecting using TCP/IP, shuold the driver set SO_SND_BUF to the + given value? The default value of '0', means use the + platform default value for this property) | 0 | 5.0.7 |
tcpTrafficClass | If connecting using TCP/IP, should the driver set traffic class or + type-of-service fields ?See the documentation for + java.net.Socket.setTrafficClass() for more information. | 0 | 5.0.7 |
+
High Availability and Clustering. +
Property Name | Definition | Default Value | Since Version |
autoReconnect | Should the driver try to re-establish stale 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. | false | 1.1 |
autoReconnectForPools | Use a reconnection strategy appropriate for connection pools (defaults + to 'false') | false | 3.1.3 |
failOverReadOnly | When failing over in autoReconnect mode, should the connection be set to + 'read-only'? | true | 3.0.12 |
maxReconnects | Maximum number of reconnects to attempt if autoReconnect is true, + default is '3'. | 3 | 1.1 |
reconnectAtTxEnd | If autoReconnect is set to true, should the driver attempt reconnections + at the end of every transaction? | false | 3.0.10 |
initialTimeout | If autoReconnect is enabled, the initial time to wait between re-connect + attempts (in seconds, defaults to '2'). | 2 | 1.1 |
roundRobinLoadBalance | When autoReconnect is enabled, and failoverReadonly is false, should we + pick hosts to connect to on a round-robin basis? | false | 3.1.2 |
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 | 30 | 3.0.2 |
resourceId | A globally unique name that identifies the resource that this datasource + or connection is connected to, used for + XAResource.isSameRM() when the driver can't determine + this value based on hostnames used in the URL | 5.0.1 |
+
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 |
allowLoadLocalInfile | Should the driver allow use of 'LOAD DATA LOCAL INFILE...' (defaults to + 'true'). | true | 3.0.3 |
allowUrlInLocalInfile | Should the driver allow URLs in 'LOAD DATA LOCAL INFILE' statements? | false | 3.1.4 |
clientCertificateKeyStorePassword | Password for the client certificates KeyStore | 5.1.0 | |
clientCertificateKeyStoreType | KeyStore type for client certificates (NULL or empty means use default, + standard keystore types supported by the JVM are "JKS" + and "PKCS12", your environment may have more available + depending on what security products are installed and + available to the JVM. | 5.1.0 | |
clientCertificateKeyStoreUrl | URL to the client certificate KeyStore (if not specified, use defaults) | 5.1.0 | |
trustCertificateKeyStorePassword | Password for the trusted root certificates KeyStore | 5.1.0 | |
trustCertificateKeyStoreType | KeyStore type for trusted root certificates (NULL or empty means use + default, standard keystore types supported by the JVM + are "JKS" and "PKCS12", your environment may have more + available depending on what security products are + installed and available to the JVM. | 5.1.0 | |
trustCertificateKeyStoreUrl | URL to the trusted root certificate KeyStore (if not specified, use + defaults) | 5.1.0 | |
paranoid | Take measures to prevent exposure sensitive information in error + messages and clear data structures holding sensitive + data when possible? (defaults to 'false') | false | 3.0.1 |
+
Performance Extensions. +
Property Name | Definition | Default Value | Since Version |
callableStmtCacheSize | If 'cacheCallableStmts' is enabled, how many callable statements should + be cached? | 100 | 3.1.2 |
metadataCacheSize | The number of queries to cache ResultSetMetadata for if + cacheResultSetMetaData is set to 'true' (default 50) | 50 | 3.1.1 |
prepStmtCacheSize | If prepared statement caching is enabled, how many prepared statements + should be cached? | 25 | 3.0.10 |
prepStmtCacheSqlLimit | If prepared statement caching is enabled, what's the largest SQL the + driver will cache the parsing for? | 256 | 3.0.10 |
alwaysSendSetIsolation | Should the driver always communicate with the database when + Connection.setTransactionIsolation() is called? If set + to false, the driver will only communicate with the + database when the requested transaction isolation is + different than the whichever is newer, the last value + that was set via Connection.setTransactionIsolation(), + or the value that was read from the server when the + connection was established. | true | 3.1.7 |
maintainTimeStats | Should the driver maintain various internal timers to enable idle time + calculations as well as more verbose error messages when + the connection to the server fails? Setting this + property to false removes at least two calls to + System.getCurrentTimeMillis() per query. | true | 3.1.9 |
useCursorFetch | If connected to MySQL > 5.0.2, and setFetchSize() > 0 on a + statement, should that statement use cursor-based + fetching to retrieve rows? | false | 5.0.0 |
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 |
enableQueryTimeouts | When enabled, query timeouts set via Statement.setQueryTimeout() use a + shared java.util.Timer instance for scheduling. Even if + the timeout doesn't expire before the query is + processed, there will be memory used by the TimerTask + for the given timeout which won't be reclaimed until the + time the timeout would have expired if it hadn't been + cancelled by the driver. High-load environments might + want to consider disabling this functionality. | true | 5.0.6 |
holdResultsOpenOverStatementClose | Should the driver close result sets on Statement.close() as required by + the JDBC specification? | false | 3.1.7 |
largeRowSizeThreshold | What size result set row should the JDBC driver consider "large", and + thus use a more memory-efficient way of representing the + row internally? | 2048 | 5.1.1 |
loadBalanceStrategy | If using a load-balanced connection to connect to SQL nodes in a MySQL + Cluster/NDB configuration (by using the URL prefix + "jdbc:mysql:loadbalance://"), which load balancing + algorithm should the driver use: (1) "random" - the + driver will pick a random host for each request. This + tends to work better than round-robin, as the randomness + will somewhat account for spreading loads where requests + vary in response time, while round-robin can sometimes + lead to overloaded nodes if there are variations in + response times across the workload. (2) + "bestResponseTime" - the driver will route the request + to the host that had the best response time for the + previous transaction. | random | 5.0.6 |
locatorFetchBufferSize | If 'emulateLocators' is configured to 'true', what size buffer should be + used when fetching BLOB data for getBinaryInputStream? | 1048576 | 3.2.1 |
rewriteBatchedStatements | Should the driver use multiqueries (irregardless of the setting of + "allowMultiQueries") as well as rewriting of prepared + statements for INSERT into multi-value inserts when + executeBatch() is called? Notice that this has the + potential for SQL injection if using plain + java.sql.Statements and your code doesn't sanitize input + correctly. Notice that for prepared statements, + server-side prepared statements can not currently take + advantage of this rewrite option, and that if you don't + specify stream lengths when using + PreparedStatement.set*Stream(), the driver won't be able + to determine the optimum number of parameters per batch + and you might receive an error from the driver that the + resultant packet is too large. + Statement.getGeneratedKeys() for these rewritten + statements only works when the entire batch includes + INSERT statements. | false | 3.1.13 |
useDirectRowUnpack | Use newer result set row unpacking code that skips a copy from network + buffers to a MySQL packet instance and instead reads + directly into the result set row data buffers. | true | 5.1.1 |
useDynamicCharsetInfo | Should the driver use a per-connection cache of character set + information queried from the server when necessary, or + use a built-in static mapping that is more efficient, + but isn't aware of custom character sets or character + sets implemented after the release of the JDBC driver? | true | 5.0.6 |
useFastDateParsing | Use internal String->Date/Time/Timestamp conversion routines to avoid + excessive object creation? | true | 5.0.5 |
useFastIntParsing | Use internal String->Integer conversion routines to avoid excessive + object creation? | true | 3.1.4 |
useJvmCharsetConverters | Always use the character encoding routines built into the JVM, rather + than using lookup tables for single-byte character sets? | false | 5.0.1 |
useLocalSessionState | Should the driver refer to the internal values of autocommit and + transaction isolation that are set by + Connection.setAutoCommit() and + Connection.setTransactionIsolation() and transaction + state as maintained by the protocol, rather than + querying the database or blindly sending commands to the + database for commit() or rollback() method calls? | false | 3.1.7 |
useReadAheadInput | Use newer, optimized non-blocking, buffered input stream when reading + from the server? | true | 3.1.5 |
+
Debugging/Profiling. +
Property Name | Definition | Default Value | Since Version |
logger | The name of a class that implements "com.mysql.jdbc.log.Log" that will + be used to log messages to. (default is + "com.mysql.jdbc.log.StandardLogger", which logs to + STDERR) | com.mysql.jdbc.log.StandardLogger | 3.1.1 |
gatherPerfMetrics | Should the driver gather performance metrics, and report them via the + configured logger every 'reportMetricsIntervalMillis' + milliseconds? | false | 3.1.2 |
profileSQL | Trace queries and their execution/fetch times to the configured logger + (true/false) defaults to 'false' | false | 3.1.0 |
profileSql | Deprecated, use 'profileSQL' instead. Trace queries and their + execution/fetch times on STDERR (true/false) defaults to + 'false' | 2.0.14 | |
reportMetricsIntervalMillis | If 'gatherPerfMetrics' is enabled, how often should they be logged (in + ms)? | 30000 | 3.1.2 |
maxQuerySizeToLog | Controls the maximum length/size of a query 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 |
slowQueryThresholdNanos | If 'useNanosForElapsedTime' is set to true, and this property is set to + a non-zero value, the driver will use this threshold (in + nanosecond units) to determine if a query was slow. | 0 | 5.0.7 |
useUsageAdvisor | Should the driver issue 'usage' warnings advising proper and efficient + usage of JDBC and MySQL Connector/J to the log + (true/false, defaults to 'false')? | 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 |
autoSlowLog | Instead of using slowQueryThreshold* to determine if a query is slow + enough to be logged, maintain statistics that allow the + driver to determine queries that are outside the 99th + percentile? | true | 5.1.4 |
clientInfoProvider | The name of a class that implements the + com.mysql.jdbc.JDBC4ClientInfoProvider interface in + order to support JDBC-4.0's + Connection.get/setClientInfo() methods | com.mysql.jdbc.JDBC4CommentClientInfoProvider | 5.1.0 |
dumpMetadataOnColumnNotFound | Should the driver dump the field-level metadata of a result set into the + exception message when ResultSet.findColumn() fails? | false | 3.1.13 |
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 |
includeInnodbStatusInDeadlockExceptions | Include the output of "SHOW ENGINE INNODB STATUS" in exception messages + when deadlock exceptions are detected? | false | 5.0.7 |
logSlowQueries | Should queries that take longer than 'slowQueryThresholdMillis' be + logged? | false | 3.1.2 |
logXaCommands | Should the driver log XA commands sent by MysqlXaConnection to the + server, at the DEBUG level of logging? | false | 5.0.5 |
resultSetSizeThreshold | If the usage advisor is enabled, how many rows should a result set + contain before the driver warns that it is suspiciously + large? | 100 | 5.0.5 |
traceProtocol | Should trace-level network protocol be logged? | false | 3.1.2 |
useNanosForElapsedTime | For profiling/debugging functionality that measures elapsed time, should + the driver try to use nanoseconds resolution if + available (JDK >= 1.5)? | false | 5.0.7 |
+
Miscellaneous. +
Property Name | Definition | Default Value | Since Version |
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 | |
useBlobToStoreUTF8OutsideBMP | Tells the driver to treat [MEDIUM/LONG]BLOB columns as [LONG]VARCHAR + columns holding text encoded in UTF-8 that has + characters outside the BMP (4-byte encodings), which + MySQL server can't handle natively. | false | 5.1.3 |
utf8OutsideBmpExcludedColumnNamePattern | When "useBlobToStoreUTF8OutsideBMP" is set to "true", column names + matching the given regex will still be treated as BLOBs + unless they match the regex specified for + "utf8OutsideBmpIncludedColumnNamePattern". The regex + must follow the patterns used for the java.util.regex + package. | 5.1.3 | |
utf8OutsideBmpIncludedColumnNamePattern | Used to specify exclusion rules to + "utf8OutsideBmpExcludedColumnNamePattern". The regex + must follow the patterns used for the java.util.regex + package. | 5.1.3 | |
sessionVariables | A comma-separated list of name/value pairs to be sent as SET SESSION ... + to the server when the driver connects. | 3.1.8 | |
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 |
blobsAreStrings | Should the driver always treat BLOBs as Strings - specifically to work + around dubious metadata returned by the server for GROUP + BY clauses? | false | 5.0.8 |
capitalizeTypeNames | Capitalize type names in DatabaseMetaData? (usually only useful when + using WebObjects, true/false, defaults to 'false') | true | 2.0.7 |
clobCharacterEncoding | The character encoding to use for sending and retrieving TEXT, + MEDIUMTEXT and LONGTEXT values instead of the configured + connection characterEncoding | 5.0.0 | |
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 | Should the driver emulate java.sql.Blobs with locators? With this + feature enabled, the driver will delay loading the + actual Blob data until the one of the retrieval methods + (getInputStream(), getBytes(), and so forth) on the blob + data stream has been accessed. For this to work, you + must use a column alias with the value of the column to + the actual name of the Blob. The feature also has the + following restrictions: The SELECT that created the + result set must reference only one table, the table must + have a primary key; the SELECT must alias the original + blob column name, specified as a string, to an alternate + name; the SELECT must cover all columns that make up the + primary key. | false | 3.1.0 |
emulateUnsupportedPstmts | Should the driver detect prepared statements that are not supported by + the server, and replace them with client-side emulated + versions? | true | 3.1.7 |
functionsNeverReturnBlobs | Should the driver always treat data from functions returning BLOBs as + Strings - specifically to work around dubious metadata + returned by the server for GROUP BY clauses? | false | 5.0.8 |
generateSimpleParameterMetadata | Should the driver generate simplified parameter metadata for + PreparedStatements when no metadata is available either + because the server couldn't support preparing the + statement, or server-side prepared statements are + disabled? | false | 5.0.5 |
ignoreNonTxTables | Ignore non-transactional table warning for rollback? (defaults to + 'false'). | false | 3.0.9 |
jdbcCompliantTruncation | Should the driver throw java.sql.DataTruncation exceptions when data is + truncated as is required by the JDBC specification when + connected to a server that supports warnings (MySQL + 4.1.0 and newer)? This property has no effect if the + server sql-mode includes STRICT_TRANS_TABLES. | true | 3.1.2 |
maxRows | The maximum number of rows to return (0, the default means return all + rows). | -1 | all versions |
netTimeoutForStreamingResults | What value should the driver automatically set the server setting + 'net_write_timeout' to when the streaming result sets + feature is in use? (value has unit of seconds, the value + '0' means the driver will not try and adjust this value) | 600 | 5.1.0 |
noAccessToProcedureBodies | When determining procedure parameter types for CallableStatements, and + the connected user can't access procedure bodies through + "SHOW CREATE PROCEDURE" or select on mysql.proc should + the driver instead create basic metadata (all parameters + reported as IN VARCHARs, but allowing + registerOutParameter() to be called on them anyway) + instead of throwing an exception? | false | 5.0.3 |
noDatetimeStringSync | Don't ensure that + ResultSet.getDatetimeType().toString().equals(ResultSet.getString()) | 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 |
padCharsWithSpace | If a result set column has the CHAR type and the value does not fill the + amount of characters specified in the DDL for the + column, should the driver pad the remaining characters + with space (for ANSI compliance)? | false | 5.0.6 |
pedantic | Follow the JDBC spec to the letter. | false | 3.0.0 |
pinGlobalTxToPhysicalConnection | When using XAConnections, should the driver ensure that operations on a + given XID are always routed to the same physical + connection? This allows the XAConnection to support "XA + START ... JOIN" after "XA END" has been called | false | 5.0.1 |
populateInsertRowWithDefaultValues | When using ResultSets that are CONCUR_UPDATABLE, should the driver + pre-populate the "insert" row with default values from + the DDL for the table used in the query so those values + are immediately available for ResultSet accessors? This + functionality requires a call to the database for + metadata each time a result set of this type is created. + If disabled (the default), the default values will be + populated by the an internal call to refreshRow() which + pulls back default values and/or values changed by + triggers. | false | 5.0.5 |
processEscapeCodesForPrepStmts | Should the driver process escape codes in queries that are prepared? | true | 3.1.12 |
relaxAutoCommit | If the version of MySQL the driver connects 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 | |
statementInterceptors | A comma-delimited list of classes that implement + "com.mysql.jdbc.StatementInterceptor" that should be + placed "in between" query execution to influence the + results. StatementInterceptors are "chainable", the + results returned by the "current" interceptor will be + passed on to the next in in the chain, from + left-to-right order, as specified in this property. | 5.1.1 | |
strictFloatingPoint | Used only in older versions of compliance test | false | 3.0.0 |
strictUpdates | Should the driver do strict checking (all 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 |
treatUtilDateAsTimestamp | Should the driver treat java.util.Date as a TIMESTAMP for the purposes + of PreparedStatement.setObject()? | true | 5.0.5 |
ultraDevHack | Create PreparedStatements for prepareCall() when required, because + UltraDev is broken and issues a prepareCall() for _all_ + statements? (true/false, defaults to '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? | false | 5.0.4 |
useOldUTF8Behavior | Use the UTF-8 behavior the driver did when communicating with 4.0 and + older servers | false | 3.1.6 |
useOnlyServerErrorMessages | Don't prepend 'standard' SQLState error messages to error messages + returned by the server. | true | 3.0.15 |
useSSPSCompatibleTimezoneShift | If migrating from an environment that was using server-side prepared + statements, and the configuration property + "useJDBCCompliantTimeZoneShift" set to "true", use + compatible behavior when not using server-side prepared + statements when sending TIMESTAMP values to the MySQL + server. | false | 5.0.5 |
useServerPrepStmts | Use server-side prepared statements if the server supports them? | false | 3.1.0 |
useSqlStateCodes | Use SQL Standard state codes instead of 'legacy' X/Open/SQL state codes + (true/false), default is 'true' | true | 3.1.3 |
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
.
+
+ 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 +
+ Starting with Connector/J version 3.1.0, you can emulate
+ Blobs with locators by adding the property
+ 'emulateLocators=true' to your JDBC URL. Using this method,
+ the driver will delay loading the actual Blob data until you
+ retrieve the other data and then use retrieval methods
+ (getInputStream()
,
+ getBytes()
, and so forth) on the blob
+ data stream.
+
+ For this to work, you must use a column alias with the value + of the column to the actual name of the Blob, for example: +
SELECT id, 'data' as blob_data from blobtable
+ For this to work, you must also follow follow these rules: +
+ The SELECT
must also reference only
+ one table, the table must have a primary key.
+
+ The SELECT
must alias the original
+ blob column name, specified as a string, to an alternate
+ name.
+
+ The SELECT
must cover all columns
+ that make up the primary key.
+
+ The Blob implementation does not allow in-place modification
+ (they are copies, as reported by the
+ DatabaseMetaData.locatorsUpdateCopies()
+ method). Because 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.
+
MySQL Enterprise + MySQL Enterprise subscribers will find more information + about type conversion in the Knowledge Base article, + Type + Conversions Supported by MySQL Connector/J. To + subscribe to MySQL Enterprise see + http://www.mysql.com/products/enterprise/advisors.html. +
+ CallableStatement +
+ Starting with Connector/J 3.1.1, stored procedures are
+ supported when connecting to MySQL version 5.0 or newer 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:
+
+ During both server-side prepared statements and
+ client-side emulation, large data is exchanged only when
+ PreparedStatement.execute()
is
+ called.
+
+ Once that has been done, the stream used to read the + data on the client side is closed (as per the JDBC + spec), and can't be read from again. +
+ 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.
+
+ Connector/J 5.0.0 and later include support for both
+ Statement.cancel()
and
+ Statement.setQueryTimeout()
. Both require
+ MySQL 5.0.0 or newer server, and require a separate
+ connection to issue the KILL QUERY
+ statement. In the case of
+ setQueryTimeout()
, the implementation
+ creates an additional thread to handle the timeout
+ functionality.
+
+ Failures to cancel the statement for
+ setQueryTimeout()
may manifest
+ themselves as RuntimeException
rather
+ than failing silently, as there is currently no way to
+ unblock the thread that is executing the query being
+ cancelled due to timeout expiration and have it throw the
+ exception instead.
+
+ MySQL does not support SQL cursors, and the JDBC driver + doesn't emulate them, so "setCursorName()" has no effect. +
+ Connector/J 5.1.3 and later include two additional methods: +
+ setLocalInfileInputStream()
sets an
+ InputStream
instance that will be
+ used to send data to the MySQL server for a
+ LOAD DATA LOCAL INFILE
statement
+ rather than a FileInputStream
or
+ URLInputStream
that represents the
+ path given as an argument to the statement.
+
+ This stream will be read to completion upon execution of
+ a LOAD DATA LOCAL INFILE
statement,
+ and will automatically be closed by the driver, so it
+ needs to be reset before each call to
+ execute*()
that would cause the MySQL
+ server to request data to fulfill the request for
+ LOAD DATA LOCAL INFILE
.
+
+ If this value is set to NULL
, the
+ driver will revert to using a
+ FileInputStream
or
+ URLInputStream
as required.
+
+ getLocalInfileInputStream()
returns
+ the InputStream
instance that will be
+ used to send data in response to a LOAD DATA
+ LOCAL INFILE
statement.
+
+ This method returns NULL
if no such
+ stream has been set via
+ setLocalInfileInputStream()
.
+
+ 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 |
+
+ 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.
+
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 |
+
+ 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
+ Server Character Set and Collation.
+
+ 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 |
+
+ 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.
+
+ 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/4273544.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 + Using 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 -keystore truststore
+ Keytool will respond with the following information: +
Enter keystore password: ********* +Owner: EMAILADDRESS=walrus@example.com, CN=Walrus, + O=MySQL AB, L=Orenburg, ST=Some-State, C=RU +Issuer: EMAILADDRESS=walrus@example.com, CN=Walrus, + O=MySQL AB, L=Orenburg, ST=Some-State, C=RU +Serial number: 0 +Valid from: + Fri Aug 02 16:55:53 CDT 2002 until: Sat Aug 02 16:55:53 CDT 2003 +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
+ 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 -keystore 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. You can do this either on the + command line: +
-Djavax.net.ssl.keyStore=path_to_keystore_file +-Djavax.net.ssl.keyStorePassword=password +-Djavax.net.ssl.trustStore=path_to_truststore_file +-Djavax.net.ssl.trustStorePassword=password
+ Or you can set the values directly within the application: +
System.setProperty("javax.net.ssl.keyStore","path_to_keystore_file"); +System.setProperty("javax.net.ssl.keyStorePassword","password"); +System.setProperty("javax.net.ssl.trustStore","path_to_truststore_file"); +System.setProperty("javax.net.ssl.trustStorePassword","password");
+ You will also need to set useSSL to
+ true
in your connection parameters for MySQL
+ Connector/J, either by adding useSSL=true
to
+ your URL, or by setting the property useSSL
+ to 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.@........ +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.
+
+ 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/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 FROM alt_table"); + + ....... + } +} +
+ The table below provides a mapping of the MySQL Error Numbers to
+ SQL States
+
Table 1. Mapping of MySQL Error Numbers to SQLStates
MySQL Error Number | MySQL Error Name | Legacy (X/Open) SQLState | SQL Standard SQLState |
---|---|---|---|
1022 | ER_DUP_KEY | S1000 | 23000 |
1037 | ER_OUTOFMEMORY | S1001 | HY001 |
1038 | ER_OUT_OF_SORTMEMORY | S1001 | HY001 |
1040 | ER_CON_COUNT_ERROR | 08004 | 08004 |
1042 | ER_BAD_HOST_ERROR | 08004 | 08S01 |
1043 | ER_HANDSHAKE_ERROR | 08004 | 08S01 |
1044 | ER_DBACCESS_DENIED_ERROR | S1000 | 42000 |
1045 | ER_ACCESS_DENIED_ERROR | 28000 | 28000 |
1047 | ER_UNKNOWN_COM_ERROR | 08S01 | HY000 |
1050 | ER_TABLE_EXISTS_ERROR | S1000 | 42S01 |
1051 | ER_BAD_TABLE_ERROR | 42S02 | 42S02 |
1052 | ER_NON_UNIQ_ERROR | S1000 | 23000 |
1053 | ER_SERVER_SHUTDOWN | S1000 | 08S01 |
1054 | ER_BAD_FIELD_ERROR | S0022 | 42S22 |
1055 | ER_WRONG_FIELD_WITH_GROUP | S1009 | 42000 |
1056 | ER_WRONG_GROUP_FIELD | S1009 | 42000 |
1057 | ER_WRONG_SUM_SELECT | S1009 | 42000 |
1058 | ER_WRONG_VALUE_COUNT | 21S01 | 21S01 |
1059 | ER_TOO_LONG_IDENT | S1009 | 42000 |
1060 | ER_DUP_FIELDNAME | S1009 | 42S21 |
1061 | ER_DUP_KEYNAME | S1009 | 42000 |
1062 | ER_DUP_ENTRY | S1009 | 23000 |
1063 | ER_WRONG_FIELD_SPEC | S1009 | 42000 |
1064 | ER_PARSE_ERROR | 42000 | 42000 |
1065 | ER_EMPTY_QUERY | 42000 | 42000 |
1066 | ER_NONUNIQ_TABLE | S1009 | 42000 |
1067 | ER_INVALID_DEFAULT | S1009 | 42000 |
1068 | ER_MULTIPLE_PRI_KEY | S1009 | 42000 |
1069 | ER_TOO_MANY_KEYS | S1009 | 42000 |
1070 | ER_TOO_MANY_KEY_PARTS | S1009 | 42000 |
1071 | ER_TOO_LONG_KEY | S1009 | 42000 |
1072 | ER_KEY_COLUMN_DOES_NOT_EXITS | S1009 | 42000 |
1073 | ER_BLOB_USED_AS_KEY | S1009 | 42000 |
1074 | ER_TOO_BIG_FIELDLENGTH | S1009 | 42000 |
1075 | ER_WRONG_AUTO_KEY | S1009 | 42000 |
1080 | ER_FORCING_CLOSE | S1000 | 08S01 |
1081 | ER_IPSOCK_ERROR | 08S01 | 08S01 |
1082 | ER_NO_SUCH_INDEX | S1009 | 42S12 |
1083 | ER_WRONG_FIELD_TERMINATORS | S1009 | 42000 |
1084 | ER_BLOBS_AND_NO_TERMINATED | S1009 | 42000 |
1090 | ER_CANT_REMOVE_ALL_FIELDS | S1000 | 42000 |
1091 | ER_CANT_DROP_FIELD_OR_KEY | S1000 | 42000 |
1101 | ER_BLOB_CANT_HAVE_DEFAULT | S1000 | 42000 |
1102 | ER_WRONG_DB_NAME | S1000 | 42000 |
1103 | ER_WRONG_TABLE_NAME | S1000 | 42000 |
1104 | ER_TOO_BIG_SELECT | S1000 | 42000 |
1106 | ER_UNKNOWN_PROCEDURE | S1000 | 42000 |
1107 | ER_WRONG_PARAMCOUNT_TO_PROCEDURE | S1000 | 42000 |
1109 | ER_UNKNOWN_TABLE | S1000 | 42S02 |
1110 | ER_FIELD_SPECIFIED_TWICE | S1000 | 42000 |
1112 | ER_UNSUPPORTED_EXTENSION | S1000 | 42000 |
1113 | ER_TABLE_MUST_HAVE_COLUMNS | S1000 | 42000 |
1115 | ER_UNKNOWN_CHARACTER_SET | S1000 | 42000 |
1118 | ER_TOO_BIG_ROWSIZE | S1000 | 42000 |
1120 | ER_WRONG_OUTER_JOIN | S1000 | 42000 |
1121 | ER_NULL_COLUMN_IN_INDEX | S1000 | 42000 |
1129 | ER_HOST_IS_BLOCKED | 08004 | HY000 |
1130 | ER_HOST_NOT_PRIVILEGED | 08004 | HY000 |
1131 | ER_PASSWORD_ANONYMOUS_USER | S1000 | 42000 |
1132 | ER_PASSWORD_NOT_ALLOWED | S1000 | 42000 |
1133 | ER_PASSWORD_NO_MATCH | S1000 | 42000 |
1136 | ER_WRONG_VALUE_COUNT_ON_ROW | S1000 | 21S01 |
1138 | ER_INVALID_USE_OF_NULL | S1000 | 42000 |
1139 | ER_REGEXP_ERROR | S1000 | 42000 |
1140 | ER_MIX_OF_GROUP_FUNC_AND_FIELDS | S1000 | 42000 |
1141 | ER_NONEXISTING_GRANT | S1000 | 42000 |
1142 | ER_TABLEACCESS_DENIED_ERROR | S1000 | 42000 |
1143 | ER_COLUMNACCESS_DENIED_ERROR | S1000 | 42000 |
1144 | ER_ILLEGAL_GRANT_FOR_TABLE | S1000 | 42000 |
1145 | ER_GRANT_WRONG_HOST_OR_USER | S1000 | 42000 |
1146 | ER_NO_SUCH_TABLE | S1000 | 42S02 |
1147 | ER_NONEXISTING_TABLE_GRANT | S1000 | 42000 |
1148 | ER_NOT_ALLOWED_COMMAND | S1000 | 42000 |
1149 | ER_SYNTAX_ERROR | S1000 | 42000 |
1152 | ER_ABORTING_CONNECTION | S1000 | 08S01 |
1153 | ER_NET_PACKET_TOO_LARGE | S1000 | 08S01 |
1154 | ER_NET_READ_ERROR_FROM_PIPE | S1000 | 08S01 |
1155 | ER_NET_FCNTL_ERROR | S1000 | 08S01 |
1156 | ER_NET_PACKETS_OUT_OF_ORDER | S1000 | 08S01 |
1157 | ER_NET_UNCOMPRESS_ERROR | S1000 | 08S01 |
1158 | ER_NET_READ_ERROR | S1000 | 08S01 |
1159 | ER_NET_READ_INTERRUPTED | S1000 | 08S01 |
1160 | ER_NET_ERROR_ON_WRITE | S1000 | 08S01 |
1161 | ER_NET_WRITE_INTERRUPTED | S1000 | 08S01 |
1162 | ER_TOO_LONG_STRING | S1000 | 42000 |
1163 | ER_TABLE_CANT_HANDLE_BLOB | S1000 | 42000 |
1164 | ER_TABLE_CANT_HANDLE_AUTO_INCREMENT | S1000 | 42000 |
1166 | ER_WRONG_COLUMN_NAME | S1000 | 42000 |
1167 | ER_WRONG_KEY_COLUMN | S1000 | 42000 |
1169 | ER_DUP_UNIQUE | S1000 | 23000 |
1170 | ER_BLOB_KEY_WITHOUT_LENGTH | S1000 | 42000 |
1171 | ER_PRIMARY_CANT_HAVE_NULL | S1000 | 42000 |
1172 | ER_TOO_MANY_ROWS | S1000 | 42000 |
1173 | ER_REQUIRES_PRIMARY_KEY | S1000 | 42000 |
1177 | ER_CHECK_NO_SUCH_TABLE | S1000 | 42000 |
1178 | ER_CHECK_NOT_IMPLEMENTED | S1000 | 42000 |
1179 | ER_CANT_DO_THIS_DURING_AN_TRANSACTION | S1000 | 25000 |
1184 | ER_NEW_ABORTING_CONNECTION | S1000 | 08S01 |
1189 | ER_MASTER_NET_READ | S1000 | 08S01 |
1190 | ER_MASTER_NET_WRITE | S1000 | 08S01 |
1203 | ER_TOO_MANY_USER_CONNECTIONS | S1000 | 42000 |
1205 | ER_LOCK_WAIT_TIMEOUT | 41000 | 41000 |
1207 | ER_READ_ONLY_TRANSACTION | S1000 | 25000 |
1211 | ER_NO_PERMISSION_TO_CREATE_USER | S1000 | 42000 |
1213 | ER_LOCK_DEADLOCK | 41000 | 40001 |
1216 | ER_NO_REFERENCED_ROW | S1000 | 23000 |
1217 | ER_ROW_IS_REFERENCED | S1000 | 23000 |
1218 | ER_CONNECT_TO_MASTER | S1000 | 08S01 |
1222 | ER_WRONG_NUMBER_OF_COLUMNS_IN_SELECT | S1000 | 21000 |
1226 | ER_USER_LIMIT_REACHED | S1000 | 42000 |
1230 | ER_NO_DEFAULT | S1000 | 42000 |
1231 | ER_WRONG_VALUE_FOR_VAR | S1000 | 42000 |
1232 | ER_WRONG_TYPE_FOR_VAR | S1000 | 42000 |
1234 | ER_CANT_USE_OPTION_HERE | S1000 | 42000 |
1235 | ER_NOT_SUPPORTED_YET | S1000 | 42000 |
1239 | ER_WRONG_FK_DEF | S1000 | 42000 |
1241 | ER_OPERAND_COLUMNS | S1000 | 21000 |
1242 | ER_SUBQUERY_NO_1_ROW | S1000 | 21000 |
1247 | ER_ILLEGAL_REFERENCE | S1000 | 42S22 |
1248 | ER_DERIVED_MUST_HAVE_ALIAS | S1000 | 42000 |
1249 | ER_SELECT_REDUCED | S1000 | 01000 |
1250 | ER_TABLENAME_NOT_ALLOWED_HERE | S1000 | 42000 |
1251 | ER_NOT_SUPPORTED_AUTH_MODE | S1000 | 08004 |
1252 | ER_SPATIAL_CANT_HAVE_NULL | S1000 | 42000 |
1253 | ER_COLLATION_CHARSET_MISMATCH | S1000 | 42000 |
1261 | ER_WARN_TOO_FEW_RECORDS | S1000 | 01000 |
1262 | ER_WARN_TOO_MANY_RECORDS | S1000 | 01000 |
1263 | ER_WARN_NULL_TO_NOTNULL | S1000 | 01000 |
1264 | ER_WARN_DATA_OUT_OF_RANGE | S1000 | 01000 |
1265 | ER_WARN_DATA_TRUNCATED | S1000 | 01000 |
1280 | ER_WRONG_NAME_FOR_INDEX | S1000 | 42000 |
1281 | ER_WRONG_NAME_FOR_CATALOG | S1000 | 42000 |
1286 | ER_UNKNOWN_STORAGE_ENGINE | S1000 | 42000 |
+ This section provides some general JDBC background. +
+ 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.
+
+ 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; + } +}
+ 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.
+
+ For more information on MySQL stored procedures, please refer + to + http://dev.mysql.com/doc/mysql/en/stored-procedures.html. +
+ Connector/J exposes stored procedure functionality through
+ JDBC's CallableStatement
interface.
+
+ 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 INT) +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:
+
+ 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");
+ 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.
+
+ 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); +... +
+
+ 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); + +...
+
+ 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 + +...
+
+ 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.
+
+ This section describes how to use Connector/J in several + contexts. +
+ This section provides general background on J2EE concepts that + pertain to use of Connector/J. +
+ 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/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; + } + } + } +}
+ + 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. +
+ The following instructions are based on the instructions for + Tomcat-5.x, available at + http://jakarta.apache.org/tomcat/tomcat-5.0-doc/jndi-datasource-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 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. --> + + <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 & 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 ' SQL +state: null
+ 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 name + "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.integration.jboss.MysqlValidConnectionChecker + </valid-connection-checker-class-name> + + </local-tx-datasource> +</datasources>
+ The Spring Framework is a Java-based application framework + designed for assisting in application design by providing a + way to configure components. The technique used by Spring is a + well known design pattern called Dependency Injection (see + Inversion + of Control Containers and the Dependency Injection + pattern). This article will focus on Java-oriented + access to MySQL databases with Spring 2.0. For those + wondering, there is a .NET port of Spring appropriately named + Spring.NET. +
+ Spring is not only a system for configuring components, but + also includes support for aspect oriented programming (AOP). + This is one of the main benefits and the foundation for + Spring's resource and transaction management. Spring also + provides utilities for integrating resource management with + JDBC and Hibernate. +
+ For the examples in this section the MySQL world sample + database will be used. The first task is to setup a MySQL data + source through Spring. Components within Spring use the "bean" + terminology. For example, to configure a connection to a MySQL + server supporting the world sample database you might use: +
<util:map id="dbProps"> + <entry key="db.driver" value="com.mysql.jdbc.Driver"/> + <entry key="db.jdbcurl" value="jdbc:mysql://localhost/world"/> + <entry key="db.username" value="myuser"/> + <entry key="db.password" value="mypass"/> +</util:map> + +
+ In the above example we are assigning values to properties + that will be used in the configuration. For the datasource + configuration: +
<bean id="dataSource" + class="org.springframework.jdbc.datasource.DriverManagerDataSource"> + <property name="driverClassName" value="${db.driver}"/> + <property name="url" value="${db.jdbcurl}"/> + <property name="username" value="${db.username}"/> + <property name="password" value="${db.password}"/> +</bean> +
+ The placeholders are used to provide values for properties of + this bean. This means that you can specify all the properties + of the configuration in one place instead of entering the + values for each property on each bean. We do, however, need + one more bean to pull this all together. The last bean is + responsible for actually replacing the placeholders with the + property values. +
<bean + class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> + <property name="properties" ref="dbProps"/> +</bean> +
+ Now that we have our MySQL data source configured and ready to + go, we write some Java code to access it. The example below + will retrieve three random cities and their corresponding + country using the data source we configured with Spring. +
// Create a new application context. this processes the Spring config +ApplicationContext ctx = + new ClassPathXmlApplicationContext("ex1appContext.xml"); +// Retrieve the data source from the application context + DataSource ds = (DataSource) ctx.getBean("dataSource"); +// Open a database connection using Spring's DataSourceUtils +Connection c = DataSourceUtils.getConnection(ds); +try { + // retrieve a list of three random cities + PreparedStatement ps = c.prepareStatement( + "select City.Name as 'City', Country.Name as 'Country' " + + "from City inner join Country on City.CountryCode = Country.Code " + + "order by rand() limit 3"); + ResultSet rs = ps.executeQuery(); + while(rs.next()) { + String city = rs.getString("City"); + String country = rs.getString("Country"); + System.out.printf("The city %s is in %s%n", city, country); + } +} catch (SQLException ex) { + // something has failed and we print a stack trace to analyse the error + ex.printStackTrace(); + // ignore failure closing connection + try { c.close(); } catch (SQLException e) { } +} finally { + // properly release our connection + DataSourceUtils.releaseConnection(c, ds); +}
+ This is very similar to normal JDBC access to MySQL with the + main difference being that we are using DataSourceUtils + instead of the DriverManager to create the connection. +
+ While it may seem like a small difference, the implications + are somewhat far reaching. Spring manages this resource in a + way similar to a container managed data source in a J2EE + application server. When a connection is opened, it can be + subsequently accessed in other parts of the code if it is + synchronized with a transaction. This makes it possible to + treat different parts of your application as transactional + instead of passing around a database connection. +
+ Spring makes extensive use of the Template method design
+ pattern (see
+ Template
+ Method Pattern). Our immediate focus will be on the
+ JdbcTemplate
and related classes,
+ specifically NamedParameterJdbcTemplate
.
+ The template classes handle obtaining and releasing a
+ connection for data access when one is needed.
+
+ The next example shows how to use
+ NamedParameterJdbcTemplate
inside of a
+ DAO (Data Access Object) class to retrieve a random city
+ given a country code.
+
public class Ex2JdbcDao { + /** + * Data source reference which will be provided by Spring. + */ + private DataSource dataSource; + + /** + * Our query to find a random city given a country code. Notice + * the ":country" parameter towards the end. This is called a + * named parameter. + */ + private String queryString = "select Name from City " + + "where CountryCode = :country order by rand() limit 1"; + + /** + * Retrieve a random city using Spring JDBC access classes. + */ + public String getRandomCityByCountryCode(String cntryCode) { + // A template that allows using queries with named parameters + NamedParameterJdbcTemplate template = + new NamedParameterJdbcTemplate(dataSource); + // A java.util.Map is used to provide values for the parameters + Map params = new HashMap(); + params.put("country", cntryCode); + // We query for an Object and specify what class we are expecting + return (String)template.queryForObject(queryString, params, String.class); + } + + /** + * A JavaBean setter-style method to allow Spring to inject the data source. + * @param dataSource + */ + public void setDataSource(DataSource dataSource) { + this.dataSource = dataSource; + } +} +
+ The focus in the above code is on the
+ getRandomCityByCountryCode()
method. We
+ pass a country code and use the
+ NamedParameterJdbcTemplate
to query for a
+ city. The country code is placed in a Map with the key
+ "country", which is the parameter is named in the SQL query.
+
+ To access this code, you need to configure it with Spring by + providing a reference to the data source. +
<bean id="dao" class="code.Ex2JdbcDao"> + <property name="dataSource" ref="dataSource"/> +</bean>
+ At this point, we can just grab a reference to the DAO from
+ Spring and call
+ getRandomCityByCountryCode()
.
+
// Create the application context + ApplicationContext ctx = + new ClassPathXmlApplicationContext("ex2appContext.xml"); + // Obtain a reference to our DAO + Ex2JdbcDao dao = (Ex2JdbcDao) ctx.getBean("dao"); + + String countryCode = "USA"; + + // Find a few random cities in the US + for(int i = 0; i < 4; ++i) + System.out.printf("A random city in %s is %s%n", countryCode, + dao.getRandomCityByCountryCode(countryCode)); +
+ This example shows how to use Spring's JDBC classes to
+ completely abstract away the use of traditional JDBC classes
+ including Connection
and
+ PreparedStatement
.
+
+ You might be wondering how we can add transactions into our + code if we don't deal directly with the JDBC classes. Spring + provides a transaction management package that not only + replaces JDBC transaction management, but also allows + declarative transaction management (configuration instead of + code). +
+ In order to use transactional database access, we will need + to change the storage engine of the tables in the world + database. The downloaded script explicitly creates MyISAM + tables which do not support transactional semantics. The + InnoDB storage engine does support transactions and this is + what we will be using. We can change the storage engine with + the following statements. +
ALTER TABLE City ENGINE=InnoDB; +ALTER TABLE Country ENGINE=InnoDB; +ALTER TABLE CountryLanguage ENGINE=InnoDB; +
+ A good programming practice emphasized by Spring is + separating interfaces and implementations. What this means + is that we can create a Java interface and only use the + operations on this interface without any internal knowledge + of what the actual implementation is. We will let Spring + manage the implementation and with this it will manage the + transactions for our implementation. +
+ First you create a simple interface: +
public interface Ex3Dao { + Integer createCity(String name, String countryCode, + String district, Integer population); +} +
+ This interface contains one method that will create a new + city record in the database and return the id of the new + record. Next you need to create an implementation of this + interface. +
public class Ex3DaoImpl implements Ex3Dao { + protected DataSource dataSource; + protected SqlUpdate updateQuery; + protected SqlFunction idQuery; + + public Integer createCity(String name, String countryCode, + String district, Integer population) { + updateQuery.update(new Object[] { name, countryCode, + district, population }); + return getLastId(); + } + + protected Integer getLastId() { + return idQuery.run(); + } +} +
+ You can see that we only operate on abstract query objects + here and don't deal directly with the JDBC API. Also, this + is the complete implementation. All of our transaction + management will be dealt with in the configuration. To get + the configuration started, we need to create the DAO. +
<bean id="dao" class="code.Ex3DaoImpl"> + <property name="dataSource" ref="dataSource"/> + <property name="updateQuery">...</property> + <property name="idQuery">...</property> +</bean> +
+ Now you need to setup the transaction configuration. The
+ first thing you must do is create transaction manager to
+ manage the data source and a specification of what
+ transaction properties are required for for the
+ dao
methods.
+
<bean id="transactionManager" + class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> + <property name="dataSource" ref="dataSource"/> +</bean> + +<tx:advice id="txAdvice" transaction-manager="transactionManager"> + <tx:attributes> + <tx:method name="*"/> + </tx:attributes> +</tx:advice>
+ The preceding code creates a transaction manager that
+ handles transactions for the data source provided to it. The
+ txAdvice
uses this transaction manager
+ and the attributes specify to create a transaction for all
+ methods. Finally you need to apply this advice with an AOP
+ pointcut.
+
<aop:config> + <aop:pointcut id="daoMethods" + expression="execution(* code.Ex3Dao.*(..))"/> + <aop:advisor advice-ref="txAdvice" pointcut-ref="daoMethods"/> +</aop:config>
+ This basically says that all methods called on the
+ Ex3Dao
interface will be wrapped in a
+ transaction. To make use of this, you only have to retrieve
+ the dao
from the application context and
+ call a method on the dao
instance.
+
Ex3Dao dao = (Ex3Dao) ctx.getBean("dao"); +Integer id = dao.createCity(name, countryCode, district, pop); +
+ We can verify from this that there is no transaction + management happening in our Java code and it's all + configured with Spring. This is a very powerful notion and + regarded as one of the most beneficial features of Spring. +
+ In many sitations, such as web applications, there will be a
+ large number of small database transactions. When this is
+ the case, it usually makes sense to create a pool of
+ database connections available for web requests as needed.
+ Although MySQL does not spawn an extra process when a
+ connection is made, there is still a small amount of
+ overhead to create and setup the connection. Pooling of
+ connections also alleviates problems such as collecting
+ large amounts of sockets in the TIME_WAIT
+ state.
+
+ Setting up pooling of MySQL connections with Spring is as
+ simple as changing the data source configuration in the
+ application context. There are a number of configurations
+ that we can use. The first example is based on the
+ Jakarta
+ Commons DBCP library. The example below replaces the
+ source configuration that was based on
+ DriverManagerDataSource
with DBCP's
+ BasicDataSource.
+
<bean id="dataSource" destroy-method="close" + class="org.apache.commons.dbcp.BasicDataSource"> + <property name="driverClassName" value="${db.driver}"/> + <property name="url" value="${db.jdbcurl}"/> + <property name="username" value="${db.username}"/> + <property name="password" value="${db.password}"/> + <property name="initialSize" value="3"/> +</bean>
+ The configuration of the two solutions is very similar. The
+ difference is that DBCP will pool connections to the
+ database instead of creating a new connection every time one
+ is requested. We have also set a parameter here called
+ initialSize
. This tells DBCP that we want
+ three connections in the pool when it is created.
+
+ Another way to configure connection pooling is to configure
+ a data source in our J2EE application server. Using JBoss as
+ an example, you can set up the MySQL connection pool by
+ creating a file called
+ mysql-local-ds.xml
and placing it in
+ the server/default/deploy directory in JBoss. Once we have
+ this setup, we can use JNDI to look it up. With Spring, this
+ lookup is very simple. The data source configuration looks
+ like this.
+
<jee:jndi-lookup id="dataSource" jndi-name="java:MySQL_DS"/>
+ 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.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. +
1.5.3.2: + My application throws an SQLException 'No Suitable + Driver'. Why is this happening? +
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
1.5.3.4: + I have a servlet/application that works fine for a day, + and then stops working overnight +
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. +
1.5.3.6: + I cannot connect to the MySQL server using Connector/J, + and I'm sure the connection paramters are correct. +
1.5.3.7: + I am trying to connect to my MySQL server within my + application, but I get the following error and stack + trace: +
java.net.SocketException +MESSAGE: Software caused connection abort: recv failed + +STACKTRACE: + +java.net.SocketException: Software caused connection abort: recv failed +at java.net.SocketInputStream.socketRead0(Native Method) +at java.net.SocketInputStream.read(Unknown Source) +at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392) +at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414) +at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625) +at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926) +at com.mysql.jdbc.Connection.<init>(Connection.java:452) +at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:411)
1.5.3.8: + My application is deployed through JBoss and I am using + transactions to handle the statements on the MySQL + database. Under heavy loads I am getting a error and stack + trace, but these only occur after a fixed period of heavy + activity. +
1.5.3.9:
+ When using gcj an
+ java.io.CharConversionException
is
+ raised when working with certain character sequences.
+
1.5.3.10:
+ Updating a table that contains a primary key that is
+ either FLOAT
or compound primary key
+ that uses FLOAT
fails to update the
+ table and raises an exception.
+
Questions and Answers
1.5.3.1: + When I try to connect to the database with MySQL + 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 GRANT
Syntax, for more information.
+
+ 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.
+
+ 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 1.2, “Connector/J Installation”.
+
+ The format of your connection URL is incorrect, or you + are referencing the wrong JDBC driver. +
+ When using DriverManager, the
+ jdbc.drivers
system 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 1.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 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 && (retryCount > 0)); +}
+
+ 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.5.3.6: + I cannot connect to the MySQL server using Connector/J, + and I'm sure the connection paramters are correct. +
+ Make sure that the skip-networking
+ option has not been enabled on your server. Connector/J
+ must be able to communicate with your server over TCP/IP,
+ named sockets are not supported. Also ensure that you are
+ not filtering connections through a Firewall or other
+ network security system. For more informaiton, see
+ Can't connect to [local] MySQL server
.
+
1.5.3.7: + I am trying to connect to my MySQL server within my + application, but I get the following error and stack + trace: +
java.net.SocketException +MESSAGE: Software caused connection abort: recv failed + +STACKTRACE: + +java.net.SocketException: Software caused connection abort: recv failed +at java.net.SocketInputStream.socketRead0(Native Method) +at java.net.SocketInputStream.read(Unknown Source) +at com.mysql.jdbc.MysqlIO.readFully(MysqlIO.java:1392) +at com.mysql.jdbc.MysqlIO.readPacket(MysqlIO.java:1414) +at com.mysql.jdbc.MysqlIO.doHandshake(MysqlIO.java:625) +at com.mysql.jdbc.Connection.createNewIO(Connection.java:1926) +at com.mysql.jdbc.Connection.<init>(Connection.java:452) +at com.mysql.jdbc.NonRegisteringDriver.connect(NonRegisteringDriver.java:411)
+ The error probably indicates that you are using a older + version of the Connector/J JDBC driver (2.0.14 or 3.0.x) + and you are trying to connect to a MySQL server with + version 4.1x or newer. The older drivers are not + compatible with 4.1 or newer of MySQL as they do not + support the newer authentication mechanisms. +
+ It is likely that the older version of the Connector/J
+ driver exists within your application directory or your
+ CLASSPATH
includes the older
+ Connector/J package.
+
1.5.3.8: + My application is deployed through JBoss and I am using + transactions to handle the statements on the MySQL + database. Under heavy loads I am getting a error and stack + trace, but these only occur after a fixed period of heavy + activity. +
+ This is a JBoss, not Connector/J, issue and is connected + to the use of transactions. Under heavy loads the time + taken for transactions to complete can increase, and the + error is caused because you have exceeded the predefined + timeout. +
+ You can increase the timeout value by setting the
+ TransactionTimeout
attribute to the
+ TransactionManagerService
within the
+ /conf/jboss-service.xml
file
+ (pre-4.0.3) or
+ /deploy/jta-service.xml
for JBoss
+ 4.0.3 or later. See
+ TransactionTimeoute
+ within the JBoss wiki for more information.
+
1.5.3.9:
+ When using gcj an
+ java.io.CharConversionException
is
+ raised when working with certain character sequences.
+
+ This is a known issue with gcj which
+ raises an exception when it reaches an unknown character
+ or one it cannot convert. You should add
+ useJvmCharsetConverters=true
to your
+ connection string to force character conversion outside of
+ the gcj libraries, or try a different
+ JDK.
+
1.5.3.10:
+ Updating a table that contains a primary key that is
+ either FLOAT
or compound primary key
+ that uses FLOAT
fails to update the
+ table and raises an exception.
+
+ Connector/J adds conditions to the
+ WHERE
clause during an
+ UPDATE
to check the old values of the
+ primary key. If there is no match then Connector/J
+ considers this a failure condition and raises an
+ exception.
+
+ The problem is that rounding differences between supplied + values and the values stored in the database may mean that + the values never match, and hence the update fails. The + issue will affect all queries, not just those from + Connector/J. +
+ To prevent this issue, use a primary key that does not use
+ FLOAT
. If you have to use a floating
+ point column in your primary key use
+ DOUBLE
or DECIMAL
+ types in place of FLOAT
.
+
+ 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. +
+ Community support from experienced users is also available + through the + JDBC + Forum. 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. +
+ 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. +
+ 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/. +
+ The Connector/J Change History (Changelog) is located with the + main Changelog for MySQL. See MySQL Connector/J Change History. +
pos
and having a length of length
.
+ *
+ * @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();
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/BlobFromLocator.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/BlobFromLocator.java
new file mode 100644
index 00000000..cfdef7a6
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/BlobFromLocator.java
@@ -0,0 +1,670 @@
+/*
+ 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
+ * pos
and having a length of length
.
+ *
+ * @throws SQLException
+ * if a database error occurs
+ */
+ public byte[] getBytes(long pos, int length) throws SQLException {
+ java.sql.PreparedStatement pStmt = null;
+
+ try {
+
+ pStmt = createGetBytesStatement();
+
+ return getBytesInternal(pStmt, pos, length);
+ } finally {
+ if (pStmt != null) {
+ try {
+ pStmt.close();
+ } catch (SQLException sqlEx) {
+ ; // do nothing
+ }
+
+ pStmt = 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();
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Buffer.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Buffer.java
new file mode 100644
index 00000000..747f1d34
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Buffer.java
@@ -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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CachedResultSetMetaData.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CachedResultSetMetaData.java
new file mode 100644
index 00000000..1a60eb88
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CachedResultSetMetaData.java
@@ -0,0 +1,40 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package com.mysql.jdbc;
+
+import java.util.Map;
+
+class CachedResultSetMetaData {
+ /** Map column names (and all of their permutations) to column indices */
+ Map columnNameToIndex = null;
+
+ /** Cached Field info */
+ Field[] fields;
+
+ /** Map of fully-specified column names to column indices */
+ Map fullColumnNameToIndex = null;
+
+ /** Cached ResultSetMetaData */
+ java.sql.ResultSetMetaData metadata;
+ }
\ No newline at end of file
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CallableStatement.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CallableStatement.java
new file mode 100644
index 00000000..2b8a494c
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CallableStatement.java
@@ -0,0 +1,2181 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+package com.mysql.jdbc;
+
+import java.io.InputStream;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+
+import java.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);
+
+ // We don't have reliable metadata in this case, trust
+ // the caller
+
+ if (this.connection.getNoAccessToProcedureBodies()) {
+ paramDescriptor.isOut = true;
+ paramDescriptor.isIn = true;
+ paramDescriptor.inOutModifier = DatabaseMetaData.procedureColumnInOut;
+ } else if (!paramDescriptor.isOut) {
+ throw SQLError.createSQLException(
+ Messages.getString("CallableStatement.9") + paramIndex //$NON-NLS-1$
+ + Messages.getString("CallableStatement.10"), //$NON-NLS-1$
+ 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.procedureColumnIn),
+ 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 {
+ String sanitizedSql = StringUtils.stripComments(this.originalSql,
+ "`\"'", "`\"'", true, false, true, true);
+
+ // TODO: Do this with less memory allocation
+ int endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql,
+ "CALL "); //$NON-NLS-1$
+ int offset = 5;
+
+ if (endCallIndex == -1) {
+ endCallIndex = StringUtils.indexOfIgnoreCase(sanitizedSql,
+ "SELECT ");
+ offset = 7;
+ }
+
+ if (endCallIndex != -1) {
+ StringBuffer nameBuf = new StringBuffer();
+
+ String trimmedStatement = sanitizedSql.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 = 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 {
+ int sqlType = inParamInfo.desiredJdbcType;
+
+ switch (sqlType) {
+ case Types.BIT:
+ case Types.BINARY:
+ case Types.BLOB:
+ case Types.JAVA_OBJECT:
+ case Types.LONGVARBINARY:
+ case Types.VARBINARY:
+ setPstmt.setBytes(1, parameterAsBytes);
+ break;
+ default:
+ // the inherited PreparedStatement methods
+ // have already escaped and quoted these parameters
+ setPstmt.setBytesNoEscape(1, parameterAsBytes);
+ }
+ }
+ } else {
+ setPstmt.setNull(1, Types.NULL);
+ }
+
+ 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();
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CharsetMapping.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CharsetMapping.java
new file mode 100644
index 00000000..f7edcc4e
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CharsetMapping.java
@@ -0,0 +1,995 @@
+/*
+ Copyright (C) 2002-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.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.TreeMap;
+
+/**
+ * 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;
+
+ /**
+ * Map of MySQL-4.1 collation index to collation names
+ */
+ public static final String[] INDEX_TO_COLLATION;
+
+ /** Mapping of Java charset names to MySQL charset names */
+ private static final Map JAVA_TO_MYSQL_CHARSET_MAP;
+
+ 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;
+
+ private static final String NOT_USED = "ISO8859_1"; // punting for not-used character sets
+
+ public static final Map STATIC_CHARSET_TO_NUM_BYTES_MAP;
+
+ static {
+ HashMap tempNumBytesMap = new HashMap();
+
+ tempNumBytesMap.put("big5", new Integer(2));
+ tempNumBytesMap.put("dec8" , new Integer(1));
+ tempNumBytesMap.put("cp850", new Integer(1));
+ tempNumBytesMap.put("hp8", new Integer(1));
+ tempNumBytesMap.put("koi8r", new Integer(1));
+ tempNumBytesMap.put("latin1", new Integer(1));
+ tempNumBytesMap.put("latin2", new Integer(1));
+ tempNumBytesMap.put("swe7", new Integer(1));
+ tempNumBytesMap.put("ascii", new Integer(1));
+ tempNumBytesMap.put("ujis", new Integer(3));
+ tempNumBytesMap.put("sjis", new Integer(2));
+ tempNumBytesMap.put("hebrew", new Integer(1));
+ tempNumBytesMap.put("tis620", new Integer(1));
+ tempNumBytesMap.put("euckr", new Integer(2));
+ tempNumBytesMap.put("koi8u", new Integer(1));
+ tempNumBytesMap.put("gb2312", new Integer(2));
+ tempNumBytesMap.put("greek", new Integer(1));
+ tempNumBytesMap.put("cp1250", new Integer(1));
+ tempNumBytesMap.put("gbk", new Integer(2));
+ tempNumBytesMap.put("latin5", new Integer(1));
+ tempNumBytesMap.put("armscii8", new Integer(1));
+ tempNumBytesMap.put("utf8", new Integer(3));
+ tempNumBytesMap.put("ucs2", new Integer(2));
+ tempNumBytesMap.put("cp866", new Integer(1));
+ tempNumBytesMap.put("keybcs2", new Integer(1));
+ tempNumBytesMap.put("macce", new Integer(1));
+ tempNumBytesMap.put("macroman", new Integer(1));
+ tempNumBytesMap.put("cp852" , new Integer(1));
+ tempNumBytesMap.put("latin7", new Integer(1));
+ tempNumBytesMap.put("cp1251", new Integer(1));
+ tempNumBytesMap.put("cp1256" , new Integer(1));
+ tempNumBytesMap.put("cp1257", new Integer(1));
+ tempNumBytesMap.put("binary", new Integer(1));
+ tempNumBytesMap.put("geostd8", new Integer(1));
+ tempNumBytesMap.put("cp932", new Integer(2));
+ tempNumBytesMap.put("eucjpms", new Integer(3));
+
+ STATIC_CHARSET_TO_NUM_BYTES_MAP = Collections.unmodifiableMap(
+ tempNumBytesMap);
+
+ 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 = >4.1.0 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);
+
+ TreeMap ucMap = new TreeMap(String.CASE_INSENSITIVE_ORDER);
+
+ 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] = "ISO8859_1"; // punting for "dec8"
+ INDEX_TO_CHARSET[4] = "ISO8859_1"; // punting for "dos"
+ INDEX_TO_CHARSET[5] = getJavaEncodingForMysqlEncoding("german1",
+ null);
+ INDEX_TO_CHARSET[6] = "ISO8859_1"; // punting for "hp8"
+ INDEX_TO_CHARSET[7] = getJavaEncodingForMysqlEncoding("koi8_ru",
+ null);
+ INDEX_TO_CHARSET[8] = getJavaEncodingForMysqlEncoding("latin1",
+ null);
+ INDEX_TO_CHARSET[9] = getJavaEncodingForMysqlEncoding("latin2",
+ null);
+ INDEX_TO_CHARSET[10] = "ISO8859_1"; // punting for "swe7"
+ INDEX_TO_CHARSET[11] = getJavaEncodingForMysqlEncoding("usa7", null);
+ INDEX_TO_CHARSET[12] = getJavaEncodingForMysqlEncoding("ujis", null);
+ INDEX_TO_CHARSET[13] = getJavaEncodingForMysqlEncoding("sjis", null);
+ INDEX_TO_CHARSET[14] = getJavaEncodingForMysqlEncoding("cp1251",
+ null);
+ INDEX_TO_CHARSET[15] = getJavaEncodingForMysqlEncoding("danish",
+ null);
+ INDEX_TO_CHARSET[16] = getJavaEncodingForMysqlEncoding("hebrew",
+ null);
+
+ INDEX_TO_CHARSET[17] = NOT_USED; // not used in the server
+
+ INDEX_TO_CHARSET[18] = getJavaEncodingForMysqlEncoding("tis620",
+ null);
+ INDEX_TO_CHARSET[19] = getJavaEncodingForMysqlEncoding("euc_kr",
+ null);
+ INDEX_TO_CHARSET[20] = getJavaEncodingForMysqlEncoding("estonia",
+ null);
+ INDEX_TO_CHARSET[21] = getJavaEncodingForMysqlEncoding("hungarian",
+ null);
+ INDEX_TO_CHARSET[22] = "KOI8_R"; //punting for "koi8_ukr"
+ 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] = "ISO8859_1"; // punting "armscii8"
+ INDEX_TO_CHARSET[33] = getJavaEncodingForMysqlEncoding("utf8", null);
+ INDEX_TO_CHARSET[34] = "Cp1250"; // punting "win1250ch"
+ INDEX_TO_CHARSET[35] = getJavaEncodingForMysqlEncoding("ucs2", null);
+ INDEX_TO_CHARSET[36] = getJavaEncodingForMysqlEncoding("cp866",
+ null);
+ INDEX_TO_CHARSET[37] = "Cp895"; // punting "keybcs2"
+ INDEX_TO_CHARSET[38] = getJavaEncodingForMysqlEncoding("macce",
+ null);
+ INDEX_TO_CHARSET[39] = getJavaEncodingForMysqlEncoding("macroman",
+ null);
+ INDEX_TO_CHARSET[40] = "latin2"; // punting "pclatin2"
+ INDEX_TO_CHARSET[41] = getJavaEncodingForMysqlEncoding("latvian",
+ null);
+ INDEX_TO_CHARSET[42] = getJavaEncodingForMysqlEncoding("latvian1",
+ null);
+ INDEX_TO_CHARSET[43] = getJavaEncodingForMysqlEncoding("macce",
+ null);
+ INDEX_TO_CHARSET[44] = getJavaEncodingForMysqlEncoding("macce",
+ null);
+ INDEX_TO_CHARSET[45] = getJavaEncodingForMysqlEncoding("macce",
+ null);
+ INDEX_TO_CHARSET[46] = getJavaEncodingForMysqlEncoding("macce",
+ null);
+ INDEX_TO_CHARSET[47] = getJavaEncodingForMysqlEncoding("latin1",
+ null);
+ INDEX_TO_CHARSET[48] = getJavaEncodingForMysqlEncoding(
+ "latin1", null);
+ INDEX_TO_CHARSET[49] = getJavaEncodingForMysqlEncoding(
+ "latin1", null);
+ INDEX_TO_CHARSET[50] = getJavaEncodingForMysqlEncoding("cp1251",
+ null);
+ INDEX_TO_CHARSET[51] = getJavaEncodingForMysqlEncoding(
+ "cp1251", null);
+ INDEX_TO_CHARSET[52] = getJavaEncodingForMysqlEncoding(
+ "cp1251", null);
+ INDEX_TO_CHARSET[53] = getJavaEncodingForMysqlEncoding(
+ "macroman", null);
+ INDEX_TO_CHARSET[54] = getJavaEncodingForMysqlEncoding(
+ "macroman", null);
+ INDEX_TO_CHARSET[55] = getJavaEncodingForMysqlEncoding(
+ "macroman", null);
+ INDEX_TO_CHARSET[56] = getJavaEncodingForMysqlEncoding(
+ "macroman", null);
+ INDEX_TO_CHARSET[57] = getJavaEncodingForMysqlEncoding("cp1256",
+ null);
+
+ INDEX_TO_CHARSET[58] = NOT_USED; // not used
+ INDEX_TO_CHARSET[59] = NOT_USED; // not used
+ INDEX_TO_CHARSET[60] = NOT_USED; // not used
+ INDEX_TO_CHARSET[61] = NOT_USED; // not used
+ INDEX_TO_CHARSET[62] = NOT_USED; // not used
+
+ INDEX_TO_CHARSET[63] = getJavaEncodingForMysqlEncoding("binary",
+ null);
+ INDEX_TO_CHARSET[64] = "ISO8859_2"; // punting "armscii"
+ 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] = "US-ASCII"; // punting for "dec8"
+ INDEX_TO_CHARSET[70] = getJavaEncodingForMysqlEncoding("greek",
+ null);
+ INDEX_TO_CHARSET[71] = getJavaEncodingForMysqlEncoding("hebrew",
+ null);
+ INDEX_TO_CHARSET[72] = "US-ASCII"; // punting for "hp8"
+ INDEX_TO_CHARSET[73] = "Cp895"; // punting for "keybcs2"
+ INDEX_TO_CHARSET[74] = getJavaEncodingForMysqlEncoding("koi8r",
+ null);
+ INDEX_TO_CHARSET[75] = "KOI8_r"; // punting for koi8ukr"
+
+ INDEX_TO_CHARSET[76] = NOT_USED; // not used
+
+ INDEX_TO_CHARSET[77] = getJavaEncodingForMysqlEncoding("latin2",
+ null);
+ INDEX_TO_CHARSET[78] = getJavaEncodingForMysqlEncoding("latin5",
+ 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] = "ISO8859_1"; // punting for "swe7"
+ INDEX_TO_CHARSET[83] = getJavaEncodingForMysqlEncoding("utf8", null);
+ INDEX_TO_CHARSET[84] = getJavaEncodingForMysqlEncoding("big5", null);
+ INDEX_TO_CHARSET[85] = getJavaEncodingForMysqlEncoding("euckr",
+ 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] = "US-ASCII"; //punting for "geostd8"
+ INDEX_TO_CHARSET[93] = "US-ASCII"; // punting for "geostd8"
+ INDEX_TO_CHARSET[94] = getJavaEncodingForMysqlEncoding("latin1",
+ null);
+ INDEX_TO_CHARSET[95] = getJavaEncodingForMysqlEncoding("cp932",
+ null);
+ INDEX_TO_CHARSET[96] = getJavaEncodingForMysqlEncoding("cp932",
+ null);
+ INDEX_TO_CHARSET[97] = getJavaEncodingForMysqlEncoding("eucjpms",
+ null);
+ INDEX_TO_CHARSET[98] = getJavaEncodingForMysqlEncoding("eucjpms",
+ null);
+
+ for (int i = 99; i < 128; i++) {
+ INDEX_TO_CHARSET[i] = NOT_USED; // not used
+ }
+
+ INDEX_TO_CHARSET[128] = getJavaEncodingForMysqlEncoding("ucs2",
+ null);
+ INDEX_TO_CHARSET[129] = getJavaEncodingForMysqlEncoding("ucs2",
+ 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);
+
+ for (int i = 147; i < 192; i++) {
+ INDEX_TO_CHARSET[i] = NOT_USED; // not used
+ }
+
+ INDEX_TO_CHARSET[192] = getJavaEncodingForMysqlEncoding("utf8",
+ null);
+ INDEX_TO_CHARSET[193] = getJavaEncodingForMysqlEncoding("utf8",
+ 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);
+
+ // Sanity check
+
+ for (int i = 1; i < INDEX_TO_CHARSET.length; i++) {
+ if (INDEX_TO_CHARSET[i] == null) {
+ throw new RuntimeException("Assertion failure: No mapping from charset index " + i + " to a Java character set");
+ }
+ }
+ } catch (SQLException sqlEx) {
+ // ignore, it won't happen in this case
+ }
+
+ INDEX_TO_COLLATION = new String[211];
+
+ INDEX_TO_COLLATION[1] = "big5_chinese_ci";
+ INDEX_TO_COLLATION[2] = "latin2_czech_cs";
+ INDEX_TO_COLLATION[3] = "dec8_swedish_ci";
+ INDEX_TO_COLLATION[4] = "cp850_general_ci";
+ INDEX_TO_COLLATION[5] = "latin1_german1_ci";
+ INDEX_TO_COLLATION[6] = "hp8_english_ci";
+ INDEX_TO_COLLATION[7] = "koi8r_general_ci";
+ INDEX_TO_COLLATION[8] = "latin1_swedish_ci";
+ INDEX_TO_COLLATION[9] = "latin2_general_ci";
+ INDEX_TO_COLLATION[10] = "swe7_swedish_ci";
+ INDEX_TO_COLLATION[11] = "ascii_general_ci";
+ INDEX_TO_COLLATION[12] = "ujis_japanese_ci";
+ INDEX_TO_COLLATION[13] = "sjis_japanese_ci";
+ INDEX_TO_COLLATION[14] = "cp1251_bulgarian_ci";
+ INDEX_TO_COLLATION[15] = "latin1_danish_ci";
+ INDEX_TO_COLLATION[16] = "hebrew_general_ci";
+ INDEX_TO_COLLATION[18] = "tis620_thai_ci";
+ INDEX_TO_COLLATION[19] = "euckr_korean_ci";
+ INDEX_TO_COLLATION[20] = "latin7_estonian_cs";
+ INDEX_TO_COLLATION[21] = "latin2_hungarian_ci";
+ INDEX_TO_COLLATION[22] = "koi8u_general_ci";
+ INDEX_TO_COLLATION[23] = "cp1251_ukrainian_ci";
+ INDEX_TO_COLLATION[24] = "gb2312_chinese_ci";
+ INDEX_TO_COLLATION[25] = "greek_general_ci";
+ INDEX_TO_COLLATION[26] = "cp1250_general_ci";
+ INDEX_TO_COLLATION[27] = "latin2_croatian_ci";
+ INDEX_TO_COLLATION[28] = "gbk_chinese_ci";
+ INDEX_TO_COLLATION[29] = "cp1257_lithuanian_ci";
+ INDEX_TO_COLLATION[30] = "latin5_turkish_ci";
+ INDEX_TO_COLLATION[31] = "latin1_german2_ci";
+ INDEX_TO_COLLATION[32] = "armscii8_general_ci";
+ INDEX_TO_COLLATION[33] = "utf8_general_ci";
+ INDEX_TO_COLLATION[34] = "cp1250_czech_cs";
+ INDEX_TO_COLLATION[35] = "ucs2_general_ci";
+ INDEX_TO_COLLATION[36] = "cp866_general_ci";
+ INDEX_TO_COLLATION[37] = "keybcs2_general_ci";
+ INDEX_TO_COLLATION[38] = "macce_general_ci";
+ INDEX_TO_COLLATION[39] = "macroman_general_ci";
+ INDEX_TO_COLLATION[40] = "cp852_general_ci";
+ INDEX_TO_COLLATION[41] = "latin7_general_ci";
+ INDEX_TO_COLLATION[42] = "latin7_general_cs";
+ INDEX_TO_COLLATION[43] = "macce_bin";
+ INDEX_TO_COLLATION[44] = "cp1250_croatian_ci";
+ INDEX_TO_COLLATION[47] = "latin1_bin";
+ INDEX_TO_COLLATION[48] = "latin1_general_ci";
+ INDEX_TO_COLLATION[49] = "latin1_general_cs";
+ INDEX_TO_COLLATION[50] = "cp1251_bin";
+ INDEX_TO_COLLATION[51] = "cp1251_general_ci";
+ INDEX_TO_COLLATION[52] = "cp1251_general_cs";
+ INDEX_TO_COLLATION[53] = "macroman_bin";
+ INDEX_TO_COLLATION[57] = "cp1256_general_ci";
+ INDEX_TO_COLLATION[58] = "cp1257_bin";
+ INDEX_TO_COLLATION[59] = "cp1257_general_ci";
+ INDEX_TO_COLLATION[63] = "binary";
+ INDEX_TO_COLLATION[64] = "armscii8_bin";
+ INDEX_TO_COLLATION[65] = "ascii_bin";
+ INDEX_TO_COLLATION[66] = "cp1250_bin";
+ INDEX_TO_COLLATION[67] = "cp1256_bin";
+ INDEX_TO_COLLATION[68] = "cp866_bin";
+ INDEX_TO_COLLATION[69] = "dec8_bin";
+ INDEX_TO_COLLATION[70] = "greek_bin";
+ INDEX_TO_COLLATION[71] = "hebrew_bin";
+ INDEX_TO_COLLATION[72] = "hp8_bin";
+ INDEX_TO_COLLATION[73] = "keybcs2_bin";
+ INDEX_TO_COLLATION[74] = "koi8r_bin";
+ INDEX_TO_COLLATION[75] = "koi8u_bin";
+ INDEX_TO_COLLATION[77] = "latin2_bin";
+ INDEX_TO_COLLATION[78] = "latin5_bin";
+ INDEX_TO_COLLATION[79] = "latin7_bin";
+ INDEX_TO_COLLATION[80] = "cp850_bin";
+ INDEX_TO_COLLATION[81] = "cp852_bin";
+ INDEX_TO_COLLATION[82] = "swe7_bin";
+ INDEX_TO_COLLATION[83] = "utf8_bin";
+ INDEX_TO_COLLATION[84] = "big5_bin";
+ INDEX_TO_COLLATION[85] = "euckr_bin";
+ INDEX_TO_COLLATION[86] = "gb2312_bin";
+ INDEX_TO_COLLATION[87] = "gbk_bin";
+ INDEX_TO_COLLATION[88] = "sjis_bin";
+ INDEX_TO_COLLATION[89] = "tis620_bin";
+ INDEX_TO_COLLATION[90] = "ucs2_bin";
+ INDEX_TO_COLLATION[91] = "ujis_bin";
+ INDEX_TO_COLLATION[92] = "geostd8_general_ci";
+ INDEX_TO_COLLATION[93] = "geostd8_bin";
+ INDEX_TO_COLLATION[94] = "latin1_spanish_ci";
+ INDEX_TO_COLLATION[95] = "cp932_japanese_ci";
+ INDEX_TO_COLLATION[96] = "cp932_bin";
+ INDEX_TO_COLLATION[97] = "eucjpms_japanese_ci";
+ INDEX_TO_COLLATION[98] = "eucjpms_bin";
+ INDEX_TO_COLLATION[99] = "cp1250_polish_ci";
+ INDEX_TO_COLLATION[128] = "ucs2_unicode_ci";
+ INDEX_TO_COLLATION[129] = "ucs2_icelandic_ci";
+ INDEX_TO_COLLATION[130] = "ucs2_latvian_ci";
+ INDEX_TO_COLLATION[131] = "ucs2_romanian_ci";
+ INDEX_TO_COLLATION[132] = "ucs2_slovenian_ci";
+ INDEX_TO_COLLATION[133] = "ucs2_polish_ci";
+ INDEX_TO_COLLATION[134] = "ucs2_estonian_ci";
+ INDEX_TO_COLLATION[135] = "ucs2_spanish_ci";
+ INDEX_TO_COLLATION[136] = "ucs2_swedish_ci";
+ INDEX_TO_COLLATION[137] = "ucs2_turkish_ci";
+ INDEX_TO_COLLATION[138] = "ucs2_czech_ci";
+ INDEX_TO_COLLATION[139] = "ucs2_danish_ci";
+ INDEX_TO_COLLATION[140] = "ucs2_lithuanian_ci ";
+ INDEX_TO_COLLATION[141] = "ucs2_slovak_ci";
+ INDEX_TO_COLLATION[142] = "ucs2_spanish2_ci";
+ INDEX_TO_COLLATION[143] = "ucs2_roman_ci";
+ INDEX_TO_COLLATION[144] = "ucs2_persian_ci";
+ INDEX_TO_COLLATION[145] = "ucs2_esperanto_ci";
+ INDEX_TO_COLLATION[146] = "ucs2_hungarian_ci";
+ INDEX_TO_COLLATION[192] = "utf8_unicode_ci";
+ INDEX_TO_COLLATION[193] = "utf8_icelandic_ci";
+ INDEX_TO_COLLATION[194] = "utf8_latvian_ci";
+ INDEX_TO_COLLATION[195] = "utf8_romanian_ci";
+ INDEX_TO_COLLATION[196] = "utf8_slovenian_ci";
+ INDEX_TO_COLLATION[197] = "utf8_polish_ci";
+ INDEX_TO_COLLATION[198] = "utf8_estonian_ci";
+ INDEX_TO_COLLATION[199] = "utf8_spanish_ci";
+ INDEX_TO_COLLATION[200] = "utf8_swedish_ci";
+ INDEX_TO_COLLATION[201] = "utf8_turkish_ci";
+ INDEX_TO_COLLATION[202] = "utf8_czech_ci";
+ INDEX_TO_COLLATION[203] = "utf8_danish_ci";
+ INDEX_TO_COLLATION[204] = "utf8_lithuanian_ci ";
+ INDEX_TO_COLLATION[205] = "utf8_slovak_ci";
+ INDEX_TO_COLLATION[206] = "utf8_spanish2_ci";
+ INDEX_TO_COLLATION[207] = "utf8_roman_ci";
+ INDEX_TO_COLLATION[208] = "utf8_persian_ci";
+ INDEX_TO_COLLATION[209] = "utf8_esperanto_ci";
+ INDEX_TO_COLLATION[210] = "utf8_hungarian_ci";
+
+ 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);
+ }
+
+ public 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);
+ }
+
+ public 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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Charsets.properties b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Charsets.properties
new file mode 100644
index 00000000..ee83a930
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Charsets.properties
@@ -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
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Clob.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Clob.java
new file mode 100644
index 00000000..3b30f491
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Clob.java
@@ -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();
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CommunicationsException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CommunicationsException.java
new file mode 100644
index 00000000..a3856a9e
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CommunicationsException.java
@@ -0,0 +1,230 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.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;
+
+ private boolean streamingResultSetInPlay = false;
+
+ 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 (this.streamingResultSetInPlay) {
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.ClientWasStreaming")); //$NON-NLS-1$
+ } else {
+ if (serverTimeoutSeconds != 0) {
+ if (timeSinceLastPacket > serverTimeoutSeconds) {
+ dueToTimeout = DUE_TO_TIMEOUT_TRUE;
+
+ timeoutMessageBuf = new StringBuffer();
+
+ timeoutMessageBuf.append(Messages
+ .getString("CommunicationsException.2")); //$NON-NLS-1$
+
+ if (!isInteractiveClient) {
+ timeoutMessageBuf.append(Messages
+ .getString("CommunicationsException.3")); //$NON-NLS-1$
+ } else {
+ timeoutMessageBuf.append(Messages
+ .getString("CommunicationsException.4")); //$NON-NLS-1$
+ }
+
+ }
+ } else if (timeSinceLastPacket > DEFAULT_WAIT_TIMEOUT_SECONDS) {
+ dueToTimeout = DUE_TO_TIMEOUT_MAYBE;
+
+ timeoutMessageBuf = new StringBuffer();
+
+ timeoutMessageBuf.append(Messages
+ .getString("CommunicationsException.5")); //$NON-NLS-1$
+ timeoutMessageBuf.append(Messages
+ .getString("CommunicationsException.6")); //$NON-NLS-1$
+ timeoutMessageBuf.append(Messages
+ .getString("CommunicationsException.7")); //$NON-NLS-1$
+ timeoutMessageBuf.append(Messages
+ .getString("CommunicationsException.8")); //$NON-NLS-1$
+ }
+
+ if (dueToTimeout == DUE_TO_TIMEOUT_TRUE
+ || dueToTimeout == DUE_TO_TIMEOUT_MAYBE) {
+
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.9")); //$NON-NLS-1$
+ exceptionMessageBuf.append(timeSinceLastPacket);
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.10")); //$NON-NLS-1$
+
+ if (timeoutMessageBuf != null) {
+ exceptionMessageBuf.append(timeoutMessageBuf);
+ }
+
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.11")); //$NON-NLS-1$
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.12")); //$NON-NLS-1$
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.13")); //$NON-NLS-1$
+
+ } else {
+ //
+ // Attempt to determine the reason for the underlying exception
+ // (we can only make a best-guess here)
+ //
+
+ if (underlyingException instanceof BindException) {
+ if (conn.getLocalSocketAddress() != null &&
+ !Util.interfaceExists(conn.getLocalSocketAddress())) {
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.19a")); //$NON-NLS-1$
+ } else {
+ // too many client connections???
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.14")); //$NON-NLS-1$
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.15")); //$NON-NLS-1$
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.16")); //$NON-NLS-1$
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.17")); //$NON-NLS-1$
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.18")); //$NON-NLS-1$
+ exceptionMessageBuf.append(Messages
+ .getString("CommunicationsException.19")); //$NON-NLS-1$
+ }
+ }
+ }
+ }
+
+ if (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;
+ }
+
+ protected void setWasStreamingResults() {
+ this.streamingResultSetInPlay = true;
+ }
+
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CompressedInputStream.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CompressedInputStream.java
new file mode 100644
index 00000000..c1bab03b
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/CompressedInputStream.java
@@ -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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Connection.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Connection.java
new file mode 100644
index 00000000..6b0548d9
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Connection.java
@@ -0,0 +1,5994 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.log.Log;
+import com.mysql.jdbc.log.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.Constructor;
+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.
+ * + * 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. + *
+ * + * @author Mark Matthews + * @version $Id: Connection.java 6599 2007-10-02 22:34:20Z mmatthews $ + * @see java.sql.Connection + */ +public class Connection extends ConnectionProperties implements + java.sql.Connection { + private static final String JDBC_LOCAL_CHARACTER_SET_RESULTS = "jdbc.local.character_set_results"; + + /** + * Used as a key for caching callable statements which (may) depend on + * current catalog...In 5.0.x, they don't (currently), but stored procedure + * names soon will, so current catalog is a (hidden) component of the name. + */ + class CompoundCacheKey { + String componentOne; + + String componentTwo; + + int hashCode; + + CompoundCacheKey(String partOne, String partTwo) { + this.componentOne = partOne; + this.componentTwo = partTwo; + + // Handle first component (in most cases, currentCatalog) + // being NULL.... + this.hashCode = (((this.componentOne != null) ? this.componentOne + : "") + this.componentTwo).hashCode(); + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + public boolean equals(Object obj) { + if (obj instanceof CompoundCacheKey) { + CompoundCacheKey another = (CompoundCacheKey) obj; + + boolean firstPartEqual = false; + + if (this.componentOne == null) { + firstPartEqual = (another.componentOne == null); + } else { + firstPartEqual = this.componentOne + .equals(another.componentOne); + } + + return (firstPartEqual && this.componentTwo + .equals(another.componentTwo)); + } + + return false; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + public int hashCode() { + return this.hashCode; + } + } + + /** + * 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)); + + boolean createdNamedTimer = false; + + // Use reflection magic to try this on JDK's 1.5 and newer, fallback to non-named + // timer on older VMs. + try { + Constructor ctr = Timer.class.getConstructor(new Class[] {String.class, Boolean.TYPE}); + + cancelTimer = (Timer)ctr.newInstance(new Object[] { "MySQL Statement Cancellation Timer", Boolean.TRUE}); + createdNamedTimer = true; + } catch (Throwable t) { + createdNamedTimer = false; + } + + if (!createdNamedTimer) { + cancelTimer = new Timer(true); + } + } + + 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; + + /** Cache of ResultSet metadata */ + protected LRUCache resultSetMetadataCache; + + /** The timezone of the server */ + private TimeZone serverTimezoneTZ = null; + + /** The map of server variables that we retrieve at connection init. */ + private Map serverVariables = null; + + private long shortestQueryTimeMs = Long.MAX_VALUE; + + /** A map of statements that have had setMaxRows() called on them */ + private Map statementsUsingMaxRows; + + private double totalQueryTimeMs = 0; + + /** Are transactions supported by the MySQL server we are connected to? */ + private boolean transactionsSupported = false; + + /** + * The type map for UDTs (not implemented, but used by some third-party + * vendors, most notably IBM WebSphere) + */ + private Map typeMap; + + /** Has ANSI_QUOTES been enabled on the server? */ + private boolean useAnsiQuotes = false; + + /** The user we're connected as */ + private String user = null; + + /** + * Should we use server-side prepared statements? (auto-detected, but can be + * disabled by user) + */ + private boolean useServerPreparedStmts = false; + + private LRUCache serverSideStatementCheckCache; + + private LRUCache serverSideStatementCache; + private Calendar sessionCalendar; + private Calendar utcCalendar; + + private String origHostToConnectTo; + + 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.connectionCreationTimeMillis = System.currentTimeMillis(); + this.pointOfOrigin = new Throwable(); + + // Stash away for later, used to clone this connection for Statement.cancel + // and Statement.setQueryTimeout(). + // + + this.origHostToConnectTo = hostToConnectTo; + this.origPortToConnectTo = portToConnectTo; + this.origDatabaseToConnectTo = databaseToConnectTo; + + try { + Blob.class.getMethod("truncate", new Class[] {Long.TYPE}); + + this.isRunningOnJDK13 = false; + } catch (NoSuchMethodException nsme) { + this.isRunningOnJDK13 = true; + } + + this.sessionCalendar = new GregorianCalendar(); + this.utcCalendar = new GregorianCalendar(); + this.utcCalendar.setTimeZone(TimeZone.getTimeZone("GMT")); + + // + // Normally, this code would be in initializeDriverProperties, + // but we need to do this as early as possible, so we can start + // logging to the 'correct' place as early as possible...this.log + // points to 'NullLogger' for every connection at startup to avoid + // NPEs and the overhead of checking for NULL at every logging call. + // + // We will reset this to the configured logger during properties + // initialization. + // + this.log = LogFactory.getLogger(getLogger(), LOGGER_INSTANCE_NAME); + + // We store this per-connection, due to static synchronization + // issues in Java's built-in TimeZone class... + this.defaultTimeZone = Util.getDefaultTimeZone(); + + if ("GMT".equalsIgnoreCase(this.defaultTimeZone.getID())) { + this.isClientTzUTC = true; + } else { + this.isClientTzUTC = false; + } + + this.openStatements = new HashMap(); + this.serverVariables = new HashMap(); + this.hostList = new ArrayList(); + + if (hostToConnectTo == null) { + this.host = "localhost"; + this.hostList.add(this.host); + } else if (hostToConnectTo.indexOf(",") != -1) { + // multiple hosts separated by commas (failover) + StringTokenizer hostTokenizer = new StringTokenizer( + hostToConnectTo, ",", false); + + while (hostTokenizer.hasMoreTokens()) { + this.hostList.add(hostTokenizer.nextToken().trim()); + } + } else { + this.host = hostToConnectTo; + this.hostList.add(this.host); + } + + this.hostListSize = this.hostList.size(); + this.port = portToConnectTo; + + if (databaseToConnectTo == null) { + databaseToConnectTo = ""; + } + + this.database = databaseToConnectTo; + this.myURL = url; + this.user = info.getProperty(NonRegisteringDriver.USER_PROPERTY_KEY); + this.password = info + .getProperty(NonRegisteringDriver.PASSWORD_PROPERTY_KEY); + + if ((this.user == null) || this.user.equals("")) { + this.user = ""; + } + + if (this.password == null) { + this.password = ""; + } + + this.props = info; + initializeDriverProperties(info); + + try { + createNewIO(false); + this.dbmd = 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); + } + + /** + * @see Connection#prepareStatement(String, int) + */ + public java.sql.PreparedStatement clientPrepareStatement(String sql, + int autoGenKeyIndex) throws SQLException { + java.sql.PreparedStatement pStmt = clientPrepareStatement(sql); + + ((com.mysql.jdbc.PreparedStatement) pStmt) + .setRetrieveGeneratedKeys(autoGenKeyIndex == java.sql.Statement.RETURN_GENERATED_KEYS); + + return pStmt; + } + + /** + * DOCUMENT ME! + * + * @param sql + * DOCUMENT ME! + * @param resultSetType + * DOCUMENT ME! + * @param resultSetConcurrency + * DOCUMENT ME! + * @return DOCUMENT ME! + * @throws SQLException + * DOCUMENT ME! + */ + public 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; + } + + + 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. + *+ * Note: MySQL does not support transactions, so this method is a + * no-op. + *
+ * + * @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) { + if (getUseLocalSessionState() && versionMeetsMinimum(5, 0, 0)) { + if (!this.io.inTransactionOnServer()) { + return; // effectively a no-op + } + } + + execSQL(null, "commit", -1, null, + java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY, false, + this.database, 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 { + + // Fault injection for testing server character set indices + + if (props != null && props.getProperty("com.mysql.jdbc.faultInjection.serverCharsetIndex") != null) { + this.io.serverCharsetIndex = Integer.parseInt( + props.getProperty( + "com.mysql.jdbc.faultInjection.serverCharsetIndex")); + } + + String serverEncodingToSet = + CharsetMapping.INDEX_TO_CHARSET[this.io.serverCharsetIndex]; + + if (serverEncodingToSet == null || serverEncodingToSet.length() == 0) { + if (realJavaEncoding != null) { + // user knows best, try it + setEncoding(realJavaEncoding); + } else { + throw SQLError.createSQLException( + "Unknown initial character set index '" + + this.io.serverCharsetIndex + + "' received from server. Initial client character set can be forced via the 'characterEncoding' property.", + SQLError.SQL_STATE_GENERAL_ERROR); + } + } + + // "latin1" on MySQL-4.1.0+ is actually CP1252, not ISO8859_1 + if (versionMeetsMinimum(4, 1, 0) && + "ISO8859_1".equalsIgnoreCase(serverEncodingToSet)) { + serverEncodingToSet = "Cp1252"; + } + + setEncoding(serverEncodingToSet); + + } catch (ArrayIndexOutOfBoundsException outOfBoundsEx) { + if (realJavaEncoding != null) { + // user knows best, try it + setEncoding(realJavaEncoding); + } else { + throw SQLError.createSQLException( + "Unknown initial character set index '" + + this.io.serverCharsetIndex + + "' received from server. Initial client character set can be forced via the 'characterEncoding' property.", + SQLError.SQL_STATE_GENERAL_ERROR); + } + } + + if (getEncoding() == null) { + // punt? + setEncoding("ISO8859_1"); + } + + // + // Has the user has 'forced' the character encoding via + // driver properties? + // + if (getUseUnicode()) { + if (realJavaEncoding != null) { + + // + // Now, inform the server what character set we + // will be using from now-on... + // + if (realJavaEncoding.equalsIgnoreCase("UTF-8") + || realJavaEncoding.equalsIgnoreCase("UTF8")) { + // charset names are case-sensitive + + if (!getUseOldUTF8Behavior()) { + if (!characterSetNamesMatches("utf8")) { + 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) { + + if (!characterSetNamesMatches(mysqlEncodingName)) { + 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); + + if (!characterSetNamesMatches(mysqlEncodingName)) { + 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 + // + + String onServer = null; + boolean isNullOnServer = false; + + if (this.serverVariables != null) { + onServer = (String)this.serverVariables.get("character_set_results"); + + isNullOnServer = onServer == null || "NULL".equalsIgnoreCase(onServer) || onServer.length() == 0; + } + + if (getCharacterSetResults() == null) { + + // + // Only send if needed, if we're caching server variables + // we -have- to send, because we don't know what it was + // before we cached them. + // + if (!isNullOnServer) { + execSQL(null, "SET character_set_results = NULL", -1, null, + java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY, false, + this.database, true, + false); + if (!this.usingCachedConfig) { + this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, null); + } + } else { + if (!this.usingCachedConfig) { + this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer); + } + } + } else { + String charsetResults = getCharacterSetResults(); + String mysqlEncodingName = null; + + if ("UTF-8".equalsIgnoreCase(charsetResults) + || "UTF8".equalsIgnoreCase(charsetResults)) { + mysqlEncodingName = "utf8"; + } else { + mysqlEncodingName = CharsetMapping + .getMysqlEncodingForJavaEncoding(charsetResults + .toUpperCase(Locale.ENGLISH), this); + } + + // + // Only change the value if needed + // + + if (!mysqlEncodingName.equalsIgnoreCase( + (String)this.serverVariables.get("character_set_results"))) { + StringBuffer setBuf = new StringBuffer( + "SET character_set_results = ".length() + + mysqlEncodingName.length()); + setBuf.append("SET character_set_results = ").append( + mysqlEncodingName); + + execSQL(null, setBuf.toString(), -1, null, + java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY, false, + this.database, true, false); + + if (!this.usingCachedConfig) { + this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, + mysqlEncodingName); + } + } else { + if (!this.usingCachedConfig) { + this.serverVariables.put(JDBC_LOCAL_CHARACTER_SET_RESULTS, onServer); + } + } + } + + if (getConnectionCollation() != null) { + StringBuffer setBuf = new StringBuffer( + "SET collation_connection = ".length() + + getConnectionCollation().length()); + setBuf.append("SET collation_connection = ").append( + getConnectionCollation()); + + execSQL(null, setBuf.toString(), -1, null, + java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY, false, + this.database, 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; + } + + private boolean characterSetNamesMatches(String mysqlEncodingName) { + // set names is equivalent to character_set_client ..._results and ..._connection, + // but we set _results later, so don't check it here. + + return (mysqlEncodingName != null && + mysqlEncodingName.equalsIgnoreCase((String)this.serverVariables.get("character_set_client")) && + mysqlEncodingName.equalsIgnoreCase((String)this.serverVariables.get("character_set_connection"))); + } + + /** + * 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.connectionId = this.io.getThreadId(); + this.isClosed = false; + + // save state from old connection + boolean oldAutoCommit = getAutoCommit(); + int oldIsolationLevel = this.isolationLevel; + boolean oldReadOnly = isReadOnly(); + String oldCatalog = getCatalog(); + + // Server properties might be different + // from previous connection, so initialize + // again... + initializePropsFromServer(); + + if (isForReconnect) { + // Restore state from old connection + setAutoCommit(oldAutoCommit); + + if (this.hasIsolationLevels) { + setTransactionIsolation(oldIsolationLevel); + } + + setCatalog(oldCatalog); + } + + if (hostIndex != 0) { + setFailedOverState(); + queriesIssuedFailedOverCopy = 0; + } else { + this.failedOver = false; + queriesIssuedFailedOverCopy = 0; + + if (this.hostListSize > 1) { + setReadOnlyInternal(false); + } else { + setReadOnlyInternal(oldReadOnly); + } + } + + connectionGood = true; + + break; // low-level connection succeeded + } catch (Exception EEE) { + if (this.io != null) { + this.io.forceClose(); + } + + connectionNotEstablishedBecause = EEE; + + connectionGood = false; + + if (EEE instanceof SQLException) { + SQLException sqlEx = (SQLException)EEE; + + String sqlState = sqlEx.getSQLState(); + + // If this isn't a communications failure, it will probably never succeed, so + // give up right here and now .... + if ((sqlState == null) + || !sqlState + .equals(SQLError.SQL_STATE_COMMUNICATION_LINK_FAILURE)) { + throw sqlEx; + } + } + + // Check next host, it might be up... + if (getRoundRobinLoadBalance()) { + hostIndex = getNextRoundRobinHostIndex(getURL(), + this.hostList) - 1 /* incremented by for loop next time around */; + } else if ((this.hostListSize - 1) == hostIndex) { + throw 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.connectionId = this.io.getThreadId(); + this.isClosed = false; + + // save state from old connection + boolean oldAutoCommit = getAutoCommit(); + int oldIsolationLevel = this.isolationLevel; + boolean oldReadOnly = isReadOnly(); + String oldCatalog = getCatalog(); + + // Server properties might be different + // from previous connection, so initialize + // again... + initializePropsFromServer(); + + if (isForReconnect) { + // Restore state from old connection + setAutoCommit(oldAutoCommit); + + if (this.hasIsolationLevels) { + setTransactionIsolation(oldIsolationLevel); + } + + setCatalog(oldCatalog); + } + + connectionGood = true; + + if (hostIndex != 0) { + setFailedOverState(); + queriesIssuedFailedOverCopy = 0; + } else { + this.failedOver = false; + queriesIssuedFailedOverCopy = 0; + + if (this.hostListSize > 1) { + setReadOnlyInternal(false); + } else { + setReadOnlyInternal(oldReadOnly); + } + } + + break; + } catch (Exception EEE) { + connectionException = EEE; + connectionGood = false; + + // Check next host, it might be up... + if (getRoundRobinLoadBalance()) { + hostIndex = getNextRoundRobinHostIndex(getURL(), + this.hostList) - 1 /* incremented by for loop next time around */; + } + } + + if (connectionGood) { + break; + } + + if (attemptCount > 0) { + try { + Thread.sleep((long) timeout * 1000); + } catch (InterruptedException IE) { + ; + } + } + } // 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; + setReadOnlyInternal(false); + } + } finally { + this.executingFailoverReconnect = false; + } + } + } + + if ((getHighAvailability() || this.failedOver) + && (this.autoCommit || getAutoReconnectForPools()) + && this.needsPing && !isBatch) { + try { + pingInternal(false); + + this.needsPing = false; + } catch (Exception Ex) { + createNewIO(true); + } + } + + try { + if (packet == null) { + String encoding = null; + + if (getUseUnicode()) { + encoding = getEncoding(); + } + + return this.io.sqlQueryDirect(callingStatement, sql, + encoding, null, maxRows, 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. + *+ * Note: MySQL's notion of catalogs are individual databases. + *
+ * + * @return the current catalog name or null + * @exception SQLException + * if a database access error occurs + */ + public String getCatalog() throws SQLException { + return this.database; + } + + /** + * @return Returns the characterSetMetadata. + */ + protected String getCharacterSetMetadata() { + return characterSetMetadata; + } + + /** + * Returns the locally mapped instance of a charset converter (to avoid + * overhead of static synchronization). + * + * @param javaEncodingName + * the encoding name to retrieve + * @return a character converter, or null if one couldn't be mapped. + */ + SingleByteCharsetConverter getCharsetConverter( + String javaEncodingName) throws SQLException { + if (javaEncodingName == null) { + return null; + } + + if (this.usePlatformCharsetConverters) { + return null; // we'll use Java's built-in routines for this + // they're finally fast enough + } + + SingleByteCharsetConverter converter = null; + + synchronized (this.charsetConverterMap) { + Object asObject = this.charsetConverterMap + .get(javaEncodingName); + + if (asObject == CHARSET_CONVERTER_NOT_AVAILABLE_MARKER) { + return null; + } + + converter = (SingleByteCharsetConverter)asObject; + + if (converter == null) { + try { + converter = SingleByteCharsetConverter.getInstance( + javaEncodingName, this); + + if (converter == null) { + this.charsetConverterMap.put(javaEncodingName, + CHARSET_CONVERTER_NOT_AVAILABLE_MARKER); + } else { + this.charsetConverterMap.put(javaEncodingName, converter); + } + } catch (UnsupportedEncodingException unsupEncEx) { + this.charsetConverterMap.put(javaEncodingName, + CHARSET_CONVERTER_NOT_AVAILABLE_MARKER); + + converter = null; + } + } + } + + return converter; + } + + /** + * Returns the Java character encoding name for the given MySQL server + * charset index + * + * @param charsetIndex + * @return the Java character encoding name for the given MySQL server + * charset index + * @throws SQLException + * if the character set index isn't known by the driver + */ + protected String getCharsetNameForIndex(int charsetIndex) + throws SQLException { + String charsetName = null; + + if (getUseOldUTF8Behavior()) { + return getEncoding(); + } + + if (charsetIndex != MysqlDefs.NO_CHARSET_INFO) { + try { + charsetName = this.indexToCharsetMapping[charsetIndex]; + + if ("sjis".equalsIgnoreCase(charsetName) || + "MS932".equalsIgnoreCase(charsetName) /* for JDK6 */) { + // Use our encoding so that code pages like Cp932 work + if (CharsetMapping.isAliasForSjis(getEncoding())) { + charsetName = getEncoding(); + } + } + } catch (ArrayIndexOutOfBoundsException outOfBoundsEx) { + throw SQLError.createSQLException( + "Unknown character set index for field '" + + charsetIndex + "' received from server.", + SQLError.SQL_STATE_GENERAL_ERROR); + } + + // Punt + if (charsetName == null) { + charsetName = getEncoding(); + } + } else { + charsetName = getEncoding(); + } + + return charsetName; + } + + /** + * DOCUMENT ME! + * + * @return Returns the defaultTimeZone. + */ + protected TimeZone getDefaultTimeZone() { + return this.defaultTimeZone; + } + + /** + * @see Connection#getHoldability() + */ + public int getHoldability() throws SQLException { + return java.sql.ResultSet.CLOSE_CURSORS_AT_COMMIT; + } + + long getId() { + return this.connectionId; + } + + /** + * NOT JDBC-Compliant, but clients can use this method to determine how long + * this connection has been idle. This time (reported in milliseconds) is + * updated once a query has completed. + * + * @return number of ms that this connection has been idle, 0 if the driver + * is busy retrieving results. + */ + public long getIdleFor() { + if (this.lastQueryFinishedTime == 0) { + return 0; + } + + long now = System.currentTimeMillis(); + long idleTime = now - this.lastQueryFinishedTime; + + return idleTime; + } + + /** + * Returns the IO channel to the server + * + * @return the IO channel to the server + * @throws SQLException + * if the connection is closed. + */ + protected MysqlIO getIO() throws SQLException { + if ((this.io == null) || this.isClosed) { + throw SQLError.createSQLException( + "Operation not allowed on closed connection", + SQLError.SQL_STATE_CONNECTION_NOT_OPEN); + } + + return this.io; + } + + /** + * Returns the log mechanism that should be used to log information from/for + * this Connection. + * + * @return the Log instance to use for logging messages. + * @throws SQLException + * if an error occurs + */ + public Log getLog() throws SQLException { + return this.log; + } + + /** + * Returns the maximum packet size the MySQL server will accept + * + * @return DOCUMENT ME! + */ + int getMaxAllowedPacket() { + return this.maxAllowedPacket; + } + + protected int getMaxBytesPerChar(String javaCharsetName) + throws SQLException { + // TODO: Check if we can actually run this query at this point in time + String charset = CharsetMapping.getMysqlEncodingForJavaEncoding( + javaCharsetName, this); + + if (versionMeetsMinimum(4, 1, 0)) { + Map mapToCheck = null; + + if (!getUseDynamicCharsetInfo()) { + mapToCheck = CharsetMapping.STATIC_CHARSET_TO_NUM_BYTES_MAP; + } else { + mapToCheck = this.charsetToNumBytesMap; + + synchronized (this.charsetToNumBytesMap) { + if (this.charsetToNumBytesMap.isEmpty()) { + + java.sql.Statement stmt = null; + java.sql.ResultSet rs = null; + + try { + stmt = getMetadataSafeStatement(); + + rs = stmt.executeQuery("SHOW CHARACTER SET"); + + while (rs.next()) { + this.charsetToNumBytesMap.put(rs.getString("Charset"), + 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) mapToCheck.get(charset); + + if (mbPerChar != null) { + return mbPerChar.intValue(); + } + + return 1; // we don't know + } + + return 1; // we don't know + } + + /** + * A connection's database is able to provide information describing its + * tables, its supported SQL grammar, its stored procedures, the + * capabilities of this connection, etc. This information is made available + * through a DatabaseMetaData object. + * + * @return a DatabaseMetaData object for this connection + * @exception SQLException + * if a database access error occurs + */ + public java.sql.DatabaseMetaData getMetaData() throws SQLException { + checkClosed(); + + 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() { + if (this.io.versionMeetsMinimum(4, 1, 0)) { + return (String) this.serverVariables.get("character_set_server"); + } else { + return (String) this.serverVariables.get("character_set"); + } + } + + int getServerMajorVersion() { + return this.io.getServerMajorVersion(); + } + + int getServerMinorVersion() { + return this.io.getServerMinorVersion(); + } + + int getServerSubMinorVersion() { + return this.io.getServerSubMinorVersion(); + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public TimeZone getServerTimezoneTZ() { + return this.serverTimezoneTZ; + } + + String getServerVariable(String variableName) { + if (this.serverVariables != null) { + return (String) this.serverVariables.get(variableName); + } + + return null; + } + + String getServerVersion() { + return this.io.getServerVersion(); + } + + protected Calendar getSessionLockedCalendar() { + + return this.sessionCalendar; + } + + + /** + * Get this Connection's current transaction isolation mode. + * + * @return the current TRANSACTION_ mode value + * @exception SQLException + * if a database access error occurs + */ + public int getTransactionIsolation() throws SQLException { + + if (this.hasIsolationLevels && !getUseLocalSessionState()) { + java.sql.Statement stmt = null; + java.sql.ResultSet rs = null; + + try { + stmt = getMetadataSafeStatement(); + + String query = null; + + int offset = 0; + + if (versionMeetsMinimum(4, 0, 3)) { + query = "SELECT @@session.tx_isolation"; + offset = 1; + } else { + query = "SHOW VARIABLES LIKE 'transaction_isolation'"; + offset = 2; + } + + rs = stmt.executeQuery(query); + + if (rs.next()) { + String s = rs.getString(offset); + + if (s != null) { + Integer intTI = (Integer) mapTransIsolationNameToValue + .get(s); + + if (intTI != null) { + return intTI.intValue(); + } + } + + throw SQLError.createSQLException( + "Could not map transaction isolation '" + s + + " to a valid JDBC level.", + SQLError.SQL_STATE_GENERAL_ERROR); + } + + throw SQLError.createSQLException( + "Could not retrieve transaction isolation level from server", + SQLError.SQL_STATE_GENERAL_ERROR); + + } finally { + if (rs != null) { + try { + rs.close(); + } catch (Exception ex) { + // ignore + ; + } + + rs = null; + } + + if (stmt != null) { + try { + stmt.close(); + } catch (Exception ex) { + // ignore + ; + } + + stmt = null; + } + } + } + + return this.isolationLevel; + } + + /** + * JDBC 2.0 Get the type-map object associated with this connection. By + * default, the map returned is empty. + * + * @return the type map + * @throws SQLException + * if a database error occurs + */ + public synchronized java.util.Map getTypeMap() throws SQLException { + if (this.typeMap == null) { + this.typeMap = new HashMap(); + } + + return this.typeMap; + } + + String getURL() { + return this.myURL; + } + + String getUser() { + return this.user; + } + + protected Calendar getUtcCalendar() { + return this.utcCalendar; + } + + /** + * The first warning reported by calls on this Connection is returned. + * Note: 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()); + } + + if (getAllowMultiQueries()) { + setCacheResultSetMetadata(false); // we don't handle this yet + } + + if (getCacheResultSetMetadata()) { + this.resultSetMetadataCache = new LRUCache( + getMetadataCacheSize()); + } + } + + /** + * Sets varying properties that depend on server information. Called once we + * have connected to the server. + * + * @param info + * DOCUMENT ME! + * @throws SQLException + * DOCUMENT ME! + */ + private void initializePropsFromServer() throws SQLException { + setSessionVariables(); + + // + // the "boolean" type didn't come along until MySQL-4.1 + // + + if (!versionMeetsMinimum(4, 1, 0)) { + setTransformedBitIsBoolean(false); + } + + this.parserKnowsUnicode = versionMeetsMinimum(4, 1, 0); + + // + // Users can turn off detection of server-side prepared statements + // + if (getUseServerPreparedStmts() && versionMeetsMinimum(4, 1, 0)) { + this.useServerPreparedStmts = true; + + if (versionMeetsMinimum(5, 0, 0) && !versionMeetsMinimum(5, 0, 3)) { + this.useServerPreparedStmts = false; // 4.1.2+ style prepared + // statements + // don't work on these versions + } + } + + this.serverVariables.clear(); + + // + // If version is greater than 3.21.22 get the server + // variables. + if (versionMeetsMinimum(3, 21, 22)) { + loadServerVariables(); + + buildCollationMapping(); + + LicenseConfiguration.checkLicenseType(this.serverVariables); + + String lowerCaseTables = (String) this.serverVariables + .get("lower_case_table_names"); + + this.lowerCaseTableNames = "on".equalsIgnoreCase(lowerCaseTables) + || "1".equalsIgnoreCase(lowerCaseTables) + || "2".equalsIgnoreCase(lowerCaseTables); + + configureTimezone(); + + if (this.serverVariables.containsKey("max_allowed_packet")) { + this.maxAllowedPacket = getServerVariableAsInt("max_allowed_packet", 1024 * 1024); + + int preferredBlobSendChunkSize = getBlobSendChunkSize(); + + int allowedBlobSendChunkSize = Math.min(preferredBlobSendChunkSize, + this.maxAllowedPacket) - + ServerPreparedStatement.BLOB_STREAM_READ_BUF_SIZE + - 11 /* LONG_DATA and MySQLIO packet header size */; + + setBlobSendChunkSize(String.valueOf(allowedBlobSendChunkSize)); + } + + if (this.serverVariables.containsKey("net_buffer_length")) { + this.netBufferLength = getServerVariableAsInt("net_buffer_length", 16 * 1024); + } + + checkTransactionIsolationLevel(); + + if (!versionMeetsMinimum(4, 1, 0)) { + checkServerEncoding(); + } + + this.io.checkForCharsetMismatch(); + + if (this.serverVariables.containsKey("sql_mode")) { + int sqlMode = 0; + + String sqlModeAsString = (String) this.serverVariables + .get("sql_mode"); + try { + sqlMode = Integer.parseInt(sqlModeAsString); + } catch (NumberFormatException nfe) { + // newer versions of the server has this as a string-y + // list... + sqlMode = 0; + + if (sqlModeAsString != null) { + if (sqlModeAsString.indexOf("ANSI_QUOTES") != -1) { + sqlMode |= 4; + } + + if (sqlModeAsString.indexOf("NO_BACKSLASH_ESCAPES") != -1) { + this.noBackslashEscapes = true; + } + } + } + + if ((sqlMode & 4) > 0) { + this.useAnsiQuotes = true; + } else { + this.useAnsiQuotes = false; + } + } + } + + this.errorMessageEncoding = + CharsetMapping.getCharacterEncodingForErrorMessages(this); + + + boolean overrideDefaultAutocommit = isAutoCommitNonDefaultOnServer(); + + configureClientCharacterSet(); + + if (versionMeetsMinimum(3, 23, 15)) { + this.transactionsSupported = true; + + if (!overrideDefaultAutocommit) { + setAutoCommit(true); // to override anything + // the server is set to...reqd + // by JDBC spec. + } + } else { + this.transactionsSupported = false; + } + + + if (versionMeetsMinimum(3, 23, 36)) { + this.hasIsolationLevels = true; + } else { + this.hasIsolationLevels = false; + } + + this.hasQuotedIdentifiers = versionMeetsMinimum(3, 23, 6); + + this.io.resetMaxBuf(); + + // + // If we're using MySQL 4.1.0 or newer, we need to figure + // out what character set metadata will be returned in, + // and then map that to a Java encoding name. + // + // We've already set it, and it might be different than what + // was originally on the server, which is why we use the + // "special" key to retrieve it + if (this.io.versionMeetsMinimum(4, 1, 0)) { + String characterSetResultsOnServerMysql = (String) this.serverVariables + .get(JDBC_LOCAL_CHARACTER_SET_RESULTS); + + if (characterSetResultsOnServerMysql == null + || StringUtils.startsWithIgnoreCaseAndWs( + characterSetResultsOnServerMysql, "NULL") + || characterSetResultsOnServerMysql.length() == 0) { + String defaultMetadataCharsetMysql = (String) this.serverVariables + .get("character_set_system"); + String defaultMetadataCharset = null; + + if (defaultMetadataCharsetMysql != null) { + defaultMetadataCharset = CharsetMapping + .getJavaEncodingForMysqlEncoding( + defaultMetadataCharsetMysql, this); + } else { + defaultMetadataCharset = "UTF-8"; + } + + this.characterSetMetadata = defaultMetadataCharset; + } else { + this.characterSetResultsOnServer = CharsetMapping + .getJavaEncodingForMysqlEncoding( + characterSetResultsOnServerMysql, this); + this.characterSetMetadata = this.characterSetResultsOnServer; + } + } + + // + // Query cache is broken wrt. multi-statements before MySQL-4.1.10 + // + + if (this.versionMeetsMinimum(4, 1, 0) + && !this.versionMeetsMinimum(4, 1, 10) + && getAllowMultiQueries()) { + if ("ON".equalsIgnoreCase((String) this.serverVariables + .get("query_cache_type")) + && !"0".equalsIgnoreCase((String) this.serverVariables + .get("query_cache_size"))) { + setAllowMultiQueries(false); + } + } + + // + // Server can do this more efficiently for us + // + + setupServerForTruncationChecks(); + } + + private int getServerVariableAsInt(String variableName, int fallbackValue) + throws SQLException { + try { + return Integer.parseInt((String) this.serverVariables + .get(variableName)); + } catch (NumberFormatException nfe) { + getLog().logWarn(Messages.getString("Connection.BadValueInServerVariables", new Object[] {variableName, + this.serverVariables.get(variableName), new Integer(fallbackValue)})); + + return fallbackValue; + } + } + + /** + * Has the default autocommit value of 0 been changed on the server + * via init_connect? + * + * @return true if autocommit is not the default of '0' on the server. + * + * @throws SQLException + */ + private boolean isAutoCommitNonDefaultOnServer() throws SQLException { + boolean overrideDefaultAutocommit = false; + + String initConnectValue = (String) this.serverVariables + .get("init_connect"); + + if (versionMeetsMinimum(4, 1, 2) && initConnectValue != null + && initConnectValue.length() > 0) { + if (!getElideSetAutoCommits()) { + // auto-commit might have changed + java.sql.ResultSet rs = null; + java.sql.Statement stmt = null; + + try { + stmt = getMetadataSafeStatement(); + + rs = stmt.executeQuery("SELECT @@session.autocommit"); + + if (rs.next()) { + this.autoCommit = rs.getBoolean(1); + if (this.autoCommit != true) { + overrideDefaultAutocommit = true; + } + } + + } finally { + if (rs != null) { + try { + rs.close(); + } catch (SQLException sqlEx) { + // do nothing + } + } + + if (stmt != null) { + try { + stmt.close(); + } catch (SQLException sqlEx) { + // do nothing + } + } + } + } else { + if (this.getIO().isSetNeededForAutoCommitMode(true)) { + // we're not in standard autocommit=true mode + this.autoCommit = false; + overrideDefaultAutocommit = true; + } + } + } + + return overrideDefaultAutocommit; + } + + 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; + } + + private boolean usingCachedConfig = false; + + /** + * Loads the result of 'SHOW VARIABLES' into the serverVariables field so + * that the driver can configure itself. + * + * @throws SQLException + * if the 'SHOW VARIABLES' query fails for any reason. + */ + private void loadServerVariables() throws SQLException { + + if (getCacheServerConfiguration()) { + synchronized (serverConfigByUrl) { + Map cachedVariableMap = (Map) serverConfigByUrl.get(getURL()); + + if (cachedVariableMap != null) { + this.serverVariables = cachedVariableMap; + this.usingCachedConfig = true; + + return; + } + } + } + + 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 SESSION 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. + *+ * Note: 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 + *
+ *+ * MySQL does not support precompilation of statements, so they are handled + * by the driver. + *
+ * + * @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, Constants.MILLIS_I18N, + null, + this.pointOfOrigin, message)); + } + + long connectionLifeTime = System.currentTimeMillis() + - this.connectionCreationTimeMillis; + + if (connectionLifeTime < 500) { + String message = "Connection lifetime of < .5 seconds. You might be un-necessarily creating short-lived connections and should investigate connection pooling to be more efficient."; + + this.eventSink.consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_WARN, "", //$NON-NLS-1$ + this.getCatalog(), this.getId(), -1, -1, System + .currentTimeMillis(), 0, Constants.MILLIS_I18N, + null, + this.pointOfOrigin, message)); + } + } + + try { + closeAllOpenStatements(); + } catch (SQLException ex) { + sqlEx = ex; + } + + if (this.io != null) { + try { + this.io.quit(); + } catch (Exception e) { + ; + } + + } + } else { + this.io.forceClose(); + } + } finally { + this.openStatements = null; + this.io = null; + ProfileEventSink.removeInstance(this); + this.isClosed = true; + } + + if (sqlEx != null) { + throw sqlEx; + } + + } + + protected void recachePreparedStatement(ServerPreparedStatement pstmt) { + 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 { + if (getUseLocalSessionState() && versionMeetsMinimum(5, 0, 0)) { + if (!this.io.inTransactionOnServer()) { + return; // effectively a no-op + } + } + + execSQL(null, "rollback", -1, null, + java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY, false, + this.database, 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. + *+ * Note: MySQL does not support transactions, so this method is a + * no-op. + *
+ * + * @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 + *+ * Note: MySQL's notion of catalogs are individual databases. + *
+ * + * @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()) { + setReadOnlyInternal(true); + } + + this.queriesIssuedFailedOver = 0; + this.failedOver = true; + this.masterFailTimeMillis = System.currentTimeMillis(); + } + + /** + * @see Connection#setHoldability(int) + */ + public void setHoldability(int arg0) throws SQLException { + // do nothing + } + + public void setInGlobalTx(boolean flag) { + this.isInGlobalTx = flag; + } + + // exposed for testing + /** + * @param preferSlaveDuringFailover + * The preferSlaveDuringFailover to set. + */ + public void setPreferSlaveDuringFailover(boolean flag) { + this.preferSlaveDuringFailover = flag; + } + + void setReadInfoMsgEnabled(boolean flag) { + this.readInfoMsg = flag; + } + + /** + * You can put a connection in read-only mode as a hint to enable database + * optimizations Note: setReadOnly cannot be called while in the + * middle of a transaction + * + * @param readOnlyFlag - + * true enables read-only mode; false disables it + * @exception SQLException + * if a database access error occurs + */ + public void setReadOnly(boolean readOnlyFlag) throws SQLException { + checkClosed(); + + // Ignore calls to this method if we're failed over and + // we're configured to fail over read-only. + if (this.failedOver && getFailOverReadOnly() && !readOnlyFlag) { + return; + } + + setReadOnlyInternal(readOnlyFlag); + } + + protected void setReadOnlyInternal(boolean readOnlyFlag) throws SQLException { + this.readOnly = readOnlyFlag; + } + + /** + * @see Connection#setSavepoint() + */ + public java.sql.Savepoint setSavepoint() throws SQLException { + MysqlSavepoint savepoint = new MysqlSavepoint(); + + setSavepoint(savepoint); + + return savepoint; + } + + private void setSavepoint(MysqlSavepoint savepoint) throws SQLException { + + if (versionMeetsMinimum(4, 0, 14) || versionMeetsMinimum(4, 1, 1)) { + synchronized (getMutex()) { + checkClosed(); + + StringBuffer savePointQuery = new StringBuffer("SAVEPOINT "); + savePointQuery.append('`'); + savePointQuery.append(savepoint.getSavepointName()); + savePointQuery.append('`'); + + java.sql.Statement stmt = null; + + try { + stmt = createStatement(); + + stmt.executeUpdate(savePointQuery.toString()); + } finally { + closeStatement(stmt); + } + } + } else { + throw new NotImplemented(); + } + } + + /** + * @see Connection#setSavepoint(String) + */ + public synchronized java.sql.Savepoint setSavepoint(String name) throws SQLException { + MysqlSavepoint savepoint = new MysqlSavepoint(name); + + setSavepoint(savepoint); + + return savepoint; + } + + /** + * + */ + private void setSessionVariables() throws SQLException { + if (this.versionMeetsMinimum(4, 0, 0) && getSessionVariables() != null) { + List variablesToSet = StringUtils.split(getSessionVariables(), ",", "\"'", "\"'", + false); + + int numVariablesToSet = variablesToSet.size(); + + java.sql.Statement stmt = null; + + try { + stmt = getMetadataSafeStatement(); + + for (int i = 0; i < numVariablesToSet; i++) { + String variableValuePair = (String) variablesToSet.get(i); + + if (variableValuePair.startsWith("@")) { + stmt.executeUpdate("SET " + variableValuePair); + } else { + stmt.executeUpdate("SET SESSION " + variableValuePair); + } + } + } finally { + if (stmt != null) { + stmt.close(); + } + } + } + + } + + /** + * Attempts to change the transaction isolation level for this + *Connection
object to the one given.
+ * The constants defined in the interface Connection
+ * are the possible transaction isolation levels.
+ *
+ * Note: If this method is called during a transaction, the result
+ * is implementation-defined.
+ *
+ * @param level one of the following Connection
constants:
+ * Connection.TRANSACTION_READ_UNCOMMITTED
,
+ * Connection.TRANSACTION_READ_COMMITTED
,
+ * Connection.TRANSACTION_REPEATABLE_READ
, or
+ * Connection.TRANSACTION_SERIALIZABLE
.
+ * (Note that Connection.TRANSACTION_NONE
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 Connection
+ * 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;
+ }
+
+ /**
+ * Returns cached metadata (or null if not cached) for the given query,
+ * which must match _exactly_.
+ *
+ * This method is synchronized by the caller on getMutex(), so if
+ * calling this method from internal code in the driver, make sure it's
+ * synchronized on the mutex that guards communication with the server.
+ *
+ * @param sql
+ * the query that is the key to the cache
+ *
+ * @return metadata cached for the given SQL, or none if it doesn't
+ * exist.
+ */
+ protected CachedResultSetMetaData getCachedMetaData(String sql) {
+ if (this.resultSetMetadataCache != null) {
+ synchronized (this.resultSetMetadataCache) {
+ return (CachedResultSetMetaData) this.resultSetMetadataCache
+ .get(sql);
+ }
+ }
+
+ return null; // no cache exists
+ }
+
+ /**
+ * Caches CachedResultSetMetaData that has been placed in the cache using
+ * the given SQL as a key.
+ *
+ * This method is synchronized by the caller on getMutex(), so if
+ * calling this method from internal code in the driver, make sure it's
+ * synchronized on the mutex that guards communication with the server.
+ *
+ * @param sql the query that the metadata pertains too.
+ * @param cachedMetaData metadata (if it exists) to populate the cache.
+ * @param resultSet the result set to retreive metadata from, or apply to.
+ *
+ * @throws SQLException
+ */
+ protected void initializeResultsMetadataFromCache(String sql,
+ CachedResultSetMetaData cachedMetaData, ResultSet resultSet)
+ throws SQLException {
+
+ if (cachedMetaData == null) {
+
+ // read from results
+ cachedMetaData = new CachedResultSetMetaData();
+ cachedMetaData.fields = resultSet.fields;
+
+ // assume that users will use named-based
+ // lookups
+ resultSet.buildIndexMapping();
+ resultSet.initializeWithMetadata();
+
+ if (resultSet instanceof UpdatableResultSet) {
+ ((UpdatableResultSet)resultSet).checkUpdatability();
+ }
+
+ cachedMetaData.columnNameToIndex = resultSet.columnNameToIndex;
+ cachedMetaData.fullColumnNameToIndex = resultSet.fullColumnNameToIndex;
+
+ cachedMetaData.metadata = resultSet.getMetaData();
+
+ 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;
+ resultSet.initializeWithMetadata();
+
+ if (resultSet instanceof UpdatableResultSet) {
+ ((UpdatableResultSet)resultSet).checkUpdatability();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java
new file mode 100644
index 00000000..f51c3001
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ConnectionFeatureNotAvailableException.java
@@ -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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ConnectionProperties.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ConnectionProperties.java
new file mode 100644
index 00000000..01ea7d1b
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ConnectionProperties.java
@@ -0,0 +1,4184 @@
+/*
+ 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 = -3004305481796850832L;
+
+ public IntegerConnectionProperty(String propertyNameToSet,
+ Object defaultValueToSet, String[] allowableValuesToSet,
+ int lowerBoundToSet, int upperBoundToSet,
+ String descriptionToSet, String sinceVersionToSet,
+ String category, int orderInCategory) {
+ super(propertyNameToSet, defaultValueToSet, allowableValuesToSet,
+ lowerBoundToSet, upperBoundToSet, descriptionToSet, sinceVersionToSet,
+ category, orderInCategory);
+ }
+
+ int multiplier = 1;
+
+ IntegerConnectionProperty(String propertyNameToSet,
+ int defaultValueToSet, int lowerBoundToSet,
+ int upperBoundToSet, String descriptionToSet,
+ String sinceVersionToSet, String category, int orderInCategory) {
+ super(propertyNameToSet, new Integer(defaultValueToSet), null,
+ lowerBoundToSet, upperBoundToSet, descriptionToSet,
+ sinceVersionToSet, category, orderInCategory);
+ }
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param propertyNameToSet
+ * @param defaultValueToSet
+ * @param descriptionToSet
+ * @param sinceVersionToSet
+ * DOCUMENT ME!
+ */
+
+ IntegerConnectionProperty(String propertyNameToSet,
+ int defaultValueToSet, String descriptionToSet,
+ String sinceVersionToSet, String category, int orderInCategory) {
+ this(propertyNameToSet, defaultValueToSet, 0, 0, descriptionToSet,
+ sinceVersionToSet, category, orderInCategory);
+ }
+
+ /**
+ * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getAllowableValues()
+ */
+ String[] getAllowableValues() {
+ return null;
+ }
+
+ /**
+ * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getLowerBound()
+ */
+ int getLowerBound() {
+ return this.lowerBound;
+ }
+
+ /**
+ * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#getUpperBound()
+ */
+ int getUpperBound() {
+ return this.upperBound;
+ }
+
+ int getValueAsInt() {
+ return ((Integer) this.valueAsObject).intValue();
+ }
+
+ /**
+ * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#hasValueConstraints()
+ */
+ boolean hasValueConstraints() {
+ return false;
+ }
+
+ /**
+ * @see com.mysql.jdbc.ConnectionProperties.ConnectionProperty#initializeFrom(java.lang.String)
+ */
+ void initializeFrom(String extractedValue) throws SQLException {
+ if (extractedValue != null) {
+ try {
+ // Parse decimals, too
+ int intValue = Double.valueOf(extractedValue).intValue();
+
+ /*
+ * if (isRangeBased()) { if ((intValue < getLowerBound()) ||
+ * (intValue > getUpperBound())) { throw new
+ * SQLException("The connection property '" +
+ * getPropertyName() + "' only accepts integer values in the
+ * range of " + getLowerBound() + " - " + getUpperBound() + ",
+ * the value '" + extractedValue + "' exceeds this range.",
+ * SQLError.SQL_STATE_ILLEGAL_ARGUMENT); } }
+ */
+ this.valueAsObject = new Integer(intValue * multiplier);
+ } catch (NumberFormatException nfe) {
+ throw SQLError.createSQLException("The connection property '"
+ + 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);
+ }
+ }
+
+ public class LongConnectionProperty extends IntegerConnectionProperty {
+
+ private static final long serialVersionUID = 6068572984340480895L;
+
+ LongConnectionProperty(String propertyNameToSet,
+ long defaultValueToSet, long lowerBoundToSet,
+ long upperBoundToSet, String descriptionToSet,
+ String sinceVersionToSet, String category, int orderInCategory) {
+ super(propertyNameToSet, new Long(defaultValueToSet), null,
+ (int)lowerBoundToSet, (int)upperBoundToSet, descriptionToSet,
+ sinceVersionToSet, category, orderInCategory);
+ }
+
+
+ LongConnectionProperty(String propertyNameToSet,
+ long defaultValueToSet, String descriptionToSet,
+ String sinceVersionToSet, String category, int orderInCategory) {
+ this(propertyNameToSet,
+ defaultValueToSet, 0,
+ 0, descriptionToSet,
+ sinceVersionToSet, category, orderInCategory);
+ }
+
+ void setValue(long value) {
+ this.valueAsObject = new Long(value);
+ }
+
+ long getValueAsLong() {
+ return ((Long) this.valueAsObject).longValue();
+ }
+
+ void initializeFrom(String extractedValue) throws SQLException {
+ if (extractedValue != null) {
+ try {
+ // Parse decimals, too
+ long longValue = Double.valueOf(extractedValue).longValue();
+
+ this.valueAsObject = new Long(longValue);
+ } catch (NumberFormatException nfe) {
+ throw SQLError.createSQLException("The connection property '"
+ + getPropertyName()
+ + "' only accepts long integer values. The value '"
+ + extractedValue
+ + "' can not be converted to a long integer.",
+ SQLError.SQL_STATE_ILLEGAL_ARGUMENT);
+ }
+ } else {
+ this.valueAsObject = this.defaultValue;
+ }
+ }
+ }
+
+ 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 NETWORK_CATEGORY = "Networking";
+
+ 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, NETWORK_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 blobsAreStrings = new BooleanConnectionProperty(
+ "blobsAreStrings", false,
+ "Should the driver always treat BLOBs as Strings - specifically to work around dubious metadata "
+ + "returned by the server for GROUP BY clauses?",
+ "5.0.8", MISC_CATEGORY, Integer.MIN_VALUE);
+
+ private BooleanConnectionProperty functionsNeverReturnBlobs = new BooleanConnectionProperty(
+ "functionsNeverReturnBlobs", false,
+ "Should the driver always treat data from functions returning BLOBs as Strings - specifically to work around dubious metadata "
+ + "returned by the server for GROUP BY clauses?",
+ "5.0.8", MISC_CATEGORY, Integer.MIN_VALUE);
+
+ private BooleanConnectionProperty cacheCallableStatements = new BooleanConnectionProperty(
+ "cacheCallableStmts", false,
+ "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",
+ true,
+ "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",
+ false,
+ "Use server-side prepared statements if the server supports them?",
+ "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 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 enableQueryTimeouts = new BooleanConnectionProperty(
+ "enableQueryTimeouts",
+ true,
+ "When enabled, query timeouts set via Statement.setQueryTimeout() use a shared "
+ + "java.util.Timer instance for scheduling. Even if the timeout doesn't expire before the query is processed, there will be "
+ + "memory used by the TimerTask for the given timeout which won't be reclaimed until "
+ + "the time the timeout would have expired if it hadn't been cancelled by the driver. High-load environments "
+ + "might want to consider disabling this functionality.",
+ "5.0.6",
+ PERFORMANCE_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 BooleanConnectionProperty generateSimpleParameterMetadata = new BooleanConnectionProperty(
+ "generateSimpleParameterMetadata", false, "Should the driver generate simplified parameter metadata for PreparedStatements when "
+ + "no metadata is available either because the server couldn't support preparing the statement, or server-side prepared statements" +
+ " are disabled?"
+ , "5.0.5", MISC_CATEGORY, Integer.MIN_VALUE);
+
+ 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 includeInnodbStatusInDeadlockExceptions = new BooleanConnectionProperty(
+ "includeInnodbStatusInDeadlockExceptions",
+ false,
+ "Include the output of \"SHOW ENGINE INNODB STATUS\" in exception messages when deadlock exceptions are detected?",
+ "5.0.7", DEBUGING_PROFILING_CATEGORY, Integer.MIN_VALUE);
+
+ private BooleanConnectionProperty ignoreNonTxTables = new BooleanConnectionProperty(
+ "ignoreNonTxTables",
+ 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 StringConnectionProperty loadBalanceStrategy = new StringConnectionProperty(
+ "loadBalanceStrategy",
+ "random",
+ new String[] {"random", "bestResponseTime"},
+ "If using a load-balanced connection to connect to SQL nodes in a MySQL Cluster/NDB configuration" +
+ "(by using the URL prefix \"jdbc:mysql:loadbalance://\"), which load balancin algorithm should the driver " +
+ "use: (1) \"random\" - the driver will pick a random host for each request. This tends " +
+ "to work better than round-robin, as the randomness will somewhat account for " +
+ "spreading loads where requests vary in response time, while round-robin " +
+ "can sometimes lead to overloaded nodes if there are variations in response times " +
+ "across the workload. (2) \"bestResponseTime\" - the driver will route the request to the host that had " +
+ "the best response time for the previous transaction.",
+ "5.0.6", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
+
+ private StringConnectionProperty localSocketAddress = new StringConnectionProperty("localSocketAddress",
+ null, "Hostname or IP address given to explicitly configure the interface that "
+ + "the driver will bind the client side of the TCP/IP connection to when connecting.",
+ "5.0.5", CONNECTION_AND_AUTH_CATEGORY, Integer.MIN_VALUE);
+
+ 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 logXaCommands = new BooleanConnectionProperty(
+ "logXaCommands",
+ false,
+ "Should the driver log XA commands sent by MysqlXaConnection to the server," +
+ " at the DEBUG level of logging?",
+ "5.0.5", 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 IN VARCHARs,"
+ + " but allowing registerOutParameter() to be called on them anyway) instead "
+ + " of throwing an exception?",
+ "5.0.3", MISC_CATEGORY, Integer.MIN_VALUE);
+
+ private BooleanConnectionProperty noDatetimeStringSync = new BooleanConnectionProperty(
+ "noDatetimeStringSync",
+ 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 padCharsWithSpace = new BooleanConnectionProperty(
+ "padCharsWithSpace",
+ false,
+ "If a result set column has the CHAR type and the value does not fill the "
+ + "amount of characters specified in the DDL for the column, should the driver "
+ + "pad the remaining characters with space (for ANSI compliance)?",
+ "5.0.6",
+ MISC_CATEGORY,
+ Integer.MIN_VALUE);
+
+ 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 BooleanConnectionProperty populateInsertRowWithDefaultValues = new BooleanConnectionProperty(
+ "populateInsertRowWithDefaultValues", false,
+ "When using ResultSets that are CONCUR_UPDATABLE, should the driver pre-poulate " +
+ "the \"insert\" row with default values from the DDL for the table used in the query " +
+ " so those values are immediately available for ResultSet accessors? This functionality requires a " +
+ " call to the database for metadata each time a result set of this type is created. " +
+ " If disabled (the default), the default values will be populated by the an internal" +
+ " call to refreshRow() which pulls back default values and/or values changed by triggers.",
+ "5.0.5", 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 IntegerConnectionProperty resultSetSizeThreshold = new IntegerConnectionProperty("resultSetSizeThreshold", 100,
+ "If the usage advisor is enabled, how many rows should a result set contain before the driver warns that it "
+ + " is suspiciously large?", "5.0.5", DEBUGING_PROFILING_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 and REPLACE into multi-value inserts/replaces 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 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 LongConnectionProperty slowQueryThresholdNanos = new LongConnectionProperty(
+ "slowQueryThresholdNanos",
+ 0,
+ "If 'useNanosForElapsedTime' is set to true, and this property is set to a non-zero value,"
+ + " the driver will use this threshold (in nanosecond units) to determine if a query was slow.",
+ "5.0.7",
+ DEBUGING_PROFILING_CATEGORY,
+ 10);
+
+ 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 tcpNoDelay = new BooleanConnectionProperty(
+ StandardSocketFactory.TCP_NO_DELAY_PROPERTY_NAME,
+ Boolean.valueOf(StandardSocketFactory.TCP_NO_DELAY_DEFAULT_VALUE).booleanValue(),
+ "If connecting using TCP/IP, should the driver set SO_TCP_NODELAY (disabling the Nagle Algorithm)?",
+ "5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE);
+
+ private BooleanConnectionProperty tcpKeepAlive = new BooleanConnectionProperty(
+ StandardSocketFactory.TCP_KEEP_ALIVE_PROPERTY_NAME,
+ Boolean.valueOf(StandardSocketFactory.TCP_KEEP_ALIVE_DEFAULT_VALUE).booleanValue(),
+ "If connecting using TCP/IP, should the driver set SO_KEEPALIVE?",
+ "5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE);
+
+ private IntegerConnectionProperty tcpRcvBuf = new IntegerConnectionProperty(
+ StandardSocketFactory.TCP_RCV_BUF_PROPERTY_NAME,
+ Integer.parseInt(StandardSocketFactory.TCP_RCV_BUF_DEFAULT_VALUE),
+ 0, Integer.MAX_VALUE,
+ "If connecting using TCP/IP, should the driver set SO_RCV_BUF to the given value? "
+ + "The default value of '0', means use the platform default value for this property)",
+ "5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE);
+
+ private IntegerConnectionProperty tcpSndBuf = new IntegerConnectionProperty(
+ StandardSocketFactory.TCP_SND_BUF_PROPERTY_NAME,
+ Integer.parseInt(StandardSocketFactory.TCP_SND_BUF_DEFAULT_VALUE),
+ 0, Integer.MAX_VALUE,
+ "If connecting using TCP/IP, shuold the driver set SO_SND_BUF to the given value? "
+ + "The default value of '0', means use the platform default value for this property)",
+ "5.0.7", NETWORK_CATEGORY, Integer.MIN_VALUE);
+
+ private IntegerConnectionProperty tcpTrafficClass = new IntegerConnectionProperty(
+ StandardSocketFactory.TCP_TRAFFIC_CLASS_PROPERTY_NAME,
+ Integer.parseInt(StandardSocketFactory.TCP_TRAFFIC_CLASS_DEFAULT_VALUE),
+ 0, 255,
+ "If connecting using TCP/IP, should the driver set traffic class or type-of-service fields ?" +
+ " See the documentation for java.net.Socket.setTrafficClass() for more information.",
+ "5.0.7", NETWORK_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 treatUtilDateAsTimestamp = new BooleanConnectionProperty(
+ "treatUtilDateAsTimestamp", true,
+ "Should the driver treat java.util.Date as a TIMESTAMP for the purposes of PreparedStatement.setObject()?",
+ "5.0.5", MISC_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 useConfigs = 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 useDynamicCharsetInfo = new BooleanConnectionProperty(
+ "useDynamicCharsetInfo",
+ true,
+ "Should the driver use a per-connection cache of character set information queried from the "
+ + " server when necessary, or use a built-in static mapping that is more efficient, but isn't "
+ + " aware of custom character sets or character sets implemented after the release of the JDBC driver?"
+ + "(this only affects the \"padCharsWithSpace\" configuration property and the "
+ + "ResultSetMetaData.getColumnDisplayWidth() method)."
+ , "5.0.6", PERFORMANCE_CATEGORY, Integer.MIN_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 useFastDateParsing = new BooleanConnectionProperty(
+ "useFastDateParsing",
+ true,
+ "Use internal String->Date/Time/Teimstamp conversion routines to avoid excessive object creation?",
+ "5.0.5", 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() and transaction state "
+ + "as maintained by the protocol, rather than querying the database or blindly "
+ + "sending commands to the database for commit() or rollback() method calls?",
+ "3.1.7", PERFORMANCE_CATEGORY, Integer.MIN_VALUE);
+
+ private BooleanConnectionProperty useNanosForElapsedTime = new BooleanConnectionProperty(
+ "useNanosForElapsedTime",
+ false,
+ "For profiling/debugging functionality that measures elapsed time, should the driver "
+ + "try to use nanoseconds resolution if available (JDK >= 1.5)?",
+ "5.0.7",
+ DEBUGING_PROFILING_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 useSSPSCompatibleTimezoneShift = new BooleanConnectionProperty(
+ "useSSPSCompatibleTimezoneShift",
+ false,
+ "If migrating from an environment that was using server-side prepared statements, and the"
+ + " configuration property \"useJDBCCompliantTimeZoneShift\" set to \"true\", use compatible behavior"
+ + " when not using server-side prepared statements when sending TIMESTAMP values to the MySQL server.",
+ "5.0.5", MISC_CATEGORY, Integer.MIN_VALUE);
+
+ 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",
+ false, "Always use the character encoding routines built into the JVM, rather than using "
+ + "lookup tables for single-byte character sets?", "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("
+ * This class provides information about the database as a whole. + *
+ *+ * 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. + *
+ *+ * 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. + *
+ * + * @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; + + Integer columnSize; + + short dataType; + + Integer 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 + + boolean isUnsigned = false; + + if (StringUtils.indexOfIgnoreCase(typeInfo, "unsigned") != -1) { + fullMysqlType = mysqlType + " unsigned"; + isUnsigned = true; + } 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 = new Integer(maxLength); + this.decimalDigits = null; + } else if (StringUtils.startsWithIgnoreCase(typeInfo, "set")) { + String temp = typeInfo.substring(typeInfo.indexOf("("), + typeInfo.lastIndexOf(")")); + java.util.StringTokenizer tokenizer = new java.util.StringTokenizer( + temp, ","); + int maxLength = 0; + + while (tokenizer.hasMoreTokens()) { + String setMember = tokenizer.nextToken().trim(); + + if (setMember.startsWith("'") + && setMember.endsWith("'")) { + maxLength += setMember.length() - 2; + } else { + maxLength += setMember.length(); + } + } + + this.columnSize = new Integer(maxLength); + this.decimalDigits = null; + } else if (typeInfo.indexOf(",") != -1) { + // Numeric with decimals + this.columnSize = new Integer(typeInfo.substring((typeInfo + .indexOf("(") + 1), (typeInfo.indexOf(","))).trim()); + this.decimalDigits = new Integer(typeInfo.substring( + (typeInfo.indexOf(",") + 1), + (typeInfo.indexOf(")"))).trim()); + } else { + this.columnSize = null; + this.decimalDigits = null; + + /* If the size is specified with the DDL, use that */ + if ((StringUtils.indexOfIgnoreCase(typeInfo, "char") != -1 + || StringUtils.indexOfIgnoreCase(typeInfo, "text") != -1 + || StringUtils.indexOfIgnoreCase(typeInfo, "blob") != -1 + || StringUtils + .indexOfIgnoreCase(typeInfo, "binary") != -1 || StringUtils + .indexOfIgnoreCase(typeInfo, "bit") != -1) + && typeInfo.indexOf("(") != -1) { + int endParenIndex = typeInfo.indexOf(")"); + + if (endParenIndex == -1) { + endParenIndex = typeInfo.length(); + } + + this.columnSize = new Integer(typeInfo.substring( + (typeInfo.indexOf("(") + 1), endParenIndex).trim()); + + // Adjust for pseudo-boolean + if (conn.getTinyInt1isBit() + && this.columnSize.intValue() == 1 + && StringUtils.startsWithIgnoreCase(typeInfo, + 0, "tinyint")) { + if (conn.getTransformedBitIsBoolean()) { + this.dataType = Types.BOOLEAN; + this.typeName = "BOOLEAN"; + } else { + this.dataType = Types.BIT; + this.typeName = "BIT"; + } + } + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "tinyint")) { + if (conn.getTinyInt1isBit() && typeInfo.indexOf("(1)") != -1) { + if (conn.getTransformedBitIsBoolean()) { + this.dataType = Types.BOOLEAN; + this.typeName = "BOOLEAN"; + } else { + this.dataType = Types.BIT; + this.typeName = "BIT"; + } + } else { + this.columnSize = new Integer(3); + this.decimalDigits = new Integer(0); + } + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "smallint")) { + this.columnSize = new Integer(5); + this.decimalDigits = new Integer(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "mediumint")) { + this.columnSize = new Integer(isUnsigned ? 8 : 7); + this.decimalDigits = new Integer(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "int")) { + this.columnSize = new Integer(10); + this.decimalDigits = new Integer(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "integer")) { + this.columnSize = new Integer(10); + this.decimalDigits = new Integer(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "bigint")) { + this.columnSize = new Integer(isUnsigned ? 20 : 19); + this.decimalDigits = new Integer(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "int24")) { + this.columnSize = new Integer(19); + this.decimalDigits = new Integer(0); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "real")) { + this.columnSize = new Integer(12); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "float")) { + this.columnSize = new Integer(12); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "decimal")) { + this.columnSize = new Integer(12); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "numeric")) { + this.columnSize = new Integer(12); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "double")) { + this.columnSize = new Integer(22); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "char")) { + this.columnSize = new Integer(1); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "varchar")) { + this.columnSize = new Integer(255); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "date")) { + this.columnSize = null; + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "time")) { + this.columnSize = null; + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "timestamp")) { + this.columnSize = null; + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "datetime")) { + this.columnSize = null; + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "tinyblob")) { + this.columnSize = new Integer(255); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "blob")) { + this.columnSize = new Integer(65535); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "mediumblob")) { + this.columnSize = new Integer(16777215); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "longblob")) { + this.columnSize = new Integer(Integer.MAX_VALUE); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "tinytext")) { + this.columnSize = new Integer(255); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "text")) { + this.columnSize = new Integer(65535); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "mediumtext")) { + this.columnSize = new Integer(16777215); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "longtext")) { + this.columnSize = new Integer(Integer.MAX_VALUE); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "enum")) { + this.columnSize = new Integer(255); + } else if (StringUtils.startsWithIgnoreCaseAndWs(typeInfo, + "set")) { + this.columnSize = new Integer(255); + } + + } + } else { + this.decimalDigits = null; + this.columnSize = null; + } + + // 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 String mysqlKeywordsThatArentSQL92; + + 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(); + + static { + // Current as-of MySQL-5.1.16 + String[] allMySQLKeywords = new String[] { "ACCESSIBLE", "ADD", "ALL", + "ALTER", "ANALYZE", "AND", "AS", "ASC", "ASENSITIVE", "BEFORE", + "BETWEEN", "BIGINT", "BINARY", "BLOB", "BOTH", "BY", "CALL", + "CASCADE", "CASE", "CHANGE", "CHAR", "CHARACTER", "CHECK", + "COLLATE", "COLUMN", "CONDITION", "CONNECTION", "CONSTRAINT", + "CONTINUE", "CONVERT", "CREATE", "CROSS", "CURRENT_DATE", + "CURRENT_TIME", "CURRENT_TIMESTAMP", "CURRENT_USER", "CURSOR", + "DATABASE", "DATABASES", "DAY_HOUR", "DAY_MICROSECOND", + "DAY_MINUTE", "DAY_SECOND", "DEC", "DECIMAL", "DECLARE", + "DEFAULT", "DELAYED", "DELETE", "DESC", "DESCRIBE", + "DETERMINISTIC", "DISTINCT", "DISTINCTROW", "DIV", "DOUBLE", + "DROP", "DUAL", "EACH", "ELSE", "ELSEIF", "ENCLOSED", + "ESCAPED", "EXISTS", "EXIT", "EXPLAIN", "FALSE", "FETCH", + "FLOAT", "FLOAT4", "FLOAT8", "FOR", "FORCE", "FOREIGN", "FROM", + "FULLTEXT", "GRANT", "GROUP", "HAVING", "HIGH_PRIORITY", + "HOUR_MICROSECOND", "HOUR_MINUTE", "HOUR_SECOND", "IF", + "IGNORE", "IN", "INDEX", "INFILE", "INNER", "INOUT", + "INSENSITIVE", "INSERT", "INT", "INT1", "INT2", "INT3", "INT4", + "INT8", "INTEGER", "INTERVAL", "INTO", "IS", "ITERATE", "JOIN", + "KEY", "KEYS", "KILL", "LEADING", "LEAVE", "LEFT", "LIKE", + "LIMIT", "LINEAR", "LINES", "LOAD", "LOCALTIME", + "LOCALTIMESTAMP", "LOCK", "LONG", "LONGBLOB", "LONGTEXT", + "LOOP", "LOW_PRIORITY", "MATCH", "MEDIUMBLOB", "MEDIUMINT", + "MEDIUMTEXT", "MIDDLEINT", "MINUTE_MICROSECOND", + "MINUTE_SECOND", "MOD", "MODIFIES", "NATURAL", "NOT", + "NO_WRITE_TO_BINLOG", "NULL", "NUMERIC", "ON", "OPTIMIZE", + "OPTION", "OPTIONALLY", "OR", "ORDER", "OUT", "OUTER", + "OUTFILE", "PRECISION", "PRIMARY", "PROCEDURE", "PURGE", + "RANGE", "READ", "READS", "READ_ONLY", "READ_WRITE", "REAL", + "REFERENCES", "REGEXP", "RELEASE", "RENAME", "REPEAT", + "REPLACE", "REQUIRE", "RESTRICT", "RETURN", "REVOKE", "RIGHT", + "RLIKE", "SCHEMA", "SCHEMAS", "SECOND_MICROSECOND", "SELECT", + "SENSITIVE", "SEPARATOR", "SET", "SHOW", "SMALLINT", "SPATIAL", + "SPECIFIC", "SQL", "SQLEXCEPTION", "SQLSTATE", "SQLWARNING", + "SQL_BIG_RESULT", "SQL_CALC_FOUND_ROWS", "SQL_SMALL_RESULT", + "SSL", "STARTING", "STRAIGHT_JOIN", "TABLE", "TERMINATED", + "THEN", "TINYBLOB", "TINYINT", "TINYTEXT", "TO", "TRAILING", + "TRIGGER", "TRUE", "UNDO", "UNION", "UNIQUE", "UNLOCK", + "UNSIGNED", "UPDATE", "USAGE", "USE", "USING", "UTC_DATE", + "UTC_TIME", "UTC_TIMESTAMP", "VALUES", "VARBINARY", "VARCHAR", + "VARCHARACTER", "VARYING", "WHEN", "WHERE", "WHILE", "WITH", + "WRITE", "X509", "XOR", "YEAR_MONTH", "ZEROFILL" }; + + String[] sql92Keywords = new String[] { "ABSOLUTE", "EXEC", "OVERLAPS", + "ACTION", "EXECUTE", "PAD", "ADA", "EXISTS", "PARTIAL", "ADD", + "EXTERNAL", "PASCAL", "ALL", "EXTRACT", "POSITION", "ALLOCATE", + "FALSE", "PRECISION", "ALTER", "FETCH", "PREPARE", "AND", + "FIRST", "PRESERVE", "ANY", "FLOAT", "PRIMARY", "ARE", "FOR", + "PRIOR", "AS", "FOREIGN", "PRIVILEGES", "ASC", "FORTRAN", + "PROCEDURE", "ASSERTION", "FOUND", "PUBLIC", "AT", "FROM", + "READ", "AUTHORIZATION", "FULL", "REAL", "AVG", "GET", + "REFERENCES", "BEGIN", "GLOBAL", "RELATIVE", "BETWEEN", "GO", + "RESTRICT", "BIT", "GOTO", "REVOKE", "BIT_LENGTH", "GRANT", + "RIGHT", "BOTH", "GROUP", "ROLLBACK", "BY", "HAVING", "ROWS", + "CASCADE", "HOUR", "SCHEMA", "CASCADED", "IDENTITY", "SCROLL", + "CASE", "IMMEDIATE", "SECOND", "CAST", "IN", "SECTION", + "CATALOG", "INCLUDE", "SELECT", "CHAR", "INDEX", "SESSION", + "CHAR_LENGTH", "INDICATOR", "SESSION_USER", "CHARACTER", + "INITIALLY", "SET", "CHARACTER_LENGTH", "INNER", "SIZE", + "CHECK", "INPUT", "SMALLINT", "CLOSE", "INSENSITIVE", "SOME", + "COALESCE", "INSERT", "SPACE", "COLLATE", "INT", "SQL", + "COLLATION", "INTEGER", "SQLCA", "COLUMN", "INTERSECT", + "SQLCODE", "COMMIT", "INTERVAL", "SQLERROR", "CONNECT", "INTO", + "SQLSTATE", "CONNECTION", "IS", "SQLWARNING", "CONSTRAINT", + "ISOLATION", "SUBSTRING", "CONSTRAINTS", "JOIN", "SUM", + "CONTINUE", "KEY", "SYSTEM_USER", "CONVERT", "LANGUAGE", + "TABLE", "CORRESPONDING", "LAST", "TEMPORARY", "COUNT", + "LEADING", "THEN", "CREATE", "LEFT", "TIME", "CROSS", "LEVEL", + "TIMESTAMP", "CURRENT", "LIKE", "TIMEZONE_HOUR", + "CURRENT_DATE", "LOCAL", "TIMEZONE_MINUTE", "CURRENT_TIME", + "LOWER", "TO", "CURRENT_TIMESTAMP", "MATCH", "TRAILING", + "CURRENT_USER", "MAX", "TRANSACTION", "CURSOR", "MIN", + "TRANSLATE", "DATE", "MINUTE", "TRANSLATION", "DAY", "MODULE", + "TRIM", "DEALLOCATE", "MONTH", "TRUE", "DEC", "NAMES", "UNION", + "DECIMAL", "NATIONAL", "UNIQUE", "DECLARE", "NATURAL", + "UNKNOWN", "DEFAULT", "NCHAR", "UPDATE", "DEFERRABLE", "NEXT", + "UPPER", "DEFERRED", "NO", "USAGE", "DELETE", "NONE", "USER", + "DESC", "NOT", "USING", "DESCRIBE", "NULL", "VALUE", + "DESCRIPTOR", "NULLIF", "VALUES", "DIAGNOSTICS", "NUMERIC", + "VARCHAR", "DISCONNECT", "OCTET_LENGTH", "VARYING", "DISTINCT", + "OF", "VIEW", "DOMAIN", "ON", "WHEN", "DOUBLE", "ONLY", + "WHENEVER", "DROP", "OPEN", "WHERE", "ELSE", "OPTION", "WITH", + "END", "OR", "WORK", "END-EXEC", "ORDER", "WRITE", "ESCAPE", + "OUTER", "YEAR", "EXCEPT", "OUTPUT", "ZONE", "EXCEPTION" }; + + TreeMap mySQLKeywordMap = new TreeMap(); + + for (int i = 0; i < allMySQLKeywords.length; i++) { + mySQLKeywordMap.put(allMySQLKeywords[i], null); + } + + HashMap sql92KeywordMap = new HashMap(sql92Keywords.length); + + for (int i = 0; i < sql92Keywords.length; i++) { + sql92KeywordMap.put(sql92Keywords[i], null); + } + + Iterator it = sql92KeywordMap.keySet().iterator(); + + while (it.hasNext()) { + mySQLKeywordMap.remove(it.next()); + } + + StringBuffer keywordBuf = new StringBuffer(); + + it = mySQLKeywordMap.keySet().iterator(); + + if (it.hasNext()) { + keywordBuf.append(it.next().toString()); + } + + while (it.hasNext()) { + keywordBuf.append(","); + keywordBuf.append(it.next().toString()); + } + + mysqlKeywordsThatArentSQL92 = keywordBuf.toString(); + } + + 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); + } + + /** 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); + } + + 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] = typeDesc.columnSize == null ? null : s2b(typeDesc.columnSize + .toString()); // PRECISION + row[8] = s2b(Integer.toString(typeDesc.bufferLength)); // LENGTH + row[9] = typeDesc.decimalDigits == null ? null + : s2b(typeDesc.decimalDigits.toString()); // SCALE + row[10] = s2b(Integer.toString(typeDesc.numPrecRadix)); // RADIX + // Map 'column****' to 'procedure****' + switch (typeDesc.nullability) { + case columnNoNulls: + row[11] = s2b(Integer.toString(procedureNoNulls)); // NULLABLE + + 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; + } + + /** + * Finds the end of the parameter declaration from the output of "SHOW + * CREATE PROCEDURE". + * + * @param beginIndex + * should be the index of the procedure body that contains the + * first "(". + * @param procedureDef + * the procedure body + * @param quoteChar + * the identifier quote character in use + * @return the ending index of the parameter declaration, not including the + * closing ")" + * @throws SQLException + * if a parse error occurs. + */ + private int endPositionOfParameterDeclaration(int beginIndex, + String procedureDef, String quoteChar) throws SQLException { + int currentPos = beginIndex + 1; + int parenDepth = 1; // counting the first openParen + + while (parenDepth > 0 && currentPos < procedureDef.length()) { + int closedParenIndex = StringUtils.indexOfIgnoreCaseRespectQuotes( + currentPos, procedureDef, ")", quoteChar.charAt(0), + !this.conn.isNoBackslashEscapesSet()); + + if (closedParenIndex != -1) { + int nextOpenParenIndex = StringUtils + .indexOfIgnoreCaseRespectQuotes(currentPos, + procedureDef, "(", quoteChar.charAt(0), + !this.conn.isNoBackslashEscapesSet()); + + if (nextOpenParenIndex != -1 + && nextOpenParenIndex < closedParenIndex) { + parenDepth++; + currentPos = closedParenIndex + 1; // set after closed + // paren that increases + // depth + } else { + parenDepth--; + currentPos = closedParenIndex; // start search from same + // position + } + } else { + // we should always get closed paren of some sort + throw SQLError + .createSQLException( + "Internal error when parsing callable statement metadata", + SQLError.SQL_STATE_GENERAL_ERROR); + } + } + + return currentPos; + } + + /** + * 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(); + try { + rs = stmt.executeQuery(query); + } catch (SQLException sqlEx) { + // Table might've disappeared on us, not really an error + String sqlState = sqlEx.getSQLState(); + + if (!"42S02".equals(sqlState) && + sqlEx.getErrorCode() != MysqlErrorNumbers.ER_NO_SUCH_TABLE) { + throw sqlEx; + } + + continue; + } + + while (rs.next()) { + extractForeignKeyForTable(rows, rs, catalog); + } + } + } finally { + if (rs != null) { + rs.close(); + } + + rs = null; + + if (stmt != null) { + stmt.close(); + } + + stmt = null; + } + + return buildResultSet(fields, rows); + } + + /** + * Finds the end of the RETURNS clause for SQL Functions by using any of the + * keywords allowed after the RETURNS clause, or a label. + * + * @param procedureDefn + * the function body containing the definition of the function + * @param quoteChar + * the identifier quote string in use + * @param positionOfReturnKeyword + * the position of "RETRUNS" in the definition + * @return the end of the returns clause + * @throws SQLException + * if a parse error occurs + */ + private int findEndOfReturnsClause(String procedureDefn, String quoteChar, + int positionOfReturnKeyword) throws SQLException { + /* + * characteristic: LANGUAGE SQL | [NOT] DETERMINISTIC | { CONTAINS SQL | + * NO SQL | READS SQL DATA | MODIFIES SQL DATA } | SQL SECURITY { + * DEFINER | INVOKER } | COMMENT 'string' + */ + + String[] tokens = new String[] { "LANGUAGE", "NOT", "DETERMINISTIC", + "CONTAINS", "NO", "READ", "MODIFIES", "SQL", "COMMENT", "BEGIN", + "RETURN" }; + + int startLookingAt = positionOfReturnKeyword + "RETURNS".length() + 1; + + for (int i = 0; i < tokens.length; i++) { + int endOfReturn = StringUtils.indexOfIgnoreCaseRespectQuotes( + startLookingAt, procedureDefn, tokens[i], quoteChar + .charAt(0), !this.conn.isNoBackslashEscapesSet()); + + if (endOfReturn != -1) { + return endOfReturn; + } + } + + // Label? + int endOfReturn = StringUtils.indexOfIgnoreCaseRespectQuotes( + startLookingAt, procedureDefn, ":", quoteChar.charAt(0), + !this.conn.isNoBackslashEscapesSet()); + + if (endOfReturn != -1) { + // seek back until whitespace + for (int i = endOfReturn; i > 0; i--) { + if (Character.isWhitespace(procedureDefn.charAt(i))) { + return i; + } + } + } + + // We can't parse it. + + throw SQLError.createSQLException( + "Internal error when parsing callable statement metadata", + SQLError.SQL_STATE_GENERAL_ERROR); + } + + /** + * @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. + *+ * Each column description has the following columns: + *
Note: Some databases may not return the column + * descriptions for a procedure. Additional columns beyond REMARKS can be + * defined by the database.
@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); + } + + String quoteChar = getIdentifierQuoteString(); + + String parameterDef = null; + + boolean isProcedureInAnsiMode = false; + String storageDefnDelims = null; + String storageDefnClosures = 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"); + + if (procedureDef == null || procedureDef.length() == 0) { + throw SQLError + .createSQLException( + "User does not have access to metadata required to determine " + + "stored procedure parameter types. If rights can not be granted, configure connection with \"noAccessToProcedureBodies=true\" " + + "to have driver generate parameters that represent INOUT strings irregardless of actual parameter types.", + SQLError.SQL_STATE_GENERAL_ERROR); + } + + try { + String sqlMode = paramRetrievalRs.getString("sql_mode"); + + if (StringUtils.indexOfIgnoreCase(sqlMode, "ANSI") != -1) { + isProcedureInAnsiMode = true; + } + } catch (SQLException sqlEx) { + // doesn't exist + } + + String identifierMarkers = isProcedureInAnsiMode ? "`\"" : "`"; + String identifierAndStringMarkers = "'" + identifierMarkers; + storageDefnDelims = "(" + identifierMarkers; + storageDefnClosures = ")" + identifierMarkers; + + // sanitize/normalize by stripping out comments + procedureDef = StringUtils.stripComments(procedureDef, + identifierAndStringMarkers, identifierAndStringMarkers, true, false, true, true); + + int openParenIndex = StringUtils + .indexOfIgnoreCaseRespectQuotes(0, procedureDef, "(", + quoteChar.charAt(0), !this.conn + .isNoBackslashEscapesSet()); + int endOfParamDeclarationIndex = 0; + + endOfParamDeclarationIndex = endPositionOfParameterDeclaration( + openParenIndex, procedureDef, quoteChar); + + if (parsingFunction) { + + // Grab the return column since it needs + // to go first in the output result set + int returnsIndex = StringUtils + .indexOfIgnoreCaseRespectQuotes(0, procedureDef, + " RETURNS ", quoteChar.charAt(0), + !this.conn.isNoBackslashEscapesSet()); + + int endReturnsDef = findEndOfReturnsClause(procedureDef, + quoteChar, returnsIndex); + + // Trim off whitespace after "RETURNS" + + int declarationStart = returnsIndex + "RETURNS ".length(); + + while (declarationStart < procedureDef.length()) { + if (Character.isWhitespace(procedureDef.charAt(declarationStart))) { + declarationStart++; + } else { + break; + } + } + + String returnsDefn = procedureDef.substring(declarationStart, endReturnsDef).trim(); + TypeDescriptor returnDescriptor = new TypeDescriptor( + returnsDefn, null); + + resultRows.add(convertTypeDescriptorToProcedureRow( + procNameAsBytes, "", false, false, true, + returnDescriptor)); + } + + if ((openParenIndex == -1) + || (endOfParamDeclarationIndex == -1)) { + // parse error? + throw SQLError + .createSQLException( + "Internal error when parsing callable statement metadata", + SQLError.SQL_STATE_GENERAL_ERROR); + } + + parameterDef = procedureDef.substring(openParenIndex + 1, + endOfParamDeclarationIndex); + } + } 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); + } + + if ((paramName.startsWith("`") && paramName.endsWith("`")) || + (isProcedureInAnsiMode && paramName.startsWith("\"") && paramName.endsWith("\""))) { + paramName = paramName.substring(1, paramName.length() - 1); + } + + int wildCompareRes = StringUtils.wildCompare(paramName, + parameterNamePattern); + + if (wildCompareRes != StringUtils.WILD_COMPARE_NO_MATCH) { + byte[][] row = convertTypeDescriptorToProcedureRow( + 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. + *+ * The catalog column is: + *
+ * Only privileges matching the column name criteria are returned. They are + * ordered by COLUMN_NAME and PRIVILEGE. + *
+ *+ * Each privilige description has the following columns: + *
+ * 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. + *
+ *+ * Each column description has the following columns: + *
+ * Each foreign key column description has the following columns: + *
+ * Each foreign key column description has the following columns: + *
+ * Each primary key column description has the following columns: + *
+ * Each index column description has the following columns: + *
+ * Each column description has the following columns: + *
+ * 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. + *
+ *+ * Each row in the ResultSet is a parameter desription or column description + * with the following fields: + *
+ * Note: Some databases may not return the column descriptions for a + * procedure. Additional columns beyond REMARKS can be defined by the + * database. + *
+ * + * @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. + *+ * Only procedure descriptions matching the schema and procedure name + * criteria are returned. They are ordered by PROCEDURE_SCHEM, and + * PROCEDURE_NAME. + *
+ *+ * Each procedure description has the the following columns: + *
+ * The schema column is: + *
+ * The '_' character represents any single character. + *
+ *+ * The '%' character represents any sequence of zero or more characters. + *
+ * + * @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 mysqlKeywordsThatArentSQL92; + } + + /** + * @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. + *+ * Only privileges matching the schema and table name criteria are returned. + * They are ordered by TABLE_SCHEM, TABLE_NAME, and PRIVILEGE. + *
+ *+ * Each privilige description has the following columns: + *
+ * 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. + *
+ *+ * Each table description has the following columns: + *
+ * Note: Some databases may not return information for all tables. + *
+ * + * @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. + *+ * The table type is: + *
+ * Each type description has the following columns: + *
+ * Each type description has the following columns: + *
+ * 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. + *
+ *+ * Each type description has the following columns: + *
+ * Note: If the driver does not support UDTs then an empty result + * set is returned. + *
+ * + * @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. + *+ * Each column description has the following columns: + *
+ * 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. + *
+ * + * @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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java new file mode 100644 index 00000000..ddf614ab --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/DatabaseMetaDataUsingInfoSchema.java @@ -0,0 +1,1214 @@ +/* + 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. + *+ * Only privileges matching the column name criteria are returned. They are + * ordered by COLUMN_NAME and PRIVILEGE. + *
+ *+ * Each privilige description has the following columns: + *
+ * 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. + *
+ *+ * Each column description has the following columns: + *
+ * Each foreign key column description has the following columns: + *
+ * Each foreign key column description has the following columns: + *
+ * Each primary key column description has the following columns: + *
+ * Each index column description has the following columns: + *
+ * Each column description has the following columns: + *
+ * Only procedure descriptions matching the schema and procedure name + * criteria are returned. They are ordered by PROCEDURE_SCHEM, and + * PROCEDURE_NAME. + *
+ *+ * Each procedure description has the the following columns: + *
+ * 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. + *
+ *+ * Each table description has the following columns: + *
+ * Note: Some databases may not return information for all tables. + *
+ * + * @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()) { + 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); + + if (catalog != null) { + pStmt.setString(1, catalog); + } else { + pStmt.setString(1, "%"); + } + + 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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/DocsConnectionPropsHelper.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/DocsConnectionPropsHelper.java new file mode 100644 index 00000000..f41c01a2 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/DocsConnectionPropsHelper.java @@ -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()); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Driver.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Driver.java new file mode 100644 index 00000000..518717ea --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Driver.java @@ -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 + * + *+ * 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. + * + *
+ * 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. + * + *
+ * 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() + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeProcessor.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeProcessor.java new file mode 100644 index 00000000..060e2f1f --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeProcessor.java @@ -0,0 +1,676 @@ +/* + Copyright (C) 2002-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ + +/** + * 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) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + + escapeSequence = escapeSequence.substring(1, + escapeSequence.length() - 1); + replaceEscapeSequence = true; + } + } catch (java.util.NoSuchElementException e) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } + } 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)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + StringTokenizer st = new StringTokenizer(argument, + " -"); + String year4 = st.nextToken(); + String month2 = st.nextToken(); + String day2 = st.nextToken(); + String dateString = "'" + year4 + "-" + month2 + + "-" + day2 + "'"; + newSql.append(dateString); + } catch (java.util.NoSuchElementException e) { + throw SQLError.createSQLException( + "Syntax error for DATE escape sequence '" + + argument + "'", "42000"); + } + } + } else if (StringUtils.startsWithIgnoreCase(collapsedToken, + "{ts")) { + int startPos = token.indexOf('\'') + 1; + int endPos = token.lastIndexOf('\''); // no } + + if ((startPos == -1) || (endPos == -1)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + StringTokenizer st = new StringTokenizer(argument, + " .-:"); + String year4 = st.nextToken(); + String month2 = st.nextToken(); + String day2 = st.nextToken(); + String hour = st.nextToken(); + String minute = st.nextToken(); + String second = st.nextToken(); + + /* + * For now, we get the fractional seconds part, but + * we don't use it, as MySQL doesn't support it in + * it's TIMESTAMP data type + * + * String fractionalSecond = ""; + * + * if (st.hasMoreTokens()) { fractionalSecond = + * st.nextToken(); } + */ + /* + * Use the full format because number format will + * not work for "between" clauses. + * + * Ref. Mysql Docs + * + * You can specify DATETIME, DATE and TIMESTAMP + * values using any of a common set of formats: + * + * As a string in either 'YYYY-MM-DD HH:MM:SS' or + * 'YY-MM-DD HH:MM:SS' format. + * + * Thanks to Craig Longman for pointing out this bug + */ + if (!conn.getUseTimezone() && !conn.getUseJDBCCompliantTimezoneShift()) { + newSql.append("'").append(year4).append("-") + .append(month2).append("-").append(day2) + .append(" ").append(hour).append(":") + .append(minute).append(":").append(second) + .append("'"); + } else { + Calendar sessionCalendar; + + if (conn != null) { + sessionCalendar = conn.getCalendarInstanceForSessionOrNew(); + } 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)) { + newSql.append(token); // it's just part of the query, push possible syntax errors onto server's shoulders + } else { + + String argument = token.substring(startPos, endPos); + + try { + StringTokenizer st = new StringTokenizer(argument, + " :"); + String hour = st.nextToken(); + String minute = st.nextToken(); + String second = st.nextToken(); + + if (!conn.getUseTimezone()) { + String timeString = "'" + hour + ":" + minute + ":" + + second + "'"; + newSql.append(timeString); + } else { + Calendar sessionCalendar = null; + + 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(); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeProcessorResult.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeProcessorResult.java new file mode 100644 index 00000000..2518c288 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeProcessorResult.java @@ -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; +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeTokenizer.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeTokenizer.java new file mode 100644 index 00000000..f19608a2 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/EscapeTokenizer.java @@ -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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ExportControlled.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ExportControlled.java new file mode 100644 index 00000000..9e4d5dea --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ExportControlled.java @@ -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 diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Field.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Field.java new file mode 100644 index 00000000..bf0a780c --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Field.java @@ -0,0 +1,895 @@ +/* + 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 6531 2007-08-30 15:21:06Z mmatthews $ + */ +public class Field { + + private static final int AUTO_INCREMENT_FLAG = 512; + + private static final int NO_CHARSET_INFO = -1; + + 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; + + private int maxBytesPerChar; + + /** + * 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); + + checkForImplicitTemporaryTable(); + // Re-map to 'real' blob type, if we're a BLOB + + if (this.mysqlType == MysqlDefs.FIELD_TYPE_BLOB) { + boolean isFromFunction = this.originalTableNameLength == 0; + + if (this.connection != null && this.connection.getBlobsAreStrings() || + (this.connection.getFunctionsNeverReturnBlobs() && isFromFunction)) { + this.sqlType = Types.VARCHAR; + this.mysqlType = MysqlDefs.FIELD_TYPE_VARCHAR; + } else if (this.charsetIndex == 63 || + !this.connection.versionMeetsMinimum(4, 1, 0)) { + setBlobTypeBasedOnLength(); + this.sqlType = MysqlDefs.mysqlToJavaType(this.mysqlType); + } 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.connection.getBlobsAreStrings()) { + this.sqlType = Types.BINARY; + } + } + + + + if (this.mysqlType == MysqlDefs.FIELD_TYPE_BIT) { + this.isSingleBit = (this.length == 0); + + if (this.connection != null && (this.connection.versionMeetsMinimum(5, 0, 21) || + this.connection.versionMeetsMinimum(5, 1, 10)) && this.length == 1) { + this.isSingleBit = true; + } + + if (this.isSingleBit) { + this.sqlType = Types.BIT; + } else { + this.sqlType = Types.VARBINARY; + this.colFlag |= 128; // we need to pretend this is a full + this.colFlag |= 16; // binary blob + isBinary = true; + } + } + + // + // Handle TEXT type (special case), Fix proposed by Peter McKeown + // + 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; + } + } 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; + } + + 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)) { + if (this.connection.getUseDynamicCharsetInfo()) { + 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; + } + } + } + } else { + this.collationName = CharsetMapping.INDEX_TO_COLLATION[charsetIndex]; + } + } + } + } + + return this.collationName; + } + + public String getColumnLabel() throws SQLException { + return getName(); // column name if not aliased, alias if used + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public String getDatabaseName() throws SQLException { + 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 synchronized int getMaxBytesPerCharacter() throws SQLException { + if (this.maxBytesPerChar == 0) { + this.maxBytesPerChar = this.connection.getMaxBytesPerChar(getCharacterSet()); + } + + return this.maxBytesPerChar; + } + + /** + * DOCUMENT ME! + * + * @return DOCUMENT ME! + */ + public int getMysqlType() { + 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 && ( + this.connection != null && !this.connection.versionMeetsMinimum(5, 0, 25))) { + 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(128); + 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()); + asString.append("("); + asString.append(MysqlDefs.typeToName(getMysqlType())); + asString.append(")"); + + 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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LicenseConfiguration.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LicenseConfiguration.java new file mode 100644 index 00000000..41edf57a --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LicenseConfiguration.java @@ -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 + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LoadBalancingConnectionProxy.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LoadBalancingConnectionProxy.java new file mode 100644 index 00000000..1063ab17 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LoadBalancingConnectionProxy.java @@ -0,0 +1,532 @@ +/* + Copyright (C) 2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + */ + +package com.mysql.jdbc; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.sql.SQLException; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; + +/** + * An implementation of java.sql.Connection that load balances requests across a + * series of MySQL JDBC connections, where the balancing takes place at + * transaction commit. + * + * Therefore, for this to work (at all), you must use transactions, even if only + * reading data. + * + * This implementation will invalidate connections that it detects have had + * communication errors when processing a request. A new connection to the + * problematic host will be attempted the next time it is selected by the load + * balancing algorithm. + * + * This implementation is thread-safe, but it's questionable whether sharing a + * connection instance amongst threads is a good idea, given that transactions + * are scoped to connections in JDBC. + * + * @version $Id: $ + * + */ +public class LoadBalancingConnectionProxy implements InvocationHandler, PingTarget { + + private static Method getLocalTimeMethod; + + static { + try { + getLocalTimeMethod = System.class.getMethod("nanoTime", + new Class[0]); + } catch (SecurityException e) { + // ignore + } catch (NoSuchMethodException e) { + // ignore + } + } + + interface BalanceStrategy { + abstract Connection pickConnection() throws SQLException; + } + + class BestResponseTimeBalanceStrategy implements BalanceStrategy { + + public Connection pickConnection() throws SQLException { + long minResponseTime = Long.MAX_VALUE; + + int bestHostIndex = 0; + + long[] localResponseTimes = new long[responseTimes.length]; + + synchronized (responseTimes) { + System.arraycopy(responseTimes, 0, localResponseTimes, 0, responseTimes.length); + } + + SQLException ex = null; + + for (int attempts = 0; attempts < 1200 /* 5 minutes */; attempts++) { + for (int i = 0; i < localResponseTimes.length; i++) { + long candidateResponseTime = localResponseTimes[i]; + + if (candidateResponseTime < minResponseTime) { + if (candidateResponseTime == 0) { + bestHostIndex = i; + + break; + } + + bestHostIndex = i; + minResponseTime = candidateResponseTime; + } + } + + if (bestHostIndex == localResponseTimes.length - 1) { + // try again, assuming that the previous list was mostly + // correct as far as distribution of response times went + + synchronized (responseTimes) { + System.arraycopy(responseTimes, 0, localResponseTimes, 0, responseTimes.length); + } + + continue; + } + String bestHost = (String) hostList.get(bestHostIndex); + + Connection conn = (Connection) liveConnections.get(bestHost); + + if (conn == null) { + try { + conn = createConnectionForHost(bestHost); + } catch (SQLException sqlEx) { + ex = sqlEx; + + if (sqlEx instanceof CommunicationsException || "08S01".equals(sqlEx.getSQLState())) { + localResponseTimes[bestHostIndex] = Long.MAX_VALUE; + + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + continue; + } else { + throw sqlEx; + } + } + } + + return conn; + } + + if (ex != null) { + throw ex; + } + + return null; // we won't get here, compiler can't tell + } + } + + // Lifted from C/J 5.1's JDBC-2.0 connection pool classes, let's merge this + // if/when this gets into 5.1 + protected class ConnectionErrorFiringInvocationHandler implements + InvocationHandler { + Object invokeOn = null; + + public ConnectionErrorFiringInvocationHandler(Object toInvokeOn) { + invokeOn = toInvokeOn; + } + + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + Object result = null; + + try { + result = method.invoke(invokeOn, args); + + if (result != null) { + result = proxyIfInterfaceIsJdbc(result, result.getClass()); + } + } catch (InvocationTargetException e) { + dealWithInvocationException(e); + } + + return result; + } + } + + class RandomBalanceStrategy implements BalanceStrategy { + + public Connection pickConnection() throws SQLException { + int random = (int) (Math.random() * hostList.size()); + + if (random == hostList.size()) { + random--; + } + + String hostPortSpec = (String) hostList.get(random); + + SQLException ex = null; + + for (int attempts = 0; attempts < 1200 /* 5 minutes */; attempts++) { + Connection conn = (Connection) liveConnections.get(hostPortSpec); + + if (conn == null) { + try { + conn = createConnectionForHost(hostPortSpec); + } catch (SQLException sqlEx) { + ex = sqlEx; + + if (sqlEx instanceof CommunicationsException || "08S01".equals(sqlEx.getSQLState())) { + + try { + Thread.sleep(250); + } catch (InterruptedException e) { + } + + continue; + } else { + throw sqlEx; + } + } + } + + return conn; + } + + if (ex != null) { + throw ex; + } + + return null; // we won't get here, compiler can't tell + } + + } + + private Connection currentConn; + + private List hostList; + + private Map liveConnections; + + private Map connectionsToHostsMap; + + private long[] responseTimes; + + private Map hostsToListIndexMap; + + boolean inTransaction = false; + + long transactionStartTime = 0; + + Properties localProps; + + boolean isClosed = false; + + BalanceStrategy balancer; + + /** + * Creates a proxy for java.sql.Connection that routes requests between the + * given list of host:port and uses the given properties when creating + * connections. + * + * @param hosts + * @param props + * @throws SQLException + */ + LoadBalancingConnectionProxy(List hosts, Properties props) + throws SQLException { + this.hostList = hosts; + + int numHosts = this.hostList.size(); + + this.liveConnections = new HashMap(numHosts); + this.connectionsToHostsMap = new HashMap(numHosts); + this.responseTimes = new long[numHosts]; + this.hostsToListIndexMap = new HashMap(numHosts); + + for (int i = 0; i < numHosts; i++) { + this.hostsToListIndexMap.put(this.hostList.get(i), new Integer(i)); + } + + this.localProps = (Properties) props.clone(); + this.localProps.remove(NonRegisteringDriver.HOST_PROPERTY_KEY); + this.localProps.remove(NonRegisteringDriver.PORT_PROPERTY_KEY); + this.localProps.setProperty("useLocalSessionState", "true"); + + String strategy = this.localProps.getProperty("loadBalanceStrategy", + "random"); + + if ("random".equals(strategy)) { + this.balancer = new RandomBalanceStrategy(); + } else if ("bestResponseTime".equals(strategy)) { + this.balancer = new BestResponseTimeBalanceStrategy(); + } else { + throw SQLError.createSQLException(Messages.getString( + "InvalidLoadBalanceStrategy", new Object[] { strategy }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + pickNewConnection(); + } + + /** + * Creates a new physical connection for the given host:port and updates + * required internal mappings and statistics for that connection. + * + * @param hostPortSpec + * @return + * @throws SQLException + */ + private synchronized Connection createConnectionForHost(String hostPortSpec) + throws SQLException { + Properties connProps = (Properties) this.localProps.clone(); + + String[] hostPortPair = NonRegisteringDriver + .parseHostPortPair(hostPortSpec); + + if (hostPortPair[1] == null) { + hostPortPair[1] = "3306"; + } + + connProps.setProperty(NonRegisteringDriver.HOST_PROPERTY_KEY, + hostPortSpec); + connProps.setProperty(NonRegisteringDriver.PORT_PROPERTY_KEY, + hostPortPair[1]); + + Connection conn = new Connection(hostPortSpec, Integer + .parseInt(hostPortPair[1]), connProps, connProps + .getProperty(NonRegisteringDriver.DBNAME_PROPERTY_KEY), + "jdbc:mysql://" + hostPortPair[0] + ":" + hostPortPair[1] + "/"); + + this.liveConnections.put(hostPortSpec, conn); + this.connectionsToHostsMap.put(conn, hostPortSpec); + + return conn; + } + + /** + * @param e + * @throws SQLException + * @throws Throwable + * @throws InvocationTargetException + */ + void dealWithInvocationException(InvocationTargetException e) + throws SQLException, Throwable, InvocationTargetException { + Throwable t = e.getTargetException(); + + if (t != null) { + if (t instanceof SQLException) { + String sqlState = ((SQLException) t).getSQLState(); + + if (sqlState != null) { + if (sqlState.startsWith("08")) { + // connection error, close up shop on current + // connection + invalidateCurrentConnection(); + } + } + } + + throw t; + } + + throw e; + } + + /** + * Closes current connection and removes it from required mappings. + * + * @throws SQLException + */ + synchronized void invalidateCurrentConnection() throws SQLException { + try { + if (!this.currentConn.isClosed()) { + this.currentConn.close(); + } + + } finally { + this.liveConnections.remove(this.connectionsToHostsMap + .get(this.currentConn)); + this.connectionsToHostsMap.remove(this.currentConn); + } + } + + /** + * Proxies method invocation on the java.sql.Connection interface, trapping + * "close", "isClosed" and "commit/rollback" (to switch connections for load + * balancing). + */ + public Object invoke(Object proxy, Method method, Object[] args) + throws Throwable { + + String methodName = method.getName(); + + if ("close".equals(methodName)) { + synchronized (this.liveConnections) { + // close all underlying connections + Iterator allConnections = this.liveConnections.values() + .iterator(); + + while (allConnections.hasNext()) { + ((Connection) allConnections.next()).close(); + } + + this.liveConnections.clear(); + this.connectionsToHostsMap.clear(); + } + + return null; + } + + if ("isClosed".equals(methodName)) { + return Boolean.valueOf(this.isClosed); + } + + if (this.isClosed) { + throw SQLError.createSQLException( + "No operations allowed after connection closed.", + SQLError.SQL_STATE_CONNECTION_NOT_OPEN); + } + + if (!inTransaction) { + this.inTransaction = true; + this.transactionStartTime = getLocalTimeBestResolution(); + } + + Object result = null; + + try { + result = method.invoke(this.currentConn, args); + + if (result != null) { + if (result instanceof com.mysql.jdbc.Statement) { + ((com.mysql.jdbc.Statement)result).setPingTarget(this); + } + + result = proxyIfInterfaceIsJdbc(result, result.getClass()); + } + } catch (InvocationTargetException e) { + dealWithInvocationException(e); + } finally { + if ("commit".equals(methodName) || "rollback".equals(methodName)) { + this.inTransaction = false; + + // Update stats + int hostIndex = ((Integer) this.hostsToListIndexMap + .get(this.connectionsToHostsMap.get(this.currentConn))) + .intValue(); + + synchronized (this.responseTimes) { + this.responseTimes[hostIndex] = getLocalTimeBestResolution() + - this.transactionStartTime; + } + + pickNewConnection(); + } + } + + return result; + } + + /** + * Picks the "best" connection to use for the next transaction based on the + * BalanceStrategy in use. + * + * @throws SQLException + */ + private synchronized void pickNewConnection() throws SQLException { + if (this.currentConn == null) { + this.currentConn = this.balancer.pickConnection(); + + return; + } + + Connection newConn = this.balancer.pickConnection(); + + newConn.setTransactionIsolation(this.currentConn + .getTransactionIsolation()); + newConn.setAutoCommit(this.currentConn.getAutoCommit()); + this.currentConn = newConn; + } + + /** + * Recursively checks for interfaces on the given object to determine if it + * implements a java.sql interface, and if so, proxies the instance so that + * we can catch and fire SQL errors. + * + * @param toProxy + * @param clazz + * @return + */ + Object proxyIfInterfaceIsJdbc(Object toProxy, Class clazz) { + Class[] interfaces = clazz.getInterfaces(); + + for (int i = 0; i < interfaces.length; i++) { + String packageName = interfaces[i].getPackage().getName(); + + if ("java.sql".equals(packageName) + || "javax.sql".equals(packageName)) { + return Proxy.newProxyInstance(toProxy.getClass() + .getClassLoader(), interfaces, + new ConnectionErrorFiringInvocationHandler(toProxy)); + } + + return proxyIfInterfaceIsJdbc(toProxy, interfaces[i]); + } + + return toProxy; + } + + /** + * Returns best-resolution representation of local time, using nanoTime() if + * availble, otherwise defaulting to currentTimeMillis(). + */ + private static long getLocalTimeBestResolution() { + if (getLocalTimeMethod != null) { + try { + return ((Long) getLocalTimeMethod.invoke(null, null)) + .longValue(); + } catch (IllegalArgumentException e) { + // ignore - we fall through to currentTimeMillis() + } catch (IllegalAccessException e) { + // ignore - we fall through to currentTimeMillis() + } catch (InvocationTargetException e) { + // ignore - we fall through to currentTimeMillis() + } + } + + return System.currentTimeMillis(); + } + + public synchronized void doPing() throws SQLException { + Iterator allConns = this.liveConnections.values().iterator(); + + while (allConns.hasNext()) { + ((Connection)allConns.next()).ping(); + } + } +} \ No newline at end of file diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LocalizedErrorMessages.properties b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LocalizedErrorMessages.properties new file mode 100644 index 00000000..74d7562f --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/LocalizedErrorMessages.properties @@ -0,0 +1,424 @@ +# +# 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_low=Column Index out of range, {0} < 1. +ResultSet.Column_Index_out_of_range_high=Column Index out of range, {0} > {1}. +ResultSet.Value_is_out_of_range=Value ''{0}'' is out of range [{1}, {2}]. +ResultSet.Positioned_Update_not_supported=Positioned Update not supported. +ResultSet.Bad_format_for_Date=Bad format for DATE ''{0}'' in column {1}. +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 \' + +# +# Usage advisor messages for ResultSets +# + +ResultSet.ResultSet_implicitly_closed_by_driver=ResultSet implicitly closed by driver.\n\nYou should close ResultSets explicitly from your code to free up resources in a more efficient manner. +ResultSet.Possible_incomplete_traversal_of_result_set=Possible incomplete traversal of result set. Cursor was left on row {0} of {1} rows when it was closed.\n\nYou should consider re-formulating your query to return only the rows you are interested in using. +ResultSet.The_following_columns_were_never_referenced=The following columns were part of the SELECT statement for this result set, but were never referenced: +ResultSet.Too_Large_Result_Set=Result set size of {0} rows is larger than \"resultSetSizeThreshold\" of {1} rows. Application may be requesting more data than it is using. Consider reformulating the query. +ResultSet.CostlyConversion=ResultSet type conversion via parsing detected when calling {0} for column {1} (column named '{2}') in table '{3}'{4}\n\nJava class of column type is '{5}', MySQL field type is '{6}'.\n\nTypes that could be converted directly without parsing are:\n{7} +ResultSet.CostlyConversionCreatedFromQuery= created from query:\n\n + +ResultSet.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.19a=The configuration parameter \"localSocketAddress\" has been set to a network interface not available for use by the JVM. +CommunicationsException.20=Communications link failure +CommunicationsException.21=\ due to underlying exception: +CommunicationsException.ClientWasStreaming=Application was streaming results when the connection failed. Consider raising value of 'net_write_timeout' on the server. +NonRegisteringDriver.3=Hostname of MySQL Server +NonRegisteringDriver.7=Port number of MySQL Server +NonRegisteringDriver.13=Username to authenticate as +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.SlowQuery=Slow query (exceeded {0} {1}, duration: {2} {1}): +Nanoseconds=ns +Milliseconds=ms +MysqlIO.28=Not issuing EXPLAIN for query of size > +MysqlIO.29=\ bytes. +MysqlIO.33=The following query was executed with a bad index, use 'EXPLAIN' for more details: +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: +MysqlIO.EOF=Can not read response from server. Expected to read {0} bytes, read {1} bytes before connection was unexpectedly lost. +MysqlIO.NoInnoDBStatusFound=No InnoDB status output returned by server. +MysqlIO.InnoDBStatusFailed=Couldn't retrieve InnoDB status due to underlying exception: +MysqlIO.LoadDataLocalNotAllowed=Server asked for stream in response to LOAD DATA LOCAL INFILE but functionality is disabled at client by 'allowLoadLocalInfile' being set to 'false'. +NotImplemented.0=Feature not implemented +PreparedStatement.0=SQL String can not be NULL +PreparedStatement.1=SQL String can not be NULL +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, can not use functions and must +NotUpdatable.4=select all primary keys from that table. See the JDBC 2.1 API Specification, +NotUpdatable.5=section 5.6 for more details. +NotUpdatableReason.0=Result Set not updatable (references more than one table). +NotUpdatableReason.1=Result Set not updatable (references more than one database). +NotUpdatableReason.2=Result Set not updatable (references no tables). +NotUpdatableReason.3=Result Set not updatable (references computed values or doesn't reference any columns or tables). +NotUpdatableReason.4=Result Set not updatable (references no primary keys). +NotUpdatableReason.5=Result Set not updatable (referenced table has no primary keys). +NotUpdatableReason.6=Result Set not updatable (references unknown primary key {0}). +NotUpdatableReason.7=Result Set not updatable (does not reference all primary keys). + +InvalidLoadBalanceStrategy=Invalid load balancing strategy '{0}'. +Connection.Connection.BadValueInServerVariables=Invalid value '{1}' for server variable named '{0}', falling back to sane default of '{2}'. diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Messages.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Messages.java new file mode 100644 index 00000000..164f269a --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Messages.java @@ -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 + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MiniAdmin.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MiniAdmin.java new file mode 100644 index 00000000..d5307fe8 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MiniAdmin.java @@ -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(); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlDataTruncation.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlDataTruncation.java new file mode 100644 index 00000000..1b4513f3 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlDataTruncation.java @@ -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$ + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlDefs.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlDefs.java new file mode 100644 index 00000000..2e18ccf2 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlDefs.java @@ -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 "); + + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlErrorNumbers.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlErrorNumbers.java new file mode 100644 index 00000000..5bb7e35e --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlErrorNumbers.java @@ -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 + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlIO.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlIO.java new file mode 100644 index 00000000..677f8379 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlIO.java @@ -0,0 +1,4066 @@ +/* + Copyright (C) 2002-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ +package com.mysql.jdbc; + +import java.io.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.lang.reflect.Method; +import java.math.BigInteger; +import java.net.MalformedURLException; +import java.net.Socket; +import java.net.URL; +import java.nio.ByteBuffer; +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; + +import com.mysql.jdbc.profiler.ProfileEventSink; +import com.mysql.jdbc.profiler.ProfilerEvent; +import com.mysql.jdbc.util.ReadAheadInputStream; +import com.mysql.jdbc.util.ResultSetUtil; + + +/** + * This class is used by Connection for communicating with the MySQL server. + * + * @author Mark Matthews + * @version $Id: MysqlIO.java 6483 2007-07-18 03:19:18Z 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; + protected static final int AUTH_411_OVERHEAD = 33; + 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 + protected static final int INITIAL_PACKET_SIZE = 1024; + /** + * We store the platform 'encoding' here, only used to avoid munging + * filenames for LOAD DATA LOCAL INFILE... + */ + private static String jvmPlatformCharset = null; + + /** + * Are we using packed or unpacked binary result set rows? + */ + private boolean binaryResultsAreUnpacked = true; + + /** + * We need to have a 'marker' for all-zero datetimes so that ResultSet + * 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 Calendar sessionCalendar; + private boolean useConnectWithDb; + private boolean needToGrabQueryFromPacket; + private boolean autoGenerateTestcaseScript; + private long threadId; + private boolean useNanosForElapsedTime; + private long slowQueryThreshold; + private String queryTimingUnits; + + /** + * 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(INITIAL_PACKET_SIZE); + this.sendPacket = new Buffer(INITIAL_PACKET_SIZE); + + this.port = port; + this.host = host; + + this.socketFactoryClassName = socketFactoryClassName; + this.socketFactory = createSocketFactory(); + + this.mysqlConnection = this.socketFactory.connect(this.host, this.port, + props); + + if (socketTimeout != 0) { + try { + this.mysqlConnection.setSoTimeout(socketTimeout); + } catch (Exception ex) { + /* Ignore if the platform does not support it */ + ; + } + } + + this.mysqlConnection = this.socketFactory.beforeHandshake(); + + if (this.connection.getUseReadAheadInput()) { + this.mysqlInput = new ReadAheadInputStream(this.mysqlConnection + .getInputStream(), 16384, this.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); + + if (this.connection.getUseNanosForElapsedTime() + && Util.nanoTimeAvailable()) { + this.useNanosForElapsedTime = true; + + this.queryTimingUnits = Messages.getString("Nanoseconds"); + } else { + this.queryTimingUnits = Messages.getString("Milliseconds"); + } + + if (this.connection.getLogSlowQueries()) { + calculateSlowQueryThreshold(); + } + } + + /** + * 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, Field[] metadataFromCache) + throws SQLException { + Buffer packet; // The packet from the server + Field[] fields = null; + + // Read in the column information + + if (unpackFieldInfo) { + fields = new Field[(int) columnCount]; + + for (int i = 0; i < columnCount; i++) { + Buffer fieldPacket = null; + + fieldPacket = readPacket(); + fields[i] = unpackField(fieldPacket, false); + } + } else { + for (int i = 0; i < columnCount; i++) { + skipPacket(); + } + + //this.reusablePacket.clear(); + } + + packet = reuseAndReadPacket(this.reusablePacket); + + readServerStatusForResultSets(packet); + + // + // Handle cursor-based fetch first + // + + if (this.connection.versionMeetsMinimum(5, 0, 2) + && this.connection.getUseCursorFetch() + && isBinaryEncoded + && callingStatement != null + && callingStatement.getFetchSize() != 0 + && callingStatement.getResultSetType() == ResultSet.TYPE_FORWARD_ONLY) { + ServerPreparedStatement prepStmt = (com.mysql.jdbc.ServerPreparedStatement) callingStatement; + + Field[] fieldMetadata = ((com.mysql.jdbc.ResultSetMetaData) prepStmt.getMetaData()).fields; + + boolean usingCursor = true; + + // + // Server versions 5.0.5 or newer will only open + // a cursor and set this flag if they can, otherwise + // they punt and go back to mysql_store_results() behavior + // + + if (this.connection.versionMeetsMinimum(5, 0, 5)) { + usingCursor = (this.serverStatus & + SERVER_STATUS_CURSOR_EXISTS) != 0; + } + + if (usingCursor) { + RowData rows = new CursorRowProvider( + this, + prepStmt, + fields); + + ResultSet rs = buildResultSetWithRows( + callingStatement, + catalog, + fields, + rows, resultSetType, resultSetConcurrency, isBinaryEncoded); + + if (usingCursor) { + rs.setFetchSize(callingStatement.getFetchSize()); + } + + return rs; + } + } + + RowData rowData = null; + + if (!streamResults) { + rowData = readSingleRowSet(columnCount, maxRows, + resultSetConcurrency, isBinaryEncoded, unpackFieldInfo ? fields : metadataFromCache); + } else { + rowData = new RowDataDynamic(this, (int) columnCount, unpackFieldInfo ? fields : metadataFromCache, + 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; + } + } + + /** + * Reads and discards a single MySQL packet from the input stream. + * + * @throws SQLException if the network fails while skipping the + * packet. + */ + protected final void skipPacket() throws SQLException { + try { + + int lengthRead = readFully(this.mysqlInput, this.packetHeaderBuf, + 0, 4); + + if (lengthRead < 4) { + forceClose(); + throw new IOException(Messages.getString("MysqlIO.1")); //$NON-NLS-1$ + } + + int packetLength = (this.packetHeaderBuf[0] & 0xff) + + ((this.packetHeaderBuf[1] & 0xff) << 8) + + ((this.packetHeaderBuf[2] & 0xff) << 16); + + if (this.traceProtocol) { + StringBuffer traceMessageBuf = new StringBuffer(); + + traceMessageBuf.append(Messages.getString("MysqlIO.2")); //$NON-NLS-1$ + traceMessageBuf.append(packetLength); + traceMessageBuf.append(Messages.getString("MysqlIO.3")); //$NON-NLS-1$ + traceMessageBuf.append(StringUtils.dumpAsHex( + this.packetHeaderBuf, 4)); + + this.connection.getLog().logTrace(traceMessageBuf.toString()); + } + + byte multiPacketSeq = this.packetHeaderBuf[3]; + + if (!this.packetSequenceReset) { + if (this.enablePacketDebug && this.checkPacketSequence) { + checkPacketSequencing(multiPacketSeq); + } + } else { + this.packetSequenceReset = false; + } + + this.readPacketSequence = multiPacketSeq; + + skipFully(this.mysqlInput, packetLength); + } catch (IOException ioEx) { + throw new CommunicationsException(this.connection, + this.lastPacketSentTimeMs, ioEx); + } catch (OutOfMemoryError oom) { + try { + this.connection.realClose(false, false, true, oom); + } finally { + throw oom; + } + } + } + + /** + * Read one packet from the MySQL server + * + * @return the packet from the server. + * + * @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 && versionMeetsMinimum(5, 0, 0)) { + // Just to be safe, check if a transaction is in progress on the server.... + // if so, then we must be in autoCommit == false + // therefore return the opposite of transaction status + boolean inTransactionOnServer = ((this.serverStatus & + SERVER_STATUS_IN_TRANS) != 0); + + return !inTransactionOnServer; + } + + return autoCommitModeOnServer != autoCommitFlag; + } + + return true; + } + + protected boolean inTransactionOnServer() { + return (this.serverStatus & SERVER_STATUS_IN_TRANS) != 0; + } + + /** + * 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 = (userName != null) ? userName.length() : 0; + int databaseLength = (database != null) ? database.length() : 0; + + int packLength = ((userLength + passwordLength + databaseLength) * 2) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD; + + 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 = (user != null) ? user.length() : 0; + int databaseLength = (database != null) ? database.length() : 0; + + int packLength = ((userLength + passwordLength + databaseLength) * 2) + 7 + HEADER_LENGTH + AUTH_411_OVERHEAD; + + 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(INITIAL_PACKET_SIZE); + } + + 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, Field[] metadataFromCache) + throws SQLException { + resultPacket.setPosition(resultPacket.getPosition() - 1); + + ResultSet topLevelResultSet = readResultsForQueryOrUpdate(callingStatement, + maxRows, resultSetType, resultSetConcurrency, streamResults, + catalog, resultPacket, isBinaryEncoded, preSentColumnCount, + unpackFieldInfo, metadataFromCache); + + 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); + + ResultSet newResultSet = readResultsForQueryOrUpdate(callingStatement, + maxRows, resultSetType, resultSetConcurrency, + streamResults, catalog, fieldPacket, isBinaryEncoded, + preSentColumnCount, unpackFieldInfo, metadataFromCache); + + 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 = getCurrentTimeNanosOrMillis(); + } + + // 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 = getCurrentTimeNanosOrMillis(); + + boolean shouldExtractQuery = false; + + if (this.profileSql) { + shouldExtractQuery = true; + } else if (this.logSlowQueries) { + + if ((queryEndTime - queryStartTime) > this.slowQueryThreshold) { + 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, null /* we don't need metadata for cached MD in this case */); + + if (queryWasSlow) { + StringBuffer mesgBuf = new StringBuffer(48 + + profileQueryToLog.length()); + + mesgBuf.append(Messages.getString("MysqlIO.SlowQuery", + new Object[] {new Long(this.slowQueryThreshold), + queryTimingUnits, + new Long(queryEndTime - queryStartTime)})); + mesgBuf.append(profileQueryToLog); + + ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection); + + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_SLOW_QUERY, + "", catalog, this.connection.getId(), //$NON-NLS-1$ + (callingStatement != null) ? callingStatement.getId() : 999, + rs.resultId, System.currentTimeMillis(), + (int) (queryEndTime - queryStartTime), queryTimingUnits, null, + new Throwable(), mesgBuf.toString())); + + if (this.connection.getExplainSlowQueries()) { + if (oldPacketPosition < MAX_QUERY_SIZE_TO_EXPLAIN) { + explainSlowQuery(queryPacket.getBytes(5, + (oldPacketPosition - 5)), profileQueryToLog); + } else { + this.connection.getLog().logWarn(Messages.getString( + "MysqlIO.28") //$NON-NLS-1$ + +MAX_QUERY_SIZE_TO_EXPLAIN + + Messages.getString("MysqlIO.29")); //$NON-NLS-1$ + } + } + } + + if (this.profileSql) { + fetchEndTime = getCurrentTimeNanosOrMillis(); + + ProfileEventSink eventSink = ProfileEventSink.getInstance(this.connection); + + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_QUERY, + "", catalog, this.connection.getId(), //$NON-NLS-1$ + (callingStatement != null) ? callingStatement.getId() : 999, + rs.resultId, System.currentTimeMillis(), + (queryEndTime - queryStartTime), this.queryTimingUnits, + null, + new Throwable(), profileQueryToLog)); + + eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_FETCH, + "", catalog, this.connection.getId(), //$NON-NLS-1$ + (callingStatement != null) ? callingStatement.getId() : 999, + rs.resultId, System.currentTimeMillis(), + (fetchEndTime - fetchBeginTime), this.queryTimingUnits, + null, + new Throwable(), null)); + + if (this.queryBadIndexUsed) { + eventSink.consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_SLOW_QUERY, "", catalog, //$NON-NLS-1$ + this.connection.getId(), + (callingStatement != null) ? callingStatement.getId() + : 999, rs.resultId, + System.currentTimeMillis(), + (queryEndTime - queryStartTime), this.queryTimingUnits, + null, + new Throwable(), + Messages.getString("MysqlIO.33") //$NON-NLS-1$ + +profileQueryToLog)); + } + + if (this.queryNoIndexUsed) { + eventSink.consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_SLOW_QUERY, "", catalog, //$NON-NLS-1$ + this.connection.getId(), + (callingStatement != null) ? callingStatement.getId() + : 999, rs.resultId, + System.currentTimeMillis(), + (queryEndTime - queryStartTime), this.queryTimingUnits, + null, + new Throwable(), + Messages.getString("MysqlIO.35") //$NON-NLS-1$ + +profileQueryToLog)); + } + } + + if (this.hadWarnings) { + scanForAndThrowDataTruncation(); + } + + return rs; + } + + private void calculateSlowQueryThreshold() { + this.slowQueryThreshold = this.connection.getSlowQueryThresholdMillis(); + + if (this.connection.getUseNanosForElapsedTime()) { + long nanosThreshold = this.connection.getSlowQueryThresholdNanos(); + + if (nanosThreshold != 0) { + this.slowQueryThreshold = nanosThreshold; + } else { + this.slowQueryThreshold *= 1000000; // 1 million millis in a nano + } + } + } + + protected long getCurrentTimeNanosOrMillis() { + if (this.useNanosForElapsedTime) { + return Util.getCurrentTimeNanosOrMillis(); + } + + return System.currentTimeMillis(); + } + + /** + * Returns the host this IO is connected to + * + * @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(Messages.getString("MysqlIO.EOF", + new Object[] {new Integer(len), new Integer(n)})); + } + + n += count; + } + + return n; + } + + private final long skipFully(InputStream in, long len) throws IOException { + if (len < 0) { + throw new IOException("Negative skip length not allowed"); + } + + long n = 0; + + while (n < len) { + long count = in.skip(len - n); + + if (count < 0) { + throw new EOFException(Messages.getString("MysqlIO.EOF", + new Object[] {new Long(len), new Long(n)})); + } + + n += count; + } + + return n; + } + + /** + * Reads one result set off of the wire, if the result is actually an + * update count, creates an update-count only result set. + * + * @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, Field[] metadataFromCache) 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, + metadataFromCache); + + 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(INITIAL_PACKET_SIZE); + } + } + + /** + * 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, 0, 4); + + if (lengthRead < 4) { + forceClose(); + throw new IOException(Messages.getString("MysqlIO.47")); //$NON-NLS-1$ + } + + packetLength = (this.packetHeaderBuf[0] & 0xff) + + ((this.packetHeaderBuf[1] & 0xff) << 8) + + ((this.packetHeaderBuf[2] & 0xff) << 16); + + Buffer multiPacket = new Buffer(packetLength); + boolean firstMultiPkt = true; + + while (true) { + if (!firstMultiPkt) { + lengthRead = readFully(this.mysqlInput, + this.packetHeaderBuf, 0, 4); + + if (lengthRead < 4) { + forceClose(); + throw new IOException(Messages.getString( + "MysqlIO.48")); //$NON-NLS-1$ + } + + packetLength = (this.packetHeaderBuf[0] & 0xff) + + ((this.packetHeaderBuf[1] & 0xff) << 8) + + ((this.packetHeaderBuf[2] & 0xff) << 16); + } else { + firstMultiPkt = false; + } + + if (!this.useNewLargePackets && (packetLength == 1)) { + clearInputStream(); + + break; + } else if (packetLength < this.maxThreeBytes) { + byte newPacketSeq = this.packetHeaderBuf[3]; + + if (newPacketSeq != (multiPacketSeq + 1)) { + throw new IOException(Messages.getString( + "MysqlIO.49")); //$NON-NLS-1$ + } + + multiPacketSeq = newPacketSeq; + + // Set the Buffer to it's original state + multiPacket.setPosition(0); + + // Set the new length + multiPacket.setBufLength(packetLength); + + // Read the data from the server + byte[] byteBuf = multiPacket.getByteBuffer(); + int lengthToWrite = packetLength; + + int bytesRead = readFully(this.mysqlInput, byteBuf, + 0, packetLength); + + if (bytesRead != lengthToWrite) { + throw 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.getAllowLoadLocalInfile()) { + throw SQLError.createSQLException( + Messages.getString("MysqlIO.LoadDataLocalNotAllowed"), + SQLError.SQL_STATE_GENERAL_ERROR); + } + + 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$ + } + } + + appendInnodbStatusInformation(xOpen, errorBuf); + + 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; + } + + private void appendInnodbStatusInformation(String xOpen, + StringBuffer errorBuf) throws SQLException { + if (this.connection.getIncludeInnodbStatusInDeadlockExceptions() + && xOpen != null + && (xOpen.startsWith("40") || xOpen.startsWith("41")) + && this.streamingData == null) { + ResultSet rs = null; + + try { + rs = sqlQueryDirect(null, "SHOW ENGINE INNODB STATUS", + this.connection.getEncoding(), null, -1, + this.connection, ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY, false, this.connection + .getCatalog(), true); + if (rs.next()) { + errorBuf.append("\n\n"); + errorBuf.append(rs.getString(1)); + } else { + errorBuf.append(Messages + .getString("MysqlIO.NoInnoDBStatusFound")); + } + } catch (Exception ex) { + errorBuf.append(Messages + .getString("MysqlIO.InnoDBStatusFailed")); + errorBuf.append("\n\n"); + errorBuf.append(Util.stackTraceToString(ex)); + } finally { + if (rs != null) { + rs.close(); + } + } + } + } + + /** + * Sends a large packet to the server as a series of smaller packets + * + * @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(INITIAL_PACKET_SIZE); + } + } + + boolean hadWarnings() { + return this.hadWarnings; + } + + void scanForAndThrowDataTruncation() throws SQLException { + if ((this.streamingData == null) && versionMeetsMinimum(4, 1, 0) && + this.connection.getJdbcCompliantTruncation() && this.warningCount > 0) { + 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.use41Extensions) { + 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.use41Extensions) { + 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 this.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 this.threadId; + } + + protected boolean useNanosForElapsedTime() { + return this.useNanosForElapsedTime; + } + + protected long getSlowQueryThreshold() { + return this.slowQueryThreshold; + } + + protected String getQueryTimingUnits() { + return this.queryTimingUnits; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlParameterMetadata.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlParameterMetadata.java new file mode 100644 index 00000000..54b7a8d3 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlParameterMetadata.java @@ -0,0 +1,162 @@ +/* + 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; +import java.sql.Types; + +public class MysqlParameterMetadata implements ParameterMetaData { + boolean returnSimpleMetadata = false; + ResultSetMetaData metadata = null; + int parameterCount = 0; + + + MysqlParameterMetadata(Field[] fieldInfo, int parameterCount) { + this.metadata = new ResultSetMetaData(fieldInfo, false); + + this.parameterCount = parameterCount; + } + + /** + * Used for "fake" basic metadata for client-side prepared statements + * when we don't know the parameter types. + * + * @param parameterCount + */ + MysqlParameterMetadata(int count) { + this.parameterCount = count; + this.returnSimpleMetadata = true; + } + + public int getParameterCount() throws SQLException { + return this.parameterCount; + } + + public int isNullable(int arg0) throws SQLException { + checkAvailable(); + + return this.metadata.isNullable(arg0); + } + + private void checkAvailable() throws SQLException { + if (this.metadata == null || this.metadata.fields == 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 { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return false; + } + + checkAvailable(); + + return (this.metadata.isSigned(arg0)); + } + + public int getPrecision(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return 0; + } + + checkAvailable(); + + return (this.metadata.getPrecision(arg0)); + } + + public int getScale(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return 0; + } + + checkAvailable(); + + return (this.metadata.getScale(arg0)); + } + + public int getParameterType(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return Types.VARCHAR; + } + + checkAvailable(); + + return (this.metadata.getColumnType(arg0)); + } + + public String getParameterTypeName(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return "VARCHAR"; + } + + checkAvailable(); + + return (this.metadata.getColumnTypeName(arg0)); + } + + public String getParameterClassName(int arg0) throws SQLException { + if (this.returnSimpleMetadata) { + checkBounds(arg0); + + return "java.lang.String"; + } + + checkAvailable(); + + return (this.metadata.getColumnClassName(arg0)); + } + + public int getParameterMode(int arg0) throws SQLException { + return parameterModeIn; + } + + private void checkBounds(int paramNumber) throws SQLException { + if (paramNumber < 1) { + throw SQLError.createSQLException("Parameter index of '" + paramNumber + + "' is invalid.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + if (paramNumber > this.parameterCount) { + throw SQLError.createSQLException("Parameter index of '" + paramNumber + + "' is greater than number of parameters, which is '" + + this.parameterCount + "'.", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + + } + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlSavepoint.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlSavepoint.java new file mode 100644 index 00000000..1a8f7773 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/MysqlSavepoint.java @@ -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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NamedPipeSocketFactory.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NamedPipeSocketFactory.java new file mode 100644 index 00000000..993d32dc --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NamedPipeSocketFactory.java @@ -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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NonRegisteringDriver.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NonRegisteringDriver.java new file mode 100644 index 00000000..1a7846f1 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NonRegisteringDriver.java @@ -0,0 +1,780 @@ +/* + Copyright (C) 2002-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ +package com.mysql.jdbc; + +import java.io.IOException; +import java.io.InputStream; +import java.io.UnsupportedEncodingException; +import java.net.URLDecoder; +import java.sql.Connection; +import java.sql.DriverPropertyInfo; +import java.sql.SQLException; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Properties; +import java.util.StringTokenizer; + +/** + * The Java SQL framework allows for multiple database drivers. Each driver + * should supply a class that implements the Driver interface + * + *
+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * 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") + *
+ * + * @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 { + private static final String REPLICATION_URL_PREFIX = "jdbc:mysql:replication://"; + + private static final String URL_PREFIX = "jdbc:mysql://"; + + private static final String MXJ_URL_PREFIX = "jdbc:mysql:mxj://"; + + private static final String LOADBALANCE_URL_PREFIX = "jdbc:mysql:loadbalance://"; + + /** + * Key used to retreive the database value from the properties instance + * passed to the driver. + */ + 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. + * + *+ * 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. + *
+ * + *+ * The java.util.Properties argument can be used to pass arbitrary string + * tag/value pairs as connection arguments. + *
+ * + *+ * My protocol takes the form: + * + *
+ * + * jdbc:mysql://host:port/database + * + *+ * + * + * + * @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 { + if (url != null) { + if (StringUtils.startsWithIgnoreCase(url, LOADBALANCE_URL_PREFIX)) { + return connectLoadBalanced(url, info); + } else if (StringUtils.startsWithIgnoreCase(url, + REPLICATION_URL_PREFIX)) { + return connectReplicationConnection(url, info); + } + } + + Properties props = null; + + if ((props = parseURL(url, info)) == null) { + 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); + } + } + + private java.sql.Connection connectLoadBalanced(String url, Properties info) + throws SQLException { + Properties parsedProps = parseURL(url, info); + + if (parsedProps == null) { + return null; + } + + String hostValues = parsedProps.getProperty(HOST_PROPERTY_KEY); + + List hostList = null; + + if (hostValues != null) { + hostList = StringUtils.split(hostValues, ",", true); + } + + if (hostList == null) { + hostList = new ArrayList(); + hostList.add("localhost:3306"); + } + + LoadBalancingConnectionProxy proxyBal = new LoadBalancingConnectionProxy( + hostList, parsedProps); + + return (java.sql.Connection) java.lang.reflect.Proxy.newProxyInstance(this + .getClass().getClassLoader(), + new Class[] { java.sql.Connection.class }, proxyBal); + } + + private java.sql.Connection connectReplicationConnection(String url, Properties info) + throws SQLException { + Properties parsedProps = parseURL(url, info); + + if (parsedProps == null) { + return null; + } + + Properties masterProps = (Properties) parsedProps.clone(); + Properties slavesProps = (Properties) parsedProps.clone(); + + // Marker used for further testing later on, also when + // debugging + slavesProps.setProperty("com.mysql.jdbc.ReplicationConnection.isSlave", + "true"); + + String hostValues = parsedProps.getProperty(HOST_PROPERTY_KEY); + + if (hostValues != null) { + StringTokenizer st = new StringTokenizer(hostValues, ","); + + StringBuffer masterHost = new StringBuffer(); + StringBuffer slaveHosts = new StringBuffer(); + + if (st.hasMoreTokens()) { + String[] hostPortPair = parseHostPortPair(st.nextToken()); + + if (hostPortPair[HOST_NAME_INDEX] != null) { + masterHost.append(hostPortPair[HOST_NAME_INDEX]); + } + + if (hostPortPair[PORT_NUMBER_INDEX] != null) { + masterHost.append(":"); + masterHost.append(hostPortPair[PORT_NUMBER_INDEX]); + } + } + + boolean firstSlaveHost = true; + + while (st.hasMoreTokens()) { + String[] hostPortPair = parseHostPortPair(st.nextToken()); + + if (!firstSlaveHost) { + slaveHosts.append(","); + } else { + firstSlaveHost = false; + } + + if (hostPortPair[HOST_NAME_INDEX] != null) { + slaveHosts.append(hostPortPair[HOST_NAME_INDEX]); + } + + if (hostPortPair[PORT_NUMBER_INDEX] != null) { + slaveHosts.append(":"); + slaveHosts.append(hostPortPair[PORT_NUMBER_INDEX]); + } + } + + if (slaveHosts.length() == 0) { + throw SQLError + .createSQLException( + "Must specify at least one slave host to connect to for master/slave replication load-balancing functionality", + SQLError.SQL_STATE_INVALID_CONNECTION_ATTRIBUTE); + } + + masterProps.setProperty(HOST_PROPERTY_KEY, masterHost.toString()); + slavesProps.setProperty(HOST_PROPERTY_KEY, slaveHosts.toString()); + } + + return new ReplicationConnection(masterProps, slavesProps); + } + + /** + * Returns the database property from
props
+ *
+ * @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();
+ }
+
+ //
+ // return the value of any property this driver knows about
+ //
+
+ /**
+ * 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.
+ *
+ * + * 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 + *
+ * + * @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(URL_PREFIX)) { //$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; + } + + /** + * 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. + * + *+ * MySQL is not SQL92 compliant + *
+ * + * @return is this driver JDBC compliant? + */ + public boolean jdbcCompliant() { + return false; + } + + 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, URL_PREFIX) + && !StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX) + && !StringUtils.startsWithIgnoreCase(url, + LOADBALANCE_URL_PREFIX) + && !StringUtils.startsWithIgnoreCase(url, + REPLICATION_URL_PREFIX)) { //$NON-NLS-1$ + + return null; + } + + int beginningOfSlashes = url.indexOf("//"); + + if (StringUtils.startsWithIgnoreCase(url, MXJ_URL_PREFIX)) { + 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 + 2); + + 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 { + hostStuff = url; + } + + 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 (Util.isColdFusion() && + urlProps.getProperty("autoConfigureForColdFusion", "true").equalsIgnoreCase("true")) { + String configs = urlProps.getProperty(USE_CONFIG_PROPERTY_KEY); + + StringBuffer newConfigs = new StringBuffer(); + + if (configs != null) { + newConfigs.append(configs); + newConfigs.append(","); + } + + newConfigs.append("coldFusion"); + + urlProps.setProperty(USE_CONFIG_PROPERTY_KEY, newConfigs.toString()); + } + + // If we use a config, it actually should get overridden by anything in + // the URL or passed-in properties + + 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 fromprops
+ *
+ * @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);
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java
new file mode 100644
index 00000000..7e7ee8f0
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NonRegisteringReplicationDriver.java
@@ -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);
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NotImplemented.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NotImplemented.java
new file mode 100644
index 00000000..1389053a
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NotImplemented.java
@@ -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$
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NotUpdatable.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NotUpdatable.java
new file mode 100644
index 00000000..6c656cd7
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/NotUpdatable.java
@@ -0,0 +1,72 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+/**
+ * Thrown when a result sate is not updatable
+ *
+ * @author Mark Matthews
+ */
+public class NotUpdatable extends SQLException {
+
+ private static final long serialVersionUID = 8084742846039782258L;
+
+ /**
+ * 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$
+
+ /**
+ * Creates a new NotUpdatable exception.
+ */
+ public NotUpdatable() {
+ this(NOT_UPDATEABLE_MESSAGE);
+ }
+
+ /**
+ * Append the given reason to the not updatable message if the reason is not
+ * null.
+ */
+ public NotUpdatable(String reason) {
+ super(reason
+ + Messages.getString("NotUpdatable.1")
+ + Messages.getString("NotUpdatable.2")
+ + Messages.getString("NotUpdatable.3")
+ + Messages.getString("NotUpdatable.4")
+ + Messages.getString("NotUpdatable.5"),
+ SQLError.SQL_STATE_GENERAL_ERROR);
+ }
+}
\ No newline at end of file
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/OperationNotSupportedException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/OperationNotSupportedException.java
new file mode 100644
index 00000000..e8f0d9fa
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/OperationNotSupportedException.java
@@ -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
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/OutputStreamWatcher.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/OutputStreamWatcher.java
new file mode 100644
index 00000000..271e9b3c
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/OutputStreamWatcher.java
@@ -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);
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PacketTooBigException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PacketTooBigException.java
new file mode 100644
index 00000000..18b8f37f
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PacketTooBigException.java
@@ -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$
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PingTarget.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PingTarget.java
new file mode 100644
index 00000000..ad255153
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PingTarget.java
@@ -0,0 +1,29 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+package com.mysql.jdbc;
+
+import java.sql.SQLException;
+
+public interface PingTarget {
+ public void doPing() throws SQLException;
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PreparedStatement.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PreparedStatement.java
new file mode 100644
index 00000000..1c3a611b
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/PreparedStatement.java
@@ -0,0 +1,4086 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.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.
+ *
+ * + * Note: 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. + *
+ * + *+ * If arbitrary parameter type conversions are required, then the setObject + * method should be used with a target SQL type. + *
+ * + * @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; + + int statementStartPos = 0; + + byte[][] staticSql = null; + + /** + * Represents the "parsed" state of a client-side + * prepared statement, with the statement broken up into + * it's static and dynamic (where parameters are bound) + * parts. + */ + public ParseInfo(String sql, Connection conn, + 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(); + + // we're not trying to be real pedantic here, but we'd like to + // skip comments at the beginning of statements, as frameworks + // such as Hibernate use them to aid in debugging + + statementStartPos = findStartOfStatement(sql); + + for (i = statementStartPos; i < this.statementLength; ++i) { + char c = sql.charAt(i); + + if ((this.firstStmtChar == 0) && !Character.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 == '-' && (i + 1) < this.statementLength && sql + .charAt(i + 1) == '-')) { + // run out to end of statement, or newline, + // whichever comes first + int endOfStmt = this.statementLength - 1; + + for (; i < endOfStmt; i++) { + c = sql.charAt(i); + + if (c == '\r' || c == '\n') { + break; + } + } + + continue; + } else if (c == '/' && (i + 1) < this.statementLength) { + // Comment? + char cNext = sql.charAt(i + 1); + + if (cNext == '*') { + i+= 2; + + for (int j = i; j < this.statementLength; j++) { + i++; + cNext = sql.charAt(j); + + if (cNext == '*' && (j + 1) < this.statementLength) { + if (sql.charAt(j + 1) == '/') { + i++; + + if (i < this.statementLength) { + c = sql.charAt(i); + } + + break; // comment done + } + } + } + } + } else if ((c == '\'') || (c == '"')) { + inQuotes = true; + quoteChar = c; + } + } + } + + 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; + + /** Where does the statement text actually start? */ + + private int statementAfterCommentsPos; + + /** + * have we checked whether we can rewrite this statement as a multi-value + * insert? + */ + + private boolean hasCheckedForRewrite = false; + + /** Can we actually rewrite this statement as a multi-value insert? */ + + private boolean canRewrite = false; + + private boolean doPingInstead; + + /** + * 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; + + if (this.originalSql.startsWith(PING_MARKER)) { + this.doPingInstead = true; + } else { + this.doPingInstead = false; + } + + this.dbmd = this.connection.getMetaData(); + + this.useTrueBoolean = this.connection.versionMeetsMinimum(3, 21, 23); + + 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 { + checkClosed(); + + Connection locallyScopedConn = this.connection; + + if (locallyScopedConn.isReadOnly() && (this.firstCharOfStmt != 'S')) { + throw SQLError.createSQLException(Messages.getString("PreparedStatement.20") //$NON-NLS-1$ + + Messages.getString("PreparedStatement.21"), //$NON-NLS-1$ + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + ResultSet rs = null; + + CachedResultSetMetaData cachedMetadata = null; + + synchronized (locallyScopedConn.getMutex()) { + clearWarnings(); + + this.batchedGeneratedKeys = null; + + Buffer sendPacket = fillSendPacket(); + + 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 = locallyScopedConn.getCachedMetaData(this.originalSql); + } + + Field[] metadataFromCache = null; + + if (cachedMetadata != null) { + metadataFromCache = cachedMetadata.fields; + } + + boolean oldInfoMsgState = false; + + if (this.retrieveGeneratedKeys) { + oldInfoMsgState = locallyScopedConn.isReadInfoMsgEnabled(); + locallyScopedConn.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 (locallyScopedConn.useMaxRows()) { + int rowLimit = -1; + + if (this.firstCharOfStmt == 'S') { + if (this.hasLimitClause) { + 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); + } 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); + } + } + } 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); + } + + // Finally, execute the query + rs = executeInternal(rowLimit, sendPacket, + createStreamingResultSet(), + (this.firstCharOfStmt == 'S'), true, metadataFromCache, false); + } else { + rs = executeInternal(-1, sendPacket, + createStreamingResultSet(), + (this.firstCharOfStmt == 'S'), true, metadataFromCache, false); + } + + if (cachedMetadata != null) { + locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, + cachedMetadata, this.results); + } else { + if (rs.reallyResult() && locallyScopedConn.getCacheResultSetMetadata()) { + locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, + null /* will be created */, rs); + } + } + + if (this.retrieveGeneratedKeys) { + locallyScopedConn.setReadInfoMsgEnabled(oldInfoMsgState); + rs.setFirstCharOfQuery(this.firstCharOfStmt); + } + + if (oldCatalog != null) { + locallyScopedConn.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()) { + if (this.batchedArgs == null || this.batchedArgs.size() == 0) { + return new int[0]; + } + + try { + clearWarnings(); + + if (!this.batchHasPlainStatements + && this.connection.getRewriteBatchedStatements()) { + + if (canRewriteAsMultivalueInsertStatement()) { + return executeBatchedInserts(); + } + } + + return executeBatchSerially(); + } catch (NullPointerException npe) { + // We do this, otherwise we need to totally overhaul how executeBatch() reuses + // existing execute() functionality, and pass around a locally-scoped connection, + // or catch the very rare race condition and handle it here. + + checkClosed(); // if we're really closed, this will throw a SQLException + + throw npe; // otherwise someone (me!) goofed error + } finally { + clearBatch(); + } + } + } + + public synchronized boolean canRewriteAsMultivalueInsertStatement() { + if (!this.hasCheckedForRewrite) { + // Needs to be INSERT, can't have INSERT ... SELECT or + // INSERT ... ON DUPLICATE KEY UPDATE + // + // We're not smart enough to re-write to + // + // INSERT INTO table (a,b,c) VALUES (1,2,3),(4,5,6) + // ON DUPLICATE KEY UPDATE c=VALUES(a)+VALUES(b); + // + // (yet) + + this.canRewrite = StringUtils.startsWithIgnoreCaseAndWs( + this.originalSql, "INSERT", this.statementAfterCommentsPos) + && StringUtils.indexOfIgnoreCaseRespectMarker(this.statementAfterCommentsPos, this.originalSql, "SELECT", "\"'`", "\"'`", false) == -1 + && StringUtils.indexOfIgnoreCaseRespectMarker(this.statementAfterCommentsPos, this.originalSql, "UPDATE", "\"'`", "\"'`", false) == -1; + + this.hasCheckedForRewrite = true; + } + + return this.canRewrite; + } + + /** + * Rewrites the already prepared statement into a multi-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 + */ + protected 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; + + int batchedParamIndex = 1; + int updateCountRunningTotal = 0; + int numberToExecuteAsMultiValue = 0; + int batchCounter = 0; + + try { + if (this.retrieveGeneratedKeys) { + batchedStatement = locallyScopedConn.prepareStatement( + generateBatchedInsertSQL(valuesClause, numValuesPerBatch), + RETURN_GENERATED_KEYS); + } else { + batchedStatement = locallyScopedConn + .prepareStatement(generateBatchedInsertSQL(valuesClause, + numValuesPerBatch)); + } + + 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; + + } + + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, + batchedParamIndex, this.batchedArgs + .get(batchCounter++)); + } + + updateCountRunningTotal += batchedStatement.executeUpdate(); + getBatchedGeneratedKeys(batchedStatement); + + numValuesPerBatch = numBatchedArgs - batchCounter; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + } + } + + try { + if (numValuesPerBatch > 0) { + + if (this.retrieveGeneratedKeys) { + batchedStatement = locallyScopedConn.prepareStatement( + generateBatchedInsertSQL(valuesClause, numValuesPerBatch), + RETURN_GENERATED_KEYS); + } else { + batchedStatement = locallyScopedConn.prepareStatement( + generateBatchedInsertSQL(valuesClause, numValuesPerBatch)); + } + + batchedParamIndex = 1; + + while (batchCounter < numBatchedArgs) { + batchedParamIndex = setOneBatchedParameterSet(batchedStatement, + batchedParamIndex, this.batchedArgs + .get(batchCounter++)); + } + + 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; + } finally { + if (batchedStatement != null) { + batchedStatement.close(); + } + } + } + + /** + * Computes the optimum number of batched parameter lists to send + * without overflowing max_allowed_packet. + * + * @param numBatchedArgs + * @return + */ + protected int computeBatchSize(int numBatchedArgs) { + long[] combinedValues = computeMaxParameterSetSizeAndBatchSize(numBatchedArgs); + + long maxSizeOfParameterSet = combinedValues[0]; + long sizeOfEntireBatch = combinedValues[1]; + + int maxAllowedPacket = this.connection.getMaxAllowedPacket(); + + if (sizeOfEntireBatch < maxAllowedPacket - this.originalSql.length()) { + return numBatchedArgs; + } + + return (int)Math.max(1, (maxAllowedPacket - this.originalSql.length()) / maxSizeOfParameterSet); + } + + /** + * Computes the maximum parameter set size, and entire batch size given + * the number of arguments in the batch. + */ + protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) { + long sizeOfEntireBatch = 0; + long maxSizeOfParameterSet = 0; + + 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 { + sizeOfParameterSet += paramArg.parameterStrings[j].length; + } + } 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; + } + } + + return new long[] {maxSizeOfParameterSet, sizeOfEntireBatch}; + } + + /** + * 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, Field[] cachedFields, + boolean isBatch) + throws SQLException { + try { + + + synchronized (this.cancelTimeoutMutex) { + this.wasCancelled = false; + } + + Connection locallyScopedConnection= this.connection; + + this.numberOfExecutions++; + + if (this.doPingInstead) { + doPingInstead(); + + return this.results; + } + + ResultSet rs; + + CancelTask timeoutTask = null; + + try { + if (locallyScopedConnection.getEnableQueryTimeouts() && + 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(); + + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + this.wasCancelled = false; + throw new MySQLTimeoutException(); + } + } + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + } + } + + return rs; + } catch (NullPointerException npe) { + checkClosed(); // we can't synchronize ourselves against async connection-close + // due to deadlock issues, so this is the next best thing for + // this particular corner case. + + throw npe; + } + } + + /** + * 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 = locallyScopedConn.getCachedMetaData(this.originalSql); + } + + Field[] metadataFromCache = null; + + if (cachedMetadata != null) { + metadataFromCache = cachedMetadata.fields; + } + + if (locallyScopedConn.useMaxRows()) { + // If there isn't a limit clause in the SQL + // then limit the number of rows to return in + // 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), metadataFromCache, false); + } else { + if (this.maxRows <= 0) { + locallyScopedConn + .execSQL( + this, + "SET OPTION SQL_SELECT_LIMIT=DEFAULT", -1, null, //$NON-NLS-1$ + java.sql.ResultSet.TYPE_FORWARD_ONLY, + java.sql.ResultSet.CONCUR_READ_ONLY, + false, this.currentCatalog, true); + } 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), metadataFromCache, false); + + if (oldCatalog != null) { + this.connection.setCatalog(oldCatalog); + } + } + } else { + this.results = executeInternal(-1, sendPacket, + createStreamingResultSet(), true, + (cachedMetadata == null), metadataFromCache, false); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + + if (cachedMetadata != null) { + locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, + cachedMetadata, this.results); + } else { + if (locallyScopedConn.getCacheResultSetMetadata()) { + locallyScopedConn.initializeResultsMetadataFromCache(this.originalSql, + null /* will be created */, this.results); + } + } + } + + this.lastInsertId = this.results.getUpdateID(); + + 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') + && isSelectQuery()) { //$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, null, + 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( + this.statementAfterCommentsPos, + this.originalSql, "VALUES ", quoteCharStr.charAt(0), false); + } else { + indexOfValues = StringUtils.indexOfIgnoreCase(this.statementAfterCommentsPos, + 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 { + + // + // We could just tack on a LIMIT 0 here no matter what the + // statement, and check if a result set was returned or not, + // but I'm not comfortable with that, myself, so we take + // the "safer" road, and only allow metadata for _actual_ + // SELECTS (but not SHOWs). + // + // CALL's are trapped further up and you end up with a + // CallableStatement anyway. + // + + if (!isSelectQuery()) { + return null; + } + + 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; + } + + protected boolean isSelectQuery() { + return StringUtils.startsWithIgnoreCaseAndWs( + StringUtils.stripComments(this.originalSql, + "'\"", "'\"", true, false, true, true), + "SELECT"); + } + + /** + * @see PreparedStatement#getParameterMetaData() + */ + public ParameterMetaData getParameterMetaData() + throws SQLException { + if (this.parameterMetaData == null) { + if (this.connection.getGenerateSimpleParameterMetadata()) { + this.parameterMetaData = new MysqlParameterMetadata(this.parameterCount); + } else { + 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; + } + + this.statementAfterCommentsPos = this.parseInfo.statementStartPos; + } + + 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, Constants.MILLIS_I18N, + 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. + * + *+ * Note: This stream object can either be a standard Java stream + * object or your own subclass that implements the standard interface. + *
+ * + * @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. + * + *+ * Note: This stream object can either be a standard Java stream + * object or your own subclass that implements the standard interface. + *
+ * + * @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. + * + *+ * Note: This stream object can either be a standard Java stream + * object or your own subclass that implements the standard interface. + *
+ * + * @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 + * + *+ * Note: You must specify the parameters SQL type (although MySQL + * ignores it) + *
+ * + * @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. + * + *+ * Note: You must specify the parameter's SQL type. + *
+ * + * @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 (this.connection.getTreatUtilDateAsTimestamp() && + 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. + * + *+ * The given Java object will be converted to the targetSqlType before being + * sent to the database. + *
+ * + *+ * 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 + *
+ * + * @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); + } + } + } + + protected int setOneBatchedParameterSet( + java.sql.PreparedStatement batchedStatement, int batchedParamIndex, + Object paramSet) throws SQLException { + BatchParams paramArg = (BatchParams)paramSet; + + 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(<structured-type>) 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, this.connection.getDefaultTimeZone(), 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, this.connection.getDefaultTimeZone(), 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.connection.getUseSSPSCompatibleTimezoneShift()) { + doSSPSCompatibleTimezoneShift(parameterIndex, x, sessionCalendar); + } else { + + if (this.tsdf == null) { + this.tsdf = new SimpleDateFormat("''yyyy-MM-dd HH:mm:ss''", Locale.US); //$NON-NLS-1$ + } + + timestampString = this.tsdf.format(x); + + setInternal(parameterIndex, timestampString); // SimpleDateFormat is not + // thread-safe + } + } + } + + private void doSSPSCompatibleTimezoneShift(int parameterIndex, Timestamp x, Calendar sessionCalendar) throws SQLException { + Calendar sessionCalendar2 = (this.connection + .getUseJDBCCompliantTimezoneShift()) ? this.connection + .getUtcCalendar() + : getCalendarInstanceForSessionOrNew(); + + synchronized (sessionCalendar2) { + java.util.Date oldTime = sessionCalendar2.getTime(); + + try { + sessionCalendar2.setTime(x); + + int year = sessionCalendar2.get(Calendar.YEAR); + int month = sessionCalendar2.get(Calendar.MONTH) + 1; + int date = sessionCalendar2.get(Calendar.DAY_OF_MONTH); + + int hour = sessionCalendar2.get(Calendar.HOUR_OF_DAY); + int minute = sessionCalendar2.get(Calendar.MINUTE); + int seconds = sessionCalendar2.get(Calendar.SECOND); + + StringBuffer tsBuf = new StringBuffer(); + + tsBuf.append('\''); + tsBuf.append(year); + + tsBuf.append("-"); + + if (month < 10) { + tsBuf.append('0'); + } + + tsBuf.append(month); + + tsBuf.append('-'); + + if (date < 10) { + tsBuf.append('0'); + } + + tsBuf.append(date); + + tsBuf.append(' '); + + if (hour < 10) { + tsBuf.append('0'); + } + + tsBuf.append(hour); + + tsBuf.append(':'); + + if (minute < 10) { + tsBuf.append('0'); + } + + tsBuf.append(minute); + + tsBuf.append(':'); + + if (seconds < 10) { + tsBuf.append('0'); + } + + tsBuf.append(seconds); + + tsBuf.append('\''); + + setInternal(parameterIndex, tsBuf.toString()); + + } finally { + sessionCalendar.setTime(oldTime); + } + } + } + + /** + * 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. + * + *+ * Note: This stream object can either be a standard Java stream + * object or your own subclass that implements the standard interface. + *
+ * + * @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 (escape) { + if (this.connection.versionMeetsMinimum(4, 1, 0)) { + bytesOut.write('_'); + bytesOut.write('b'); + bytesOut.write('i'); + bytesOut.write('n'); + bytesOut.write('a'); + bytesOut.write('r'); + bytesOut.write('y'); + } + + bytesOut.write('\''); + } + + 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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ReplicationConnection.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ReplicationConnection.java new file mode 100644 index 00000000..fa9e49d4 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ReplicationConnection.java @@ -0,0 +1,560 @@ +/* + 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, PingTarget { + 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 { + Statement stmt = this.currentConnection.createStatement(); + ((com.mysql.jdbc.Statement) stmt).setPingTarget(this); + + return stmt; + } + + /* + * (non-Javadoc) + * + * @see java.sql.Connection#createStatement(int, int) + */ + public synchronized Statement createStatement(int resultSetType, + int resultSetConcurrency) throws SQLException { + Statement stmt = this.currentConnection.createStatement(resultSetType, + resultSetConcurrency); + + ((com.mysql.jdbc.Statement) stmt).setPingTarget(this); + + return stmt; + } + + /* + * (non-Javadoc) + * + * @see java.sql.Connection#createStatement(int, int, int) + */ + public synchronized Statement createStatement(int resultSetType, + int resultSetConcurrency, int resultSetHoldability) + throws SQLException { + Statement stmt = this.currentConnection.createStatement(resultSetType, + resultSetConcurrency, resultSetHoldability); + + ((com.mysql.jdbc.Statement) stmt).setPingTarget(this); + + return stmt; + } + + /* + * (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 { + PreparedStatement pstmt = this.currentConnection.prepareStatement(sql); + + ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this); + + return pstmt; + } + + /* + * (non-Javadoc) + * + * @see java.sql.Connection#prepareStatement(java.lang.String, int) + */ + public synchronized PreparedStatement prepareStatement(String sql, + int autoGeneratedKeys) throws SQLException { + PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, autoGeneratedKeys); + + ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this); + + return pstmt; + } + + /* + * (non-Javadoc) + * + * @see java.sql.Connection#prepareStatement(java.lang.String, int, int) + */ + public synchronized PreparedStatement prepareStatement(String sql, + int resultSetType, int resultSetConcurrency) throws SQLException { + PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, resultSetType, + resultSetConcurrency); + + ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this); + + return pstmt; + } + + /* + * (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 { + PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, resultSetType, + resultSetConcurrency, resultSetHoldability); + + ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this); + + return pstmt; + } + + /* + * (non-Javadoc) + * + * @see java.sql.Connection#prepareStatement(java.lang.String, int[]) + */ + public synchronized PreparedStatement prepareStatement(String sql, + int[] columnIndexes) throws SQLException { + PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, columnIndexes); + + ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this); + + return pstmt; + } + + /* + * (non-Javadoc) + * + * @see java.sql.Connection#prepareStatement(java.lang.String, + * java.lang.String[]) + */ + public synchronized PreparedStatement prepareStatement(String sql, + String[] columnNames) throws SQLException { + PreparedStatement pstmt = this.currentConnection.prepareStatement(sql, columnNames); + + ((com.mysql.jdbc.Statement) pstmt).setPingTarget(this); + + return pstmt; + } + + /* + * (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; + } + + public synchronized void doPing() throws SQLException { + if (this.masterConnection != null) { + this.masterConnection.ping(); + } + + if (this.slavesConnection != null) { + this.slavesConnection.ping(); + } + } +} \ No newline at end of file diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ReplicationDriver.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ReplicationDriver.java new file mode 100644 index 00000000..e5c4bb24 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ReplicationDriver.java @@ -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 + * + *+ * 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. + * + *
+ * 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. + * + *
+ * 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() + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ResultSet.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ResultSet.java new file mode 100644 index 00000000..35bdcae4 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ResultSet.java @@ -0,0 +1,9090 @@ +/* + Copyright (C) 2002-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ +package com.mysql.jdbc; + +import com.mysql.jdbc.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; +import java.util.TreeMap; + +/** + * A ResultSet provides access to a table of data generated by executing a + * Statement. The table rows are retrieved in sequence. Within a row its column + * values can be accessed in any order. + * + *
+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * For maximum portability, ResultSet columns within each row should be read in + * left-to-right order and each column should be read only once. + *
+ * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * The number, types and properties of a ResultSet's columns are provided by the + * ResultSetMetaData object returned by the getMetaData method. + *
+ * + * @author Mark Matthews + * @version $Id: ResultSet.java 6563 2007-09-06 14:02:01Z 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; + + protected boolean useFastDateParsing = false; + + private boolean padCharsWithSpace = false; + + protected final static char[] EMPTY_SPACE = new char[255]; + + static { + for (int i = 0; i < EMPTY_SPACE.length; i++) { + EMPTY_SPACE[i] = ' '; + } + } + + /** + * 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; + + if (this.connection != null) { + this.useStrictFloatingPoint = this.connection + .getStrictFloatingPoint(); + this.setDefaultTimeZone(this.connection.getDefaultTimeZone()); + this.connectionId = this.connection.getId(); + this.useFastDateParsing = this.connection.getUseFastDateParsing(); + this.padCharsWithSpace = this.connection.getPadCharsWithSpace(); + } + + 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.fields != null) { + initializeWithMetadata(); + } // else called by Connection.initializeResultsMetadataFromCache() when cached + + this.retainOwningStatement = false; + + if (this.connection != null) { + retainOwningStatement = + this.connection.getRetainStatementAfterResultSetClose(); + } + } + + protected void initializeWithMetadata() throws SQLException { + if (this.profileSql || this.connection.getUseUsageAdvisor()) { + this.columnUsed = new boolean[this.fields.length]; + this.pointOfOrigin = new Throwable(); + this.resultId = resultCounter++; + this.useUsageAdvisor = this.connection.getUseUsageAdvisor(); + this.eventSink = ProfileEventSink.getInstance(this.connection); + } + + if (this.connection.getGatherPerformanceMetrics()) { + this.connection.incrementNumberOfResultSetsCreated(); + + Map tableNamesMap = new HashMap(); + + for (int i = 0; i < this.fields.length; i++) { + Field f = this.fields[i]; + + String tableName = f.getOriginalTableName(); + + if (tableName == null) { + tableName = f.getTableName(); + } + + if (tableName != null) { + if (this.connection.lowerCaseTableNames()) { + tableName = tableName.toLowerCase(); // on windows, table + // names are not case-sens. + } + + tableNamesMap.put(tableName, null); + } + } + + this.connection.reportNumberOfTablesAccessed(tableNamesMap.size()); + } + + + } + + private synchronized void createCalendarIfNeeded() { + if (this.fastDateCal == null) { + this.fastDateCal = new GregorianCalendar(Locale.US); + this.fastDateCal.setTimeZone(this.getDefaultTimeZone()); + } + } + + /** + * JDBC 2.0 + * + *+ * Move to an absolute row number in the result set. + *
+ * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * Note: Calling absolute(1) is the same as calling first(). Calling + * absolute(-1) is the same as calling last(). + *
+ * + * @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 + * + *+ * Moves to the end of the result set, just after the last row. Has no + * effect if the result set contains no rows. + *
+ * + * @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 + * + *+ * Moves to the front of the result set, just before the first row. Has no + * effect if the result set contains no rows. + *
+ * + * @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 TreeMap(String.CASE_INSENSITIVE_ORDER); + this.fullColumnNameToIndex = new TreeMap(String.CASE_INSENSITIVE_ORDER); + + + // We do this in reverse order, so that the 'first' column + // with a given name ends up as the final mapping in the + // hashtable... + // + // Quoting the JDBC Spec: + // + // "Column names used as input to getter + // methods are case insensitive. When a getter method is called with a + // column + // name and several columns have the same name, the value of the first + // matching column will be returned. " + // + for (int i = numFields - 1; i >= 0; i--) { + Integer index = new Integer(i); + String columnName = this.fields[i].getName(); + String fullColumnName = this.fields[i].getFullName(); + + if (columnName != null) { + this.columnNameToIndex.put(columnName, index); + } + + if (fullColumnName != null) { + this.fullColumnNameToIndex.put(fullColumnName, index); + } + } + + // set the flag to prevent rebuilding... + this.hasBuiltIndexMapping = true; + } + + /** + * JDBC 2.0 The cancelRowUpdates() method may be called after calling an + * updateXXX() method(s) and before calling updateRow() to rollback the + * updates made to a row. If no updates have been made or updateRow() has + * already been called, then this method has no effect. + * + * @exception SQLException + * if a database-access error occurs, or if called when on + * the insert row. + * @throws NotUpdatable + * DOCUMENT ME! + */ + public void cancelRowUpdates() throws SQLException { + throw new NotUpdatable(); + } + + /** + * Ensures that the result set is not closed + * + * @throws SQLException + * if the result set is closed + */ + protected final void checkClosed() throws SQLException { + if (this.isClosed) { + throw SQLError.createSQLException( + Messages + .getString("ResultSet.Operation_not_allowed_after_ResultSet_closed_144"), //$NON-NLS-1$ + SQLError.SQL_STATE_GENERAL_ERROR); + } + } + + /** + * Checks if columnIndex is within the number of columns in this result set. + * + * @param columnIndex + * the index to check + * + * @throws SQLException + * if the index is out of bounds + */ + protected final void checkColumnBounds(int columnIndex) throws SQLException { + if ((columnIndex < 1)) { + throw SQLError.createSQLException(Messages.getString( + "ResultSet.Column_Index_out_of_range_low", new Object[] { + new Integer(columnIndex), + new Integer(this.fields.length) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ + } else if ((columnIndex > this.fields.length)) { + throw SQLError.createSQLException(Messages.getString( + "ResultSet.Column_Index_out_of_range_high", new Object[] { + 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. + * + *+ * Note: 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. + *
+ * + * @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); + this.fields[i].setConnection(this.connection); + } + } + + /** + * 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 synchronized Date fastDateCreate(Calendar cal, int year, int month, + int day) { + if (cal == null) { + createCalendarIfNeeded(); + cal = this.fastDateCal; + } + + boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes(); + + return TimeUtil.fastDateCreate(useGmtMillis, + useGmtMillis ? getGmtCalendar() : null, + cal, year, month, day); + } + + private synchronized Time fastTimeCreate(Calendar cal, int hour, + int minute, int second) throws SQLException { + if (cal == null) { + createCalendarIfNeeded(); + cal = this.fastDateCal; + } + + return TimeUtil.fastTimeCreate(cal, hour, minute, second); + } + + private synchronized Timestamp fastTimestampCreate(Calendar cal, int year, + int month, int day, int hour, int minute, int seconds, + int secondsPart) { + if (cal == null) { + createCalendarIfNeeded(); + cal = this.fastDateCal; + } + + boolean useGmtMillis = this.connection.getUseGmtMillisForDatetimes(); + + return TimeUtil.fastTimestampCreate(useGmtMillis, + useGmtMillis ? getGmtCalendar() : null, + cal, year, month, day, hour, + minute, seconds, secondsPart); + } + + /* + /** + * Required by JDBC spec + */ + /* + protected void finalize() throws Throwable { + if (!this.isClosed) { + realClose(false); + } + } + */ + + // --------------------------JDBC 2.0----------------------------------- + // --------------------------------------------------------------------- + // Getter's and Setter's + // --------------------------------------------------------------------- + + + /** + * Map a ResultSet column name to a ResultSet column index + * + * @param columnName + * the name of the column + * + * @return the column index + * + * @exception SQLException + * if a database access error occurs + */ + public synchronized int findColumn(String columnName) throws SQLException { + Integer index; + + if (!this.hasBuiltIndexMapping) { + buildIndexMapping(); + } + + index = (Integer) this.columnNameToIndex.get(columnName); + + if (index == null) { + index = (Integer) this.fullColumnNameToIndex.get(columnName); + } + + if (index != null) { + return index.intValue() + 1; + } + + // Try this inefficient way, now + + for (int i = 0; i < this.fields.length; i++) { + if (this.fields[i].getName().equalsIgnoreCase(columnName)) { + return i + 1; + } else if (this.fields[i].getFullName() + .equalsIgnoreCase(columnName)) { + return i + 1; + } + } + + throw SQLError.createSQLException(Messages.getString("ResultSet.Column____112") + + columnName + + Messages.getString("ResultSet.___not_found._113"), //$NON-NLS-1$ //$NON-NLS-2$ + SQLError.SQL_STATE_COLUMN_NOT_FOUND); + } + + /** + * JDBC 2.0 + * + *+ * Moves to the first row in the result set. + *
+ * + * @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. + * + *+ * Note: 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. + *
+ * + * @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 (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + 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. + * + *+ * Be warned If the blob is huge, then you may run out of memory. + *
+ * + * @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); + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + 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 + * + *+ * Get the value of a column in the current row as a java.io.Reader. + *
+ * + * @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 + * + *+ * Get the value of a column in the current row as a java.io.Reader. + *
+ * + * @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 + * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * Note: If positioned update is not supported, a SQLException is + * thrown. + *
+ * + * @return the ResultSet's SQL cursor name. + * + * @exception SQLException + * if a database access error occurs + */ + public String getCursorName() throws SQLException { + throw SQLError.createSQLException(Messages + .getString("ResultSet.Positioned_Update_not_supported"), + SQLError.SQL_STATE_DRIVER_NOT_CAPABLE); //$NON-NLS-1$ + } + + /** + * Get the value of a column in the current row as a java.sql.Date object + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value; null if SQL NULL + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + public java.sql.Date getDate(int columnIndex) throws java.sql.SQLException { + return getDate(columnIndex, null); + } + + /** + * JDBC 2.0 Get the value of a column in the current row as a java.sql.Date + * object. Use the calendar to construct an appropriate millisecond value + * for the Date, if the underlying database doesn't store timezone + * information. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param cal + * the calendar to use in constructing the date + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Date getDate(int columnIndex, Calendar cal) + throws SQLException { + if (this.isBinaryEncoded) { + return getNativeDate(columnIndex, (cal != null) ? cal.getTimeZone() + : this.getDefaultTimeZone()); + } + + if (!this.useFastDateParsing) { + String stringVal = getStringInternal(columnIndex, false); + + if (stringVal == null) { + return null; + } + + return getDateFromString(stringVal, columnIndex); + } else { + checkColumnBounds(columnIndex); + + return getDateFromBytes(((byte[][])this.thisRow)[columnIndex - 1], 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) { + if (stringVal.length() == 8) { + return fastDateCreate(null, 1970, 1, 1); // Return EPOCH for TIME + } + + throw SQLError.createSQLException(Messages.getString( + "ResultSet.Bad_format_for_Date", new Object[] { + stringVal, 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 final java.sql.Date getDateFromBytes(byte[] dateAsBytes, + int columnIndex) throws SQLException { + checkColumnBounds(columnIndex); + + int year = 0; + int month = 0; + int day = 0; + + try { + this.wasNullFlag = false; + + if (dateAsBytes == null) { + this.wasNullFlag = true; + + return null; + } + + + boolean allZeroDate = true; + + boolean onlyTimePresent = StringUtils.indexOf(dateAsBytes, ':') != -1; + + int length = dateAsBytes.length; + + for (int i = 0; i < length; i++) { + byte b = dateAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { + allZeroDate = false; + + break; + } + } + + if (!onlyTimePresent && allZeroDate) { + + 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 '" + new String(dateAsBytes) + + "' can not be represented as java.sql.Date", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + // We're left with the case of 'round' to a date Java _can_ + // represent, which is '0001-01-01'. + return fastDateCreate(null, 1, 1, 1); + + } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { + // Convert from TIMESTAMP + switch (length) { + case 21: + case 19: { // java.sql.Timestamp format + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 5, 7); + day = StringUtils.getInt(dateAsBytes, 8, 10); + + return fastDateCreate(null, year, month, day); + } + + case 14: + case 8: { + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 4, 6); + day = StringUtils.getInt(dateAsBytes, 6, 8); + + return fastDateCreate(null, year, month, day); + } + + case 12: + case 10: + case 6: { + year = StringUtils.getInt(dateAsBytes, 0, 2); + + if (year <= 69) { + year = year + 100; + } + + month = StringUtils.getInt(dateAsBytes, 2, 4); + day = StringUtils.getInt(dateAsBytes, 4, 6); + + return fastDateCreate(null, year + 1900, month, day); + } + + case 4: { + year = StringUtils.getInt(dateAsBytes, 0, 4); + + if (year <= 69) { + year = year + 100; + } + + month = StringUtils.getInt(dateAsBytes, 2, 4); + + return fastDateCreate(null, year + 1900, month, 1); + } + + case 2: { + year = StringUtils.getInt(dateAsBytes, 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[] { + new String(dateAsBytes), new Integer(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ + } /* endswitch */ + } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { + + if (length == 2 || length == 1) { + year = StringUtils.getInt(dateAsBytes); + + if (year <= 69) { + year = year + 100; + } + + year += 1900; + } else { + year = StringUtils.getInt(dateAsBytes, 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 (length < 10) { + if (length == 8) { + return fastDateCreate(null, 1970, 1, 1); // Return EPOCH for TIME + } + + throw SQLError.createSQLException(Messages.getString( + "ResultSet.Bad_format_for_Date", new Object[] { + new String(dateAsBytes), new Integer(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ + } + + if (length != 18) { + year = StringUtils.getInt(dateAsBytes, 0, 4); + month = StringUtils.getInt(dateAsBytes, 5, 7); + day = StringUtils.getInt(dateAsBytes, 8, 10); + } else { + // JDK-1.3 timestamp format, not real easy to parse positionally :p + StringTokenizer st = new StringTokenizer(new String(dateAsBytes), "- "); + + 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[] { new String(dateAsBytes), + new Integer(columnIndex) }), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ + } + } + + private TimeZone getDefaultTimeZone() { + return this.connection.getDefaultTimeZone(); + } + + /** + * Get the value of a column in the current row as a Java double. + * + * @param columnIndex + * the first column is 1, the second is 2,... + * + * @return the column value; 0 if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + public double getDouble(int columnIndex) throws SQLException { + if (!this.isBinaryEncoded) { + return getDoubleInternal(columnIndex); + } + + return getNativeDouble(columnIndex); + } + + /** + * DOCUMENT ME! + * + * @param columnName + * DOCUMENT ME! + * + * @return DOCUMENT ME! + * + * @throws SQLException + * DOCUMENT ME! + */ + public double getDouble(String columnName) throws SQLException { + return getDouble(findColumn(columnName)); + } + + private final double getDoubleFromString(String stringVal, int columnIndex) + throws SQLException { + return getDoubleInternal(stringVal, columnIndex); + } + + /** + * Converts a string representation of a number to a double. Need a faster + * way to do this. + * + * @param colIndex + * the 1-based index of the column to retrieve a double from. + * + * @return the double value represented by the string in buf + * + * @throws SQLException + * if an error occurs + */ + protected double getDoubleInternal(int colIndex) throws SQLException { + return getDoubleInternal(getString(colIndex), colIndex); + } + + /** + * Converts a string representation of a number to a double. Need a faster + * way to do this. + * + * @param stringVal + * the double as a String + * @param colIndex + * the 1-based index of the column to retrieve a double from. + * + * @return the double value represented by the string in buf + * + * @throws SQLException + * if an error occurs + */ + protected double getDoubleInternal(String stringVal, int colIndex) + throws SQLException { + try { + if ((stringVal == null)) { + return 0; + } + + if (stringVal.length() == 0) { + return convertToZeroWithEmptyCheck(); + } + + double d = Double.parseDouble(stringVal); + + if (this.useStrictFloatingPoint) { + // Fix endpoint rounding precision loss in MySQL server + if (d == 2.147483648E9) { + // Fix Odd end-point rounding on MySQL + d = 2.147483647E9; + } else if (d == 1.0000000036275E-15) { + // Fix odd end-point rounding on MySQL + d = 1.0E-15; + } else if (d == 9.999999869911E14) { + d = 9.99999999999999E14; + } else if (d == 1.4012984643248E-45) { + d = 1.4E-45; + } else if (d == 1.4013E-45) { + d = 1.4E-45; + } else if (d == 3.4028234663853E37) { + d = 3.4028235E37; + } else if (d == -2.14748E9) { + d = -2.147483648E9; + } else if (d == 3.40282E37) { + d = 3.4028235E37; + } + } + + return d; + } catch (NumberFormatException e) { + if (this.fields[colIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_BIT) { + long valueAsLong = getNumericRepresentationOfSQLBitType(colIndex); + + return valueAsLong; + } + + throw SQLError.createSQLException(Messages.getString( + "ResultSet.Bad_format_for_number", new Object[] { + stringVal, 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); + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + 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); + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + 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. + * + *+ * Note: 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. + *
+ * + * @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); + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return null; + } + + int mysqlType = this.fields[columnIndex - 1].getMysqlType(); + + byte[] dataAsBytes = null; + + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + dataAsBytes = (byte[]) 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; + } + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return 0; + } + + columnIndex--; + + Field field = this.fields[columnIndex]; + + switch (field.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_BIT: + long valueAsLong = getNumericRepresentationOfSQLBitType(columnIndex + 1); + + if (overflowCheck && this.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. + * + *+ * Be warned If the blob is huge, then you may run out of memory. + *
+ * + * @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); + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + if (this.wasNullFlag) { + return null; + } + + Field field = this.fields[columnIndex - 1]; + + int mysqlType = field.getMysqlType(); + + // Workaround for emulated locators in servers > 4.1, + // as server returns SUBSTRING(blob) as STRING type... + if (noConversion) { + mysqlType = MysqlDefs.FIELD_TYPE_BLOB; + } + + switch (mysqlType) { + case MysqlDefs.FIELD_TYPE_TINY_BLOB: + case MysqlDefs.FIELD_TYPE_MEDIUM_BLOB: + case MysqlDefs.FIELD_TYPE_LONG_BLOB: + case MysqlDefs.FIELD_TYPE_BLOB: + case MysqlDefs.FIELD_TYPE_BIT: + return (byte[]) 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 + * + *+ * Get the value of a column in the current row as a java.io.Reader. + *
+ * + * @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", + new Object[] {stringVal, new Integer(columnIndex)}), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + return val.toString(); + } + + this.wasNullFlag = true; + + return null; + + case Types.CHAR: + case Types.VARCHAR: + case Types.LONGVARCHAR: + + return extractStringFromNativeColumn(columnIndex, mysqlType); + case Types.BINARY: + case Types.VARBINARY: + case Types.LONGVARBINARY: + + if (!field.isBlob()) { + return extractStringFromNativeColumn(columnIndex, mysqlType); + } else if (!field.isBinary()) { + return extractStringFromNativeColumn(columnIndex, mysqlType); + } else { + byte[] data = getBytes(columnIndex); + Object obj = data; + + if ((data != null) && (data.length >= 2)) { + if ((data[0] == -84) && (data[1] == -19)) { + // Serialized object? + try { + ByteArrayInputStream bytesIn = new ByteArrayInputStream( + data); + ObjectInputStream objIn = new ObjectInputStream( + bytesIn); + obj = objIn.readObject(); + objIn.close(); + bytesIn.close(); + } catch (ClassNotFoundException cnfe) { + throw SQLError.createSQLException( + Messages + .getString("ResultSet.Class_not_found___91") //$NON-NLS-1$ + + cnfe.toString() + + Messages + .getString("ResultSet._while_reading_serialized_object_92")); //$NON-NLS-1$ + } catch (IOException ex) { + obj = data; // not serialized? + } + } + + return obj.toString(); + } + + return extractStringFromNativeColumn(columnIndex, mysqlType); + } + + case Types.DATE: + + // The YEAR datatype needs to be handled differently here. + if (mysqlType == MysqlDefs.FIELD_TYPE_YEAR) { + short shortVal = getNativeShort(columnIndex); + + if (!this.connection.getYearIsDateType()) { + + if (this.wasNullFlag) { + return null; + } + + return String.valueOf(shortVal); + } + + if (field.getLength() == 2) { + + if (shortVal <= 69) { + shortVal = (short) (shortVal + 100); + } + + shortVal += 1900; + } + + return fastDateCreate(null, shortVal, 1, 1).toString(); + + } + + Date dt = getNativeDate(columnIndex); + + if (dt == null) { + return null; + } + + return String.valueOf(dt); + + case Types.TIME: + Time tm = getNativeTime(columnIndex, null, this.defaultTimeZone, false); + + if (tm == null) { + return null; + } + + return String.valueOf(tm); + + case Types.TIMESTAMP: + Timestamp tstamp = getNativeTimestamp(columnIndex, + null, this.defaultTimeZone, false); + + if (tstamp == null) { + return null; + } + + String result = String.valueOf(tstamp); + + if (!this.connection.getNoDatetimeStringSync()) { + return result; + } + + if (result.endsWith(".0")) { + return result.substring(0, result.length() - 2); + } + + default: + return extractStringFromNativeColumn(columnIndex, mysqlType); + } + } + + /** + * Get the value of a column in the current row as a java.sql.Date object + * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return the column value; null if SQL NULL + * + * @exception SQLException + * if a database access error occurs + */ + protected java.sql.Date getNativeDate(int columnIndex) throws SQLException { + return getNativeDate(columnIndex, null); + } + + /** + * JDBC 2.0 Get the value of a column in the current row as a java.sql.Date + * object. Use the calendar to construct an appropriate millisecond value + * for the Date, if the underlying database doesn't store timezone + * information. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param tz + * the calendar to use in constructing the date + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + protected java.sql.Date getNativeDate(int columnIndex, TimeZone tz) + throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + int 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(<structured-type>) 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); + } + + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + 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 + * + *+ * 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. + *
+ * + *+ * This method may also be used to read database specific abstract data + * types. + *
+ * + * @param columnIndex + * the first column is 1, the second is 2... + * + * @return a Object holding the column value + * + * @exception SQLException + * if a database access error occurs + */ + public Object getObject(int columnIndex) throws SQLException { + checkRowPos(); + checkColumnBounds(columnIndex); + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + + return null; + } + + 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 + * + *+ * 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. + *
+ * + *+ * This method may also be used to read database specific abstract data + * types. + *
+ * + * @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(); + checkColumnBounds(columnIndex); + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + Field field; + field = this.fields[columnIndex - 1]; + + switch (desiredSqlType) { + case Types.BIT: + case Types.BOOLEAN: + // valueOf would be nicer here, but it isn't + // present in JDK-1.3.1, which is what the CTS + // uses. + return 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(<structured-type>) 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(<structured-type>) 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 + * + *+ * Determine the current row number. The first row is number 1, the second + * number 2, etc. + *
+ * + * @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); + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + } else { + this.wasNullFlag = false; + } + + 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 { + String stringVal = getStringInternal(columnIndex, true); + + if (stringVal != null && this.padCharsWithSpace) { + Field f = this.fields[columnIndex - 1]; + + if (f.getMysqlType() == MysqlDefs.FIELD_TYPE_STRING ) { + int fieldLength = (int)f.getLength() /* safe, bytes in a CHAR <= 1024 */ / + f.getMaxBytesPerCharacter(); /* safe, this will never be 0 */ + + int currentLength = stringVal.length(); + + if (currentLength < fieldLength) { + StringBuffer paddedBuf = new StringBuffer(fieldLength); + paddedBuf.append(stringVal); + + int difference = fieldLength - currentLength; + + paddedBuf.append(EMPTY_SPACE, 0, difference); + + stringVal = paddedBuf.toString(); + } + } + } + + return stringVal; + } + + /** + * The following routines simply convert the columnName into a columnIndex + * and then call the appropriate routine above. + * + * @param columnName + * is the SQL name of the column + * + * @return the column value + * + * @exception SQLException + * if a database access error occurs + */ + public String getString(String columnName) throws SQLException { + return getString(findColumn(columnName)); + } + + private String getStringForClob(int columnIndex) throws SQLException { + String asString = null; + + String forcedEncoding = + this.connection.getClobCharacterEncoding(); + + if (forcedEncoding == null) { + if (!this.isBinaryEncoded) { + asString = getString(columnIndex); + } else { + asString = getNativeString(columnIndex); + } + } else { + try { + byte[] asBytes = null; + + if (!this.isBinaryEncoded) { + asBytes = getBytes(columnIndex); + } else { + asBytes = getNativeBytes(columnIndex, true); + } + + if (asBytes != null) { + asString = new String(asBytes, forcedEncoding); + } + } catch (UnsupportedEncodingException uee) { + throw SQLError.createSQLException("Unsupported character encoding " + + forcedEncoding, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + + return asString; + } + + protected String getStringInternal(int columnIndex, boolean checkDateTypes) + throws SQLException { + if (!this.isBinaryEncoded) { + checkRowPos(); + checkColumnBounds(columnIndex); + + if (this.fields == null) { + throw SQLError.createSQLException( + Messages + .getString("ResultSet.Query_generated_no_fields_for_ResultSet_99"), //$NON-NLS-1$ + SQLError.SQL_STATE_INVALID_COLUMN_NUMBER); + } + + if (this.thisRow[columnIndex - 1] == null) { + this.wasNullFlag = true; + + return null; + } + + this.wasNullFlag = false; + + 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 19: { // YYYY-MM-DD hh:mm:ss + + hr = Integer.parseInt(timeAsString.substring(length - 8, + length - 6)); + min = Integer.parseInt(timeAsString.substring(length - 5, + length - 3)); + sec = Integer.parseInt(timeAsString.substring(length - 2, + length)); + } + + break; + case 14: + case 12: { + hr = Integer.parseInt(timeAsString.substring(length - 6, + length - 4)); + min = Integer.parseInt(timeAsString.substring(length - 4, + length - 2)); + sec = Integer.parseInt(timeAsString.substring(length - 2, + length)); + } + + break; + + case 10: { + hr = Integer.parseInt(timeAsString.substring(6, 8)); + min = Integer.parseInt(timeAsString.substring(8, 10)); + sec = 0; + } + + break; + + default: + throw SQLError.createSQLException( + Messages + .getString("ResultSet.Timestamp_too_small_to_convert_to_Time_value_in_column__257") //$NON-NLS-1$ + + columnIndex + + "(" + + this.fields[columnIndex - 1] + ").", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } /* endswitch */ + + SQLWarning precisionLost = new SQLWarning( + Messages + .getString("ResultSet.Precision_lost_converting_TIMESTAMP_to_Time_with_getTime()_on_column__261") //$NON-NLS-1$ + + columnIndex + + "(" + + this.fields[columnIndex - 1] + ")."); + + if (this.warningChain == null) { + this.warningChain = precisionLost; + } else { + this.warningChain.setNextWarning(precisionLost); + } + } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATETIME) { + hr = Integer.parseInt(timeAsString.substring(11, 13)); + min = Integer.parseInt(timeAsString.substring(14, 16)); + sec = Integer.parseInt(timeAsString.substring(17, 19)); + + SQLWarning precisionLost = new SQLWarning( + Messages + .getString("ResultSet.Precision_lost_converting_DATETIME_to_Time_with_getTime()_on_column__264") //$NON-NLS-1$ + + columnIndex + + "(" + + this.fields[columnIndex - 1] + ")."); + + if (this.warningChain == null) { + this.warningChain = precisionLost; + } else { + this.warningChain.setNextWarning(precisionLost); + } + } else if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) { + return fastTimeCreate(null, 0, 0, 0); // midnight on the given + // date + } else { + // convert a String to a Time + if ((timeAsString.length() != 5) + && (timeAsString.length() != 8)) { + throw SQLError.createSQLException(Messages + .getString("ResultSet.Bad_format_for_Time____267") //$NON-NLS-1$ + + timeAsString + + Messages.getString("ResultSet.___in_column__268") + + columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + hr = Integer.parseInt(timeAsString.substring(0, 2)); + min = Integer.parseInt(timeAsString.substring(3, 5)); + sec = (timeAsString.length() == 5) ? 0 : Integer + .parseInt(timeAsString.substring(6)); + } + + Calendar sessionCalendar = this.getCalendarInstanceForSessionOrNew(); + + synchronized (sessionCalendar) { + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimeCreate( + sessionCalendar, hr, min, sec), + this.connection.getServerTimezoneTZ(), + tz, rollForward); + } + } catch (Exception ex) { + throw SQLError.createSQLException(ex.toString(), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + + private Time getTimeFromBytes(byte[] timeAsBytes, Calendar targetCalendar, + int columnIndex, + TimeZone tz, + boolean rollForward) throws SQLException { + checkColumnBounds(columnIndex); + + int hr = 0; + int min = 0; + int sec = 0; + + try { + + if (timeAsBytes == null) { + this.wasNullFlag = true; + + return null; + } + + int length = timeAsBytes.length; + + boolean allZeroTime = true; + boolean onlyTimePresent = StringUtils.indexOf(timeAsBytes, ':') != -1; + + for (int i = 0; i < length; i++) { + byte b = timeAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { + allZeroTime = false; + + break; + } + } + + if (!onlyTimePresent && allZeroTime) { + 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 '" + new String(timeAsBytes) + + "' can not be represented as java.sql.Time", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + // We're left with the case of 'round' to a time Java _can_ + // represent, which is '00:00:00' + return fastTimeCreate(null, 0, 0, 0); + } + + this.wasNullFlag = false; + + Field timeColField = this.fields[columnIndex - 1]; + + if (timeColField.getMysqlType() == MysqlDefs.FIELD_TYPE_TIMESTAMP) { + + switch (length) { + case 19: { // YYYY-MM-DD hh:mm:ss + + hr = StringUtils.getInt(timeAsBytes, length - 8, + length - 6); + min = StringUtils.getInt(timeAsBytes, length - 5, + length - 3); + sec = StringUtils.getInt(timeAsBytes, length - 2, + length); + } + + break; + case 14: + case 12: { + hr = StringUtils.getInt(timeAsBytes, length - 6, + length - 4); + min = StringUtils.getInt(timeAsBytes, length - 4, + length - 2); + sec = StringUtils.getInt(timeAsBytes, length - 2, + length); + } + + break; + + case 10: { + hr = StringUtils.getInt(timeAsBytes, 6, 8); + min = StringUtils.getInt(timeAsBytes, 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 = StringUtils.getInt(timeAsBytes, 11, 13); + min = StringUtils.getInt(timeAsBytes, 14, 16); + sec = StringUtils.getInt(timeAsBytes, 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 ((length != 5) + && (length != 8)) { + throw SQLError.createSQLException(Messages + .getString("ResultSet.Bad_format_for_Time____267") //$NON-NLS-1$ + + new String(timeAsBytes) + + Messages.getString("ResultSet.___in_column__268") + + columnIndex, SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + hr = StringUtils.getInt(timeAsBytes, 0, 2); + min = StringUtils.getInt(timeAsBytes, 3, 5); + sec = (length == 5) ? 0 : StringUtils.getInt(timeAsBytes, 6, 8); + } + + Calendar sessionCalendar = this.getCalendarInstanceForSessionOrNew(); + + synchronized (sessionCalendar) { + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimeCreate( + sessionCalendar, hr, min, sec), + this.connection.getServerTimezoneTZ(), + tz, rollForward); + } + } catch (Exception ex) { + throw SQLError.createSQLException(ex.toString(), + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + + /** + * Get the value of a column in the current row as a java.sql.Time object in + * the given timezone + * + * @param columnIndex + * the first column is 1, the second is 2... + * @param tz + * the Timezone to use + * + * @return the column value; null if SQL NULL + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + private Time getTimeInternal(int columnIndex, Calendar targetCalendar, + TimeZone tz, + boolean rollForward) throws java.sql.SQLException { + if (this.isBinaryEncoded) { + return getNativeTime(columnIndex, targetCalendar, tz, rollForward); + } + + if (!this.useFastDateParsing) { + String timeAsString = getStringInternal(columnIndex, false); + + return getTimeFromString(timeAsString, targetCalendar, + columnIndex, tz, rollForward); + } else { + checkColumnBounds(columnIndex); + + return getTimeFromBytes(((byte[][])this.thisRow)[columnIndex - 1], 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 { + checkColumnBounds(columnIndex); + + return getTimestampInternal(columnIndex, null, this.getDefaultTimeZone(), + false); + } + + /** + * Get the value of a column in the current row as a java.sql.Timestamp + * object. Use the calendar to construct an appropriate millisecond value + * for the Timestamp, if the underlying database doesn't store timezone + * information. + * + * @param columnIndex + * the first column is 1, the second is 2, ... + * @param cal + * the calendar to use in constructing the timestamp + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Timestamp getTimestamp(int columnIndex, Calendar cal) + throws SQLException { + return getTimestampInternal(columnIndex, cal, cal.getTimeZone(), true); + } + + /** + * DOCUMENT ME! + * + * @param columnName + * DOCUMENT ME! + * + * @return DOCUMENT ME! + * + * @throws java.sql.SQLException + * DOCUMENT ME! + */ + public Timestamp getTimestamp(String columnName) + throws java.sql.SQLException { + return getTimestamp(findColumn(columnName)); + } + + /** + * Get the value of a column in the current row as a java.sql.Timestamp + * object. Use the calendar to construct an appropriate millisecond value + * for the Timestamp, if the underlying database doesn't store timezone + * information. + * + * @param columnName + * is the SQL name of the column + * @param cal + * the calendar to use in constructing the timestamp + * + * @return the column value; if the value is SQL NULL, the result is null + * + * @exception SQLException + * if a database-access error occurs. + */ + public java.sql.Timestamp getTimestamp(String columnName, Calendar cal) + throws SQLException { + return getTimestamp(findColumn(columnName), cal); + } + + private Timestamp getTimestampFromString(int columnIndex, + Calendar targetCalendar, + String timestampValue, TimeZone tz, boolean rollForward) + throws java.sql.SQLException { + try { + this.wasNullFlag = false; + + if (timestampValue == null) { + this.wasNullFlag = true; + + return null; + } + + // + // JDK-6 doesn't like trailing whitespace + // + // Note this isn't a performance issue, other + // than the iteration over the string, as String.trim() + // will return a new string only if whitespace is present + // + + timestampValue = timestampValue.trim(); + + int length = timestampValue.length(); + + Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? + this.connection.getUtcCalendar() : + getCalendarInstanceForSessionOrNew(); + + synchronized (sessionCalendar) { + if ((length > 0) + && (timestampValue.charAt(0) == '0') + && (timestampValue.equals("0000-00-00") + || timestampValue.equals("0000-00-00 00:00:00") + || timestampValue.equals("00000000000000") || timestampValue + .equals("0"))) { + + if (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, 1970, 1, 1, + hour, minutes, seconds, 0), + this.connection.getServerTimezoneTZ(), + tz, rollForward); + + } + + int year = Integer.parseInt(timestampValue.substring(0, 4)); + int month = Integer + .parseInt(timestampValue.substring(4, 6)); + int day = Integer.parseInt(timestampValue.substring(6, 8)); + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year - 1900, month - 1, + day, 0, 0, 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 6: { + int year = Integer.parseInt(timestampValue.substring(0, 2)); + + if (year <= 69) { + year = (year + 100); + } + + int month = Integer + .parseInt(timestampValue.substring(2, 4)); + int day = Integer.parseInt(timestampValue.substring(4, 6)); + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year + 1900, month, day, + 0, 0, 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 4: { + int year = Integer.parseInt(timestampValue.substring(0, 2)); + + if (year <= 69) { + year = (year + 100); + } + + int month = Integer + .parseInt(timestampValue.substring(2, 4)); + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year + 1900, month, 1, 0, + 0, 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 2: { + int year = Integer.parseInt(timestampValue.substring(0, 2)); + + if (year <= 69) { + year = (year + 100); + } + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(null, year + 1900, 1, 1, 0, 0, + 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + default: + throw new java.sql.SQLException( + "Bad format for Timestamp '" + timestampValue + + "' in column " + columnIndex + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + } + } catch (Exception e) { + throw new java.sql.SQLException("Cannot convert value '" + + timestampValue + "' from column " + columnIndex + + " to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + } + + private Timestamp getTimestampFromBytes(int columnIndex, + Calendar targetCalendar, + byte[] timestampAsBytes, TimeZone tz, boolean rollForward) + throws java.sql.SQLException { + checkColumnBounds(columnIndex); + + try { + this.wasNullFlag = false; + + if (timestampAsBytes == null) { + this.wasNullFlag = true; + + return null; + } + + int length = timestampAsBytes.length; + + Calendar sessionCalendar = this.connection.getUseJDBCCompliantTimezoneShift() ? + this.connection.getUtcCalendar() : + getCalendarInstanceForSessionOrNew(); + + synchronized (sessionCalendar) { + boolean allZeroTimestamp = true; + + boolean onlyTimePresent = StringUtils.indexOf(timestampAsBytes, ':') != -1; + + for (int i = 0; i < length; i++) { + byte b = timestampAsBytes[i]; + + if (b == ' ' || b == '-' || b == '/') { + onlyTimePresent = false; + } + + if (b != '0' && b != ' ' && b != ':' && b != '-' && b != '/' + && b != '.') { + allZeroTimestamp = false; + + break; + } + } + + if (!onlyTimePresent && allZeroTimestamp) { + + if (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 '" + timestampAsBytes + + "' can not be represented as java.sql.Timestamp", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + + // We're left with the case of 'round' to a date Java _can_ + // represent, which is '0001-01-01'. + return fastTimestampCreate(null, 1, 1, 1, 0, 0, 0, 0); + + } else if (this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_YEAR) { + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, + StringUtils.getInt(timestampAsBytes, 0, 4), 1, + 1, 0, 0, 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + + } else { + if (timestampAsBytes[length - 1] == '.') { + length--; + } + + // Convert from TIMESTAMP or DATE + switch (length) { + case 26: + case 25: + case 24: + case 23: + case 22: + case 21: + case 20: + case 19: { + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 5, 7); + int day = StringUtils.getInt(timestampAsBytes, 8, 10); + int hour = StringUtils.getInt(timestampAsBytes, 11, 13); + int minutes = StringUtils.getInt(timestampAsBytes, 14, 16); + int seconds = StringUtils.getInt(timestampAsBytes, 17, 19); + + int nanos = 0; + + if (length > 19) { + int decimalIndex = StringUtils.lastIndexOf(timestampAsBytes, '.'); + + if (decimalIndex != -1) { + if ((decimalIndex + 2) <= length) { + nanos = StringUtils.getInt(timestampAsBytes, decimalIndex + 1, length); + } else { + throw new IllegalArgumentException(); // re-thrown + // further + // down + // with + // a + // much better error message + } + } + } + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year, month, day, hour, + minutes, seconds, nanos), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 14: { + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 4, 6); + int day = StringUtils.getInt(timestampAsBytes, 6, 8); + int hour = StringUtils.getInt(timestampAsBytes, 8, 10); + int minutes = StringUtils.getInt(timestampAsBytes, 10, 12); + int seconds = StringUtils.getInt(timestampAsBytes, 12, 14); + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year, month, day, hour, + minutes, seconds, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 12: { + int year = StringUtils.getInt(timestampAsBytes, 0, 2); + + if (year <= 69) { + year = (year + 100); + } + + int month = StringUtils.getInt(timestampAsBytes, 2, 4); + int day = StringUtils.getInt(timestampAsBytes, 4, 6); + int hour = StringUtils.getInt(timestampAsBytes, 6, 8); + int minutes = StringUtils.getInt(timestampAsBytes, 8, 10); + int seconds = StringUtils.getInt(timestampAsBytes, 10, 12); + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year + 1900, month, day, + hour, minutes, seconds, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 10: { + int year; + int month; + int day; + int hour; + int minutes; + + if ((this.fields[columnIndex - 1].getMysqlType() == MysqlDefs.FIELD_TYPE_DATE) + || (StringUtils.indexOf(timestampAsBytes, '-') != -1)) { + year = StringUtils.getInt(timestampAsBytes, 0, 4); + month = StringUtils.getInt(timestampAsBytes, 5, 7); + day = StringUtils.getInt(timestampAsBytes, 8, 10); + hour = 0; + minutes = 0; + } else { + year = StringUtils.getInt(timestampAsBytes, 0, 2); + + if (year <= 69) { + year = (year + 100); + } + + month = StringUtils.getInt(timestampAsBytes, 2, 4); + day = StringUtils.getInt(timestampAsBytes, 4, 6); + hour = StringUtils.getInt(timestampAsBytes, 6, 8); + minutes = StringUtils.getInt(timestampAsBytes, 8, 10); + + year += 1900; // two-digit year + } + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year, month, day, hour, + minutes, 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 8: { + if (StringUtils.indexOf(timestampAsBytes, ':') != -1) { + int hour = StringUtils.getInt(timestampAsBytes, 0, 2); + int minutes = StringUtils.getInt(timestampAsBytes, 3, 5); + int seconds = StringUtils.getInt(timestampAsBytes, 6, 8); + + return TimeUtil + .changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, 1970, 1, 1, + hour, minutes, seconds, 0), + this.connection.getServerTimezoneTZ(), + tz, rollForward); + + } + + int year = StringUtils.getInt(timestampAsBytes, 0, 4); + int month = StringUtils.getInt(timestampAsBytes, 4, 6); + int day = StringUtils.getInt(timestampAsBytes, 6, 8); + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year - 1900, month - 1, + day, 0, 0, 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 6: { + int year = StringUtils.getInt(timestampAsBytes, 0, 2); + + if (year <= 69) { + year = (year + 100); + } + + int month = StringUtils.getInt(timestampAsBytes, 2, 4); + int day = StringUtils.getInt(timestampAsBytes, 4, 6); + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year + 1900, month, day, + 0, 0, 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 4: { + int year = StringUtils.getInt(timestampAsBytes, 0, 2); + + if (year <= 69) { + year = (year + 100); + } + + int month = StringUtils.getInt(timestampAsBytes, 2, 4); + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(sessionCalendar, year + 1900, month, 1, 0, + 0, 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + case 2: { + int year = StringUtils.getInt(timestampAsBytes, 0, 2); + + if (year <= 69) { + year = (year + 100); + } + + return TimeUtil.changeTimezone(this.connection, + sessionCalendar, + targetCalendar, + fastTimestampCreate(null, year + 1900, 1, 1, 0, 0, + 0, 0), this.connection + .getServerTimezoneTZ(), tz, rollForward); + } + + default: + throw new java.sql.SQLException( + "Bad format for Timestamp '" + new String(timestampAsBytes) + + "' in column " + columnIndex + ".", + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + } + } catch (Exception e) { + throw new java.sql.SQLException("Cannot convert value '" + + new String(timestampAsBytes) + "' from column " + columnIndex + + " to TIMESTAMP.", SQLError.SQL_STATE_ILLEGAL_ARGUMENT); + } + } + + /** + * Get the value of a column in the current row as a java.sql.Timestamp + * object in the given timezone + * + * @param columnIndex + * the first column is 1, the second is 2... + * @param tz + * the timezone to use + * + * @return the column value; null if SQL NULL + * + * @exception java.sql.SQLException + * if a database access error occurs + */ + private Timestamp getTimestampInternal(int columnIndex, Calendar targetCalendar, + TimeZone tz, + boolean rollForward) throws java.sql.SQLException { + if (this.isBinaryEncoded) { + return getNativeTimestamp(columnIndex, targetCalendar, tz, rollForward); + } + + + if (!this.useFastDateParsing) { + String timestampValue = getStringInternal(columnIndex, false); + + return getTimestampFromString(columnIndex, targetCalendar, + timestampValue, tz, + rollForward); + } else { + return getTimestampFromBytes(columnIndex, targetCalendar, + ((byte[][])this.thisRow)[columnIndex - 1], 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. + * + *+ * The warning chain is automatically cleared each time a new row is read. + *
+ * + *+ * Note: 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. + *
+ * + * @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 + * + *+ * Determine if the cursor is after the last row in the result set. + *
+ * + * @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 + * + *+ * Determine if the cursor is before the first row in the result set. + *
+ * + * @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 + * + *+ * Determine if the cursor is on the first row of the result set. + *
+ * + * @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 + * + *+ * 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. + *
+ * + * @return true if on the last row, false otherwise. + * + * @exception SQLException + * if a database-access error occurs. + */ + public boolean isLast() throws SQLException { + checkClosed(); + + return this.rowData.isLast(); + } + + /** + * @param string + * @param mysqlType + * @param s + */ + private void issueConversionViaParsingWarning(String methodName, + int columnIndex, Object value, Field fieldInfo, + int[] typesWithNoParseConversion) throws SQLException { + + StringBuffer originalQueryBuf = new StringBuffer(); + + if (this.owningStatement != null + && this.owningStatement instanceof com.mysql.jdbc.PreparedStatement) { + originalQueryBuf.append(Messages.getString("ResultSet.CostlyConversionCreatedFromQuery")); + originalQueryBuf + .append(((com.mysql.jdbc.PreparedStatement) this.owningStatement).originalSql); + originalQueryBuf.append("\n\n"); + } else { + originalQueryBuf.append("."); + } + + StringBuffer convertibleTypesBuf = new StringBuffer(); + + for (int i = 0; i < typesWithNoParseConversion.length; i++) { + convertibleTypesBuf.append(MysqlDefs.typeToName(typesWithNoParseConversion[i])); + convertibleTypesBuf.append("\n"); + } + + String message = Messages.getString("ResultSet.CostlyConversion", new Object[] { + methodName, + new Integer(columnIndex + 1), + fieldInfo.getOriginalName(), + fieldInfo.getOriginalTableName(), + originalQueryBuf.toString(), + value != null ? value.getClass().getName() : ResultSetMetaData.getClassNameForJavaType( + fieldInfo.getSQLType(), + fieldInfo.isUnsigned(), + fieldInfo.getMysqlType(), + fieldInfo.isBinary() || fieldInfo.isBlob(), + fieldInfo.isOpaqueBinary()), + MysqlDefs.typeToName(fieldInfo.getMysqlType()), + convertibleTypesBuf.toString()}); + + this.eventSink.consumeEvent(new ProfilerEvent(ProfilerEvent.TYPE_WARN, + "", (this.owningStatement == null) ? "N/A" + : this.owningStatement.currentCatalog, + this.connectionId, (this.owningStatement == null) ? (-1) + : this.owningStatement.getId(), this.resultId, System + .currentTimeMillis(), 0, Constants.MILLIS_I18N, null, + this.pointOfOrigin, message)); + + } + + /** + * JDBC 2.0 + * + *+ * Moves to the last row in the result set. + *
+ * + * @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. + * + *+ * 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 + *
+ * + * @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. + * + *+ * 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 + *
+ * + * @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 + * + *+ * Moves to the previous row in the result set. + *
+ * + *+ * Note: previous() is not the same as relative(-1) since it makes sense to + * call previous() when there is no current row. + *
+ * + * @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) { + + // Report on result set closed by driver instead of application + + if (!calledExplicitly) { + this.eventSink + .consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_WARN, + "", + (this.owningStatement == null) ? "N/A" + : this.owningStatement.currentCatalog, + this.connectionId, + (this.owningStatement == null) ? (-1) + : this.owningStatement.getId(), + this.resultId, + System.currentTimeMillis(), + 0, + Constants.MILLIS_I18N, + null, + this.pointOfOrigin, + Messages + .getString("ResultSet.ResultSet_implicitly_closed_by_driver"))); //$NON-NLS-1$ + } + + if (this.rowData instanceof RowDataStatic) { + + // Report on possibly too-large result sets + + if (this.rowData.size() > this.connection + .getResultSetSizeThreshold()) { + this.eventSink + .consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_WARN, + "", + (this.owningStatement == null) ? Messages + .getString("ResultSet.N/A_159") + : this.owningStatement.currentCatalog, //$NON-NLS-1$ + this.connectionId, + (this.owningStatement == null) ? (-1) + : this.owningStatement.getId(), + this.resultId, + System.currentTimeMillis(), + 0, + Constants.MILLIS_I18N, + null, + this.pointOfOrigin, + Messages + .getString( + "ResultSet.Too_Large_Result_Set", + new Object[] { + new Integer( + this.rowData + .size()), + new Integer( + this.connection + .getResultSetSizeThreshold()) }))); + } + + if (!isLast() && !isAfterLast() && (this.rowData.size() != 0)) { + + this.eventSink + .consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_WARN, + "", + (this.owningStatement == null) ? Messages + .getString("ResultSet.N/A_159") + : this.owningStatement.currentCatalog, //$NON-NLS-1$ + this.connectionId, + (this.owningStatement == null) ? (-1) + : this.owningStatement.getId(), + this.resultId, + System.currentTimeMillis(), + 0, + Constants.MILLIS_I18N, + null, + this.pointOfOrigin, + Messages + .getString( + "ResultSet.Possible_incomplete_traversal_of_result_set", //$NON-NLS-1$ + new Object[] { + new Integer( + getRow()), + new Integer( + this.rowData + .size()) }))); + } + } + + // + // Report on any columns that were selected but + // not referenced + // + + if (this.columnUsed.length > 0 && !this.rowData.wasEmpty()) { + StringBuffer buf = new StringBuffer( + Messages + .getString("ResultSet.The_following_columns_were_never_referenced")); //$NON-NLS-1$ + + boolean issueWarn = false; + + for (int i = 0; i < this.columnUsed.length; i++) { + if (!this.columnUsed[i]) { + if (!issueWarn) { + issueWarn = true; + } else { + buf.append(", "); + } + + buf.append(this.fields[i].getFullName()); + } + } + + if (issueWarn) { + this.eventSink.consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_WARN, "", + (this.owningStatement == null) ? "N/A" + : this.owningStatement.currentCatalog, + this.connectionId, + (this.owningStatement == null) ? (-1) + : this.owningStatement.getId(), 0, + System.currentTimeMillis(), 0, + Constants.MILLIS_I18N, null, + this.pointOfOrigin, buf.toString())); + } + } + } + } finally { + SQLException exceptionDuringClose = null; + + if (this.rowData != null) { + try { + this.rowData.close(); + } catch (SQLException sqlEx) { + exceptionDuringClose = sqlEx; + } + } + + this.rowData = null; + this.defaultTimeZone = null; + this.fields = null; + this.columnNameToIndex = null; + this.fullColumnNameToIndex = null; + + this.eventSink = null; + this.warningChain = null; + + if (!this.retainOwningStatement) { + this.owningStatement = null; + } + + this.catalog = null; + this.serverInfo = null; + this.thisRow = null; + this.fastDateCal = null; + this.connection = null; + + this.isClosed = true; + + if (exceptionDuringClose != null) { + throw exceptionDuringClose; + } + } + } + + 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 + * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + * @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); + } + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ResultSetMetaData.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ResultSetMetaData.java new file mode 100644 index 00000000..8ba434ea --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/ResultSetMetaData.java @@ -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 + * + *+ * 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. + *
+ * + * @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() ? "INT UNSIGNED" : "INT"; + + 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$ + } + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowData.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowData.java new file mode 100644 index 00000000..ee282505 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowData.java @@ -0,0 +1,242 @@ +/* + Copyright (C) 2002-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ +package com.mysql.jdbc; + +import java.sql.SQLException; + +/** + * 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; + + /** + * Did this result set have no rows? + */ + boolean wasEmpty(); +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowDataDynamic.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowDataDynamic.java new file mode 100644 index 00000000..d74a778a --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowDataDynamic.java @@ -0,0 +1,455 @@ +/* + Copyright (C) 2002-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ +package com.mysql.jdbc; + +import java.sql.SQLException; + +import com.mysql.jdbc.profiler.ProfileEventSink; +import com.mysql.jdbc.profiler.ProfilerEvent; + +/** + * Allows streaming of MySQL data. + * + * @author dgan + * @version $Id: RowDataDynamic.java 6433 2007-05-18 18:38:56Z 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; + + private boolean wasEmpty = false; // we don't know until we attempt to traverse + + // ~ 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, + Constants.MILLIS_I18N, + null, + null, + Messages.getString("RowDataDynamic.2") //$NON-NLS-1$ + + howMuchMore + + Messages + .getString("RowDataDynamic.3") //$NON-NLS-1$ + + Messages + .getString("RowDataDynamic.4") //$NON-NLS-1$ + + Messages + .getString("RowDataDynamic.5") //$NON-NLS-1$ + + Messages + .getString("RowDataDynamic.6") //$NON-NLS-1$ + + this.owner.pointOfOrigin)); + } + } + } + + this.fields = null; + this.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; + + if (this.index == -1) { + this.wasEmpty = true; + } + } + } else { + this.isAfterEnd = true; + } + } catch (CommunicationsException comEx) { + // Give a better error message + comEx.setWasStreamingResults(); + + throw comEx; + } 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; + } + + public boolean wasEmpty() { + return this.wasEmpty; + } + + + +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowDataStatic.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowDataStatic.java new file mode 100644 index 00000000..67bbc340 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/RowDataStatic.java @@ -0,0 +1,260 @@ +/* + Copyright (C) 2002-2006 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ +package com.mysql.jdbc; + +import java.util.ArrayList; +import java.util.List; + +/** + * Represents an in-memory result set + * + * @author dgan + * @version $Id: RowDataStatic.java 6027 2006-11-15 18:49:17Z 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(); + } + + public boolean wasEmpty() { + return (this.rows != null && this.rows.size() == 0); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SQLError.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SQLError.java new file mode 100644 index 00000000..8de10372 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SQLError.java @@ -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+ * + * + * - 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) + * + * + *+ * + * @param maxRowsToRetrieve + * DOCUMENT ME! + * @param createStreamingResultSet + * DOCUMENT ME! + * + * @return DOCUMENT ME! + * + * @throws SQLException + */ + private com.mysql.jdbc.ResultSet serverExecute(int maxRowsToRetrieve, + boolean createStreamingResultSet, boolean unpackFields, + Field[] metadataFromCache) 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; + + boolean logSlowQueries = this.connection.getLogSlowQueries(); + boolean gatherPerformanceMetrics = this.connection + .getGatherPerformanceMetrics(); + + if (this.profileSQL || logSlowQueries || gatherPerformanceMetrics) { + begin = mysql.getCurrentTimeNanosOrMillis(); + } + + synchronized (this.cancelTimeoutMutex) { + this.wasCancelled = false; + } + + CancelTask timeoutTask = null; + + try { + if (this.connection.getEnableQueryTimeouts() && + 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); + + long queryEndTime = 0L; + + if (logSlowQueries || gatherPerformanceMetrics || this.profileSQL) { + queryEndTime = mysql.getCurrentTimeNanosOrMillis(); + } + + if (timeoutTask != null) { + timeoutTask.cancel(); + + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + this.wasCancelled = false; + throw new MySQLTimeoutException(); + } + } + + boolean queryWasSlow = false; + + if (logSlowQueries || gatherPerformanceMetrics) { + long elapsedTime = queryEndTime - begin; + + if (logSlowQueries + && (elapsedTime >= mysql.getSlowQueryThreshold())) { + queryWasSlow = true; + + StringBuffer mesgBuf = new StringBuffer( + 48 + this.originalSql.length()); + mesgBuf.append(Messages + .getString("ServerPreparedStatement.15")); //$NON-NLS-1$ + mesgBuf.append(mysql.getSlowQueryThreshold()); + mesgBuf.append(Messages + .getString("ServerPreparedStatement.15a")); //$NON-NLS-1$ + mesgBuf.append(elapsedTime); + mesgBuf.append(Messages + .getString("ServerPreparedStatement.16")); //$NON-NLS-1$ + + mesgBuf.append("as prepared: "); + mesgBuf.append(this.originalSql); + mesgBuf.append("\n\n with parameters bound:\n\n"); + mesgBuf.append(asSql(true)); + + this.eventSink + .consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_SLOW_QUERY, + "", this.currentCatalog, this.connection.getId(), //$NON-NLS-1$ + getId(), 0, System.currentTimeMillis(), + elapsedTime, mysql + .getQueryTimingUnits(), null, + new Throwable(), mesgBuf.toString())); + } + + if (gatherPerformanceMetrics) { + this.connection.registerQueryExecutionTime(elapsedTime); + } + } + + this.connection.incrementNumberOfPreparedExecutes(); + + if (this.profileSQL) { + this.eventSink = ProfileEventSink + .getInstance(this.connection); + + this.eventSink.consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_EXECUTE, + "", this.currentCatalog, //$NON-NLS-1$ + this.connectionId, this.statementId, -1, System + .currentTimeMillis(), (int) (mysql + .getCurrentTimeNanosOrMillis() - begin), + mysql.getQueryTimingUnits(), null, new Throwable(), + truncateQueryToLog(asSql(true)))); + } + + com.mysql.jdbc.ResultSet rs = mysql.readAllResults(this, + maxRowsToRetrieve, this.resultSetType, + this.resultSetConcurrency, createStreamingResultSet, + this.currentCatalog, resultPacket, true, this.fieldCount, + unpackFields, metadataFromCache); + + if (this.profileSQL) { + long fetchEndTime = mysql.getCurrentTimeNanosOrMillis(); + + this.eventSink.consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_FETCH, + "", this.currentCatalog, this.connection.getId(), //$NON-NLS-1$ + getId(), rs.resultId, System.currentTimeMillis(), + (fetchEndTime - queryEndTime), mysql + .getQueryTimingUnits(), null, + new Throwable(), null)); + } + + if (queryWasSlow && this.connection.getExplainSlowQueries()) { + String queryAsString = asSql(true); + + mysql.explainSlowQuery(queryAsString.getBytes(), + queryAsString); + } + + if (!createStreamingResultSet && + this.serverNeedsResetBeforeEachExecution) { + serverResetStatement(); // clear any long data... + } + + + this.sendTypesToServer = false; + this.results = rs; + + if (mysql.hadWarnings()) { + mysql.scanForAndThrowDataTruncation(); + } + + return rs; + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + } + } + } + } + + /** + * Sends stream-type data parameters to the server. + * + *
+ * + * 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 + * + *+ * + * @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 = mysql.getCurrentTimeNanosOrMillis(); + } + + 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.profileSQL) { + this.eventSink.consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_PREPARE, + "", this.currentCatalog, //$NON-NLS-1$ + this.connectionId, this.statementId, -1, + System.currentTimeMillis(), + mysql.getCurrentTimeNanosOrMillis() - begin, + mysql.getQueryTimingUnits(), 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(StringUtils.consistentToString(x)); + 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, this.connection.getDefaultTimeZone(), 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, this.connection.getDefaultTimeZone(), 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; + + if (dt instanceof java.sql.Timestamp) { + length = (byte) 11; + } + + intoBuf.ensureCapacity(length); + + 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) { + // MySQL expects microseconds, not nanos + intoBuf.writeLong(((java.sql.Timestamp) dt).getNanos() / 1000); + } + + } 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; + } + + public synchronized boolean canRewriteAsMultivalueInsertStatement() { + if (!super.canRewriteAsMultivalueInsertStatement()) { + return false; + } + + BindValue[] currentBindValues = null; + BindValue[] previousBindValues = null; + + int nbrCommands = this.batchedArgs.size(); + + // Can't have type changes between sets of bindings for this to work... + + for (int commandIndex = 0; commandIndex < nbrCommands; commandIndex++) { + Object arg = this.batchedArgs.get(commandIndex); + + if (!(arg instanceof String)) { + + currentBindValues = ((BatchedBindValues) arg).batchedParameterValues; + + // We need to check types each time, as + // the user might have bound different + // types in each addBatch() + + if (previousBindValues != null) { + for (int j = 0; j < this.parameterBindings.length; j++) { + if (currentBindValues[j].bufferType != previousBindValues[j].bufferType) { + return false; + } + } + } + } + } + + return true; + } + + /** + * Computes the maximum parameter set size, and entire batch size given + * the number of arguments in the batch. + */ + protected long[] computeMaxParameterSetSizeAndBatchSize(int numBatchedArgs) { + long sizeOfEntireBatch = 1 + /* com_execute */ + 4 /* stmt id */ + 1 /* flags */ + 4 /* batch count padding */; + long maxSizeOfParameterSet = 0; + + for (int i = 0; i < numBatchedArgs; i++) { + BindValue[] paramArg = ((BatchedBindValues) this.batchedArgs.get(i)).batchedParameterValues; + + long sizeOfParameterSet = 0; + + sizeOfParameterSet += (this.parameterCount + 7) / 8; // for isNull + + sizeOfParameterSet += this.parameterCount * 2; // have to send types + + for (int j = 0; j < this.parameterBindings.length; j++) { + if (!paramArg[j].isNull) { + + long size = paramArg[j].getBoundLength(); + + if (paramArg[j].isLongData) { + if (size != -1) { + sizeOfParameterSet += size; + } + } else { + sizeOfParameterSet += size; + } + } + } + + sizeOfEntireBatch += sizeOfParameterSet; + + if (sizeOfParameterSet > maxSizeOfParameterSet) { + maxSizeOfParameterSet = sizeOfParameterSet; + } + } + + return new long[] {maxSizeOfParameterSet, sizeOfEntireBatch}; + } + + protected int setOneBatchedParameterSet( + java.sql.PreparedStatement batchedStatement, int batchedParamIndex, + Object paramSet) throws SQLException { + BindValue[] paramArg = ((BatchedBindValues) paramSet).batchedParameterValues; + + for (int j = 0; j < paramArg.length; j++) { + if (paramArg[j].isNull) { + batchedStatement.setNull(batchedParamIndex++, Types.NULL); + } else { + if (paramArg[j].isLongData) { + Object value = paramArg[j].value; + + if (value instanceof InputStream) { + batchedStatement.setBinaryStream(batchedParamIndex++, + (InputStream) value, + (int) paramArg[j].bindLength); + } else { + batchedStatement.setCharacterStream( + batchedParamIndex++, (Reader) value, + (int) paramArg[j].bindLength); + } + } else { + + switch (paramArg[j].bufferType) { + + case MysqlDefs.FIELD_TYPE_TINY: + batchedStatement.setByte(batchedParamIndex++, + paramArg[j].byteBinding); + break; + case MysqlDefs.FIELD_TYPE_SHORT: + batchedStatement.setShort(batchedParamIndex++, + paramArg[j].shortBinding); + break; + case MysqlDefs.FIELD_TYPE_LONG: + batchedStatement.setInt(batchedParamIndex++, + paramArg[j].intBinding); + break; + case MysqlDefs.FIELD_TYPE_LONGLONG: + batchedStatement.setLong(batchedParamIndex++, + paramArg[j].longBinding); + break; + case MysqlDefs.FIELD_TYPE_FLOAT: + batchedStatement.setFloat(batchedParamIndex++, + paramArg[j].floatBinding); + break; + case MysqlDefs.FIELD_TYPE_DOUBLE: + batchedStatement.setDouble(batchedParamIndex++, + paramArg[j].doubleBinding); + break; + case MysqlDefs.FIELD_TYPE_TIME: + batchedStatement.setTime(batchedParamIndex++, + (Time) paramArg[j].value); + break; + case MysqlDefs.FIELD_TYPE_DATE: + batchedStatement.setDate(batchedParamIndex++, + (Date) paramArg[j].value); + break; + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + batchedStatement.setTimestamp(batchedParamIndex++, + (Timestamp) paramArg[j].value); + break; + 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: + Object value = paramArg[j].value; + + if (value instanceof byte[]) { + batchedStatement.setBytes(batchedParamIndex, + (byte[]) value); + } else { + batchedStatement.setString(batchedParamIndex, + (String) value); + } + + BindValue asBound = ((ServerPreparedStatement) batchedStatement) + .getBinding( + batchedParamIndex + 1 /* + * uses 1-based + * offset + */, + false); + asBound.bufferType = paramArg[j].bufferType; + + batchedParamIndex++; + + break; + default: + throw new IllegalArgumentException( + "Unknown type when re-binding parameter into batched statement for parameter index " + + batchedParamIndex); + } + } + } + } + + return batchedParamIndex; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SingleByteCharsetConverter.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SingleByteCharsetConverter.java new file mode 100644 index 00000000..c1d79b59 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SingleByteCharsetConverter.java @@ -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); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SocketFactory.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SocketFactory.java new file mode 100644 index 00000000..1fe6b1b7 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/SocketFactory.java @@ -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 + * "socketFactory", where the standard is + *
com.mysql.jdbc.StandardSocketFactory
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;
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/StandardSocketFactory.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/StandardSocketFactory.java
new file mode 100644
index 00000000..e3a36ef7
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/StandardSocketFactory.java
@@ -0,0 +1,409 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import java.io.IOException;
+
+import java.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 {
+
+ public static final String TCP_NO_DELAY_PROPERTY_NAME = "tcpNoDelay";
+
+ public static final String TCP_KEEP_ALIVE_DEFAULT_VALUE = "true";
+
+ public static final String TCP_KEEP_ALIVE_PROPERTY_NAME = "tcpKeepAlive";
+
+ public static final String TCP_RCV_BUF_PROPERTY_NAME = "tcpRcvBuf";
+
+ public static final String TCP_SND_BUF_PROPERTY_NAME = "tcpSndBuf";
+
+ public static final String TCP_TRAFFIC_CLASS_PROPERTY_NAME = "tcpTrafficClass";
+
+ public static final String TCP_RCV_BUF_DEFAULT_VALUE = "0";
+
+ public static final String TCP_SND_BUF_DEFAULT_VALUE = "0";
+
+ public static final String TCP_TRAFFIC_CLASS_DEFAULT_VALUE = "0";
+
+ public static final String TCP_NO_DELAY_DEFAULT_VALUE = "true";
+
+ /** Use reflection for pre-1.4 VMs */
+
+ private static Method setTraficClassMethod;
+
+ static {
+ try {
+ setTraficClassMethod = Socket.class.getMethod("setTrafficClass",
+ new Class[] { Integer.TYPE });
+ } catch (SecurityException e) {
+ setTraficClassMethod = null;
+ } catch (NoSuchMethodException e) {
+ setTraficClassMethod = null;
+ }
+ }
+
+ /** The hostname to connect to */
+ protected String host = null;
+
+ /** The port number to connect to */
+ protected int port = 3306;
+
+ /** The underlying TCP/IP socket to use */
+ protected Socket rawSocket = null;
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Configures socket properties based on properties from the connection
+ * (tcpNoDelay, snd/rcv buf, traffic class, etc).
+ *
+ * @param props
+ * @throws SocketException
+ * @throws IOException
+ */
+ private void configureSocket(Socket sock, Properties props) throws SocketException,
+ IOException {
+ try {
+ sock.setTcpNoDelay(Boolean.valueOf(
+ props.getProperty(TCP_NO_DELAY_PROPERTY_NAME,
+ TCP_NO_DELAY_DEFAULT_VALUE)).booleanValue());
+
+ String keepAlive = props.getProperty(TCP_KEEP_ALIVE_PROPERTY_NAME,
+ TCP_KEEP_ALIVE_DEFAULT_VALUE);
+
+ if (keepAlive != null && keepAlive.length() > 0) {
+ sock.setKeepAlive(Boolean.valueOf(keepAlive)
+ .booleanValue());
+ }
+
+ int receiveBufferSize = Integer.parseInt(props.getProperty(
+ TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));
+
+ if (receiveBufferSize > 0) {
+ sock.setReceiveBufferSize(receiveBufferSize);
+ }
+
+ int sendBufferSize = Integer.parseInt(props.getProperty(
+ TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));
+
+ if (sendBufferSize > 0) {
+ sock.setSendBufferSize(sendBufferSize);
+ }
+
+ int trafficClass = Integer.parseInt(props.getProperty(
+ TCP_TRAFFIC_CLASS_PROPERTY_NAME,
+ TCP_TRAFFIC_CLASS_DEFAULT_VALUE));
+
+ if (trafficClass > 0 && setTraficClassMethod != null) {
+ setTraficClassMethod.invoke(sock,
+ new Object[] { new Integer(trafficClass) });
+ }
+ } catch (Throwable t) {
+ unwrapExceptionToProperClassAndThrowIt(t);
+ }
+ }
+
+ /**
+ * @see com.mysql.jdbc.SocketFactory#createSocket(Properties)
+ */
+ public Socket connect(String hostname, int portNumber, Properties props)
+ throws SocketException, IOException {
+
+ if (props != null) {
+ this.host = hostname;
+
+ this.port = portNumber;
+
+ Method connectWithTimeoutMethod = null;
+ Method socketBindMethod = null;
+ Class socketAddressClass = null;
+
+ String localSocketHostname = props
+ .getProperty("localSocketAddress");
+
+ String connectTimeoutStr = props.getProperty("connectTimeout");
+
+ int connectTimeout = 0;
+
+ boolean wantsTimeout = (connectTimeoutStr != null
+ && connectTimeoutStr.length() > 0 && !connectTimeoutStr
+ .equals("0"));
+
+ boolean wantsLocalBind = (localSocketHostname != null && localSocketHostname
+ .length() > 0);
+
+ boolean needsConfigurationBeforeConnect = socketNeedsConfigurationBeforeConnect(props);
+
+ if (wantsTimeout || wantsLocalBind || needsConfigurationBeforeConnect) {
+
+ if (connectTimeoutStr != null) {
+ try {
+ connectTimeout = Integer.parseInt(connectTimeoutStr);
+ } catch (NumberFormatException nfe) {
+ throw new SocketException("Illegal value '"
+ + connectTimeoutStr + "' for connectTimeout");
+ }
+ }
+
+ try {
+ // Have to do this with reflection, otherwise older JVMs
+ // croak
+ socketAddressClass = Class
+ .forName("java.net.SocketAddress");
+
+ connectWithTimeoutMethod = Socket.class.getMethod(
+ "connect", new Class[] { socketAddressClass,
+ Integer.TYPE });
+
+ socketBindMethod = Socket.class.getMethod("bind",
+ new Class[] { socketAddressClass });
+
+ } catch (NoClassDefFoundError noClassDefFound) {
+ // ignore, we give a better error below if needed
+ } catch (NoSuchMethodException noSuchMethodEx) {
+ // ignore, we give a better error below if needed
+ } catch (Throwable catchAll) {
+ // ignore, we give a better error below if needed
+ }
+
+ if (wantsLocalBind && socketBindMethod == null) {
+ throw new SocketException(
+ "Can't specify \"localSocketAddress\" on JVMs older than 1.4");
+ }
+
+ if (wantsTimeout && connectWithTimeoutMethod == null) {
+ throw new SocketException(
+ "Can't specify \"connectTimeout\" on JVMs older than 1.4");
+ }
+ }
+
+ if (this.host != null) {
+ if (!(wantsLocalBind || wantsTimeout || needsConfigurationBeforeConnect)) {
+ InetAddress[] possibleAddresses = InetAddress
+ .getAllByName(this.host);
+
+ Throwable caughtWhileConnecting = null;
+
+ // Need to loop through all possible addresses, in case
+ // someone has IPV6 configured (SuSE, for example...)
+
+ for (int i = 0; i < possibleAddresses.length; i++) {
+ try {
+ this.rawSocket = new Socket(possibleAddresses[i],
+ port);
+
+ configureSocket(this.rawSocket, props);
+
+ break;
+ } catch (Exception ex) {
+ caughtWhileConnecting = ex;
+ }
+ }
+
+ if (rawSocket == null) {
+ unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
+ }
+ } else {
+ // must explicitly state this due to classloader issues
+ // when running on older JVMs :(
+ try {
+
+ InetAddress[] possibleAddresses = InetAddress
+ .getAllByName(this.host);
+
+ Throwable caughtWhileConnecting = null;
+
+ Object localSockAddr = null;
+
+ Class inetSocketAddressClass = null;
+
+ Constructor addrConstructor = null;
+
+ try {
+ inetSocketAddressClass = Class
+ .forName("java.net.InetSocketAddress");
+
+ addrConstructor = inetSocketAddressClass
+ .getConstructor(new Class[] {
+ InetAddress.class, Integer.TYPE });
+
+ if (wantsLocalBind) {
+ localSockAddr = addrConstructor
+ .newInstance(new Object[] {
+ InetAddress
+ .getByName(localSocketHostname),
+ new Integer(0 /*
+ * use ephemeral
+ * port
+ */) });
+
+ }
+ } catch (Throwable ex) {
+ unwrapExceptionToProperClassAndThrowIt(ex);
+ }
+
+ // Need to loop through all possible addresses, in case
+ // someone has IPV6 configured (SuSE, for example...)
+
+ for (int i = 0; i < possibleAddresses.length; i++) {
+
+ try {
+ this.rawSocket = new Socket();
+
+ configureSocket(this.rawSocket, props);
+
+ Object sockAddr = addrConstructor
+ .newInstance(new Object[] {
+ possibleAddresses[i],
+ new Integer(port) });
+ // bind to the local port, null is 'ok', it
+ // means
+ // use the ephemeral port
+ socketBindMethod.invoke(rawSocket,
+ new Object[] { localSockAddr });
+
+ connectWithTimeoutMethod.invoke(rawSocket,
+ new Object[] { sockAddr,
+ new Integer(connectTimeout) });
+
+ break;
+ } catch (Exception ex) {
+ this.rawSocket = null;
+
+ caughtWhileConnecting = ex;
+ }
+ }
+
+ if (this.rawSocket == null) {
+ unwrapExceptionToProperClassAndThrowIt(caughtWhileConnecting);
+ }
+
+ } catch (Throwable t) {
+ unwrapExceptionToProperClassAndThrowIt(t);
+ }
+ }
+
+ return this.rawSocket;
+ }
+ }
+
+ throw new SocketException("Unable to create socket");
+ }
+
+ /**
+ * Does the configureSocket() need to be called before the socket is
+ * connect()d based on the properties supplied?
+ *
+ */
+ private boolean socketNeedsConfigurationBeforeConnect(Properties props) {
+ int receiveBufferSize = Integer.parseInt(props.getProperty(
+ TCP_RCV_BUF_PROPERTY_NAME, TCP_RCV_BUF_DEFAULT_VALUE));
+
+ if (receiveBufferSize > 0) {
+ return true;
+ }
+
+ int sendBufferSize = Integer.parseInt(props.getProperty(
+ TCP_SND_BUF_PROPERTY_NAME, TCP_SND_BUF_DEFAULT_VALUE));
+
+ if (sendBufferSize > 0) {
+ return true;
+ }
+
+ int trafficClass = Integer.parseInt(props.getProperty(
+ TCP_TRAFFIC_CLASS_PROPERTY_NAME,
+ TCP_TRAFFIC_CLASS_DEFAULT_VALUE));
+
+ if (trafficClass > 0 && setTraficClassMethod != null) {
+ return true;
+ }
+
+ return false;
+ }
+
+ private void unwrapExceptionToProperClassAndThrowIt(
+ Throwable caughtWhileConnecting) throws SocketException,
+ IOException {
+ if (caughtWhileConnecting instanceof InvocationTargetException) {
+
+ // Replace it with the target, don't use 1.4 chaining as this still
+ // needs to run on older VMs
+ caughtWhileConnecting = ((InvocationTargetException) caughtWhileConnecting)
+ .getTargetException();
+ }
+
+ if (caughtWhileConnecting instanceof SocketException) {
+ throw (SocketException) caughtWhileConnecting;
+ }
+
+ if (caughtWhileConnecting instanceof IOException) {
+ throw (IOException) caughtWhileConnecting;
+ }
+
+ throw new SocketException(caughtWhileConnecting.toString());
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Statement.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Statement.java
new file mode 100644
index 00000000..800c9f8b
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Statement.java
@@ -0,0 +1,2368 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import com.mysql.jdbc.exceptions.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.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.TimerTask;
+
+/**
+ * A Statement object is used for executing a static SQL statement and obtaining
+ * the results produced by it.
+ *
+ * + * 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. + *
+ * + * @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 { + protected static final String PING_MARKER = "/* ping */"; + + /** + * Thread used to implement query timeouts...Eventually we could be more + * efficient and have one thread with timers, but this is a straightforward + * and simple way to implement a feature that isn't used all that often. + */ + class CancelTask extends TimerTask { + + long connectionId = 0; + SQLException caughtWhileCancelling = null; + + 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 { + synchronized (cancelTimeoutMutex) { + cancelConn = connection.duplicate(); + cancelStmt = cancelConn.createStatement(); + cancelStmt.execute("KILL QUERY " + connectionId); + wasCancelled = true; + } + } catch (SQLException sqlEx) { + caughtWhileCancelling = sqlEx; + } catch (NullPointerException npe) { + // Case when connection closed while starting to cancel + // We can't easily synchronize this, because then one thread + // can't cancel() a running query + + // ignore, we shouldn't re-throw this, because the connection's + // already closed, so the statement has been timed out. + } finally { + if (cancelStmt != null) { + try { + cancelStmt.close(); + } catch (SQLException sqlEx) { + throw new RuntimeException(sqlEx.toString()); + } + } + + if (cancelConn != null) { + try { + cancelConn.close(); + } catch (SQLException sqlEx) { + throw new RuntimeException(sqlEx.toString()); + } + } + } + } + }; + + cancelThread.start(); + } + } + + /** Mutex to prevent race between returning query results and noticing + that we're timed-out or cancelled. */ + + protected Object cancelTimeoutMutex = new Object(); + + /** Used to generate IDs when profiling. */ + protected static int statementCounter = 1; + + public final static byte USES_VARIABLES_FALSE = 0; + + public final static byte USES_VARIABLES_TRUE = 1; + + public final static byte USES_VARIABLES_UNKNOWN = -1; + + protected boolean wasCancelled = false; + + /** 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; + + /** The type of this result set (scroll sensitive or in-sensitive) */ + protected int resultSetType = 0; + + /** Used to identify this statement when profiling. */ + protected int statementId; + + /** The timeout for a query */ + protected int timeoutInMillis = 0; + + /** The update count for this statement */ + protected long updateCount = -1; + + /** Should we use the usage advisor? */ + protected boolean useUsageAdvisor = false; + + /** The warnings chain. */ + protected SQLWarning warningChain = null; + + /** + * Should this statement hold results open over .close() irregardless of + * connection's setting? + */ + protected boolean holdResultsOpenOverClose = false; + + protected ArrayList batchedGeneratedKeys = null; + + protected boolean retrieveGeneratedKeys = false; + + protected boolean continueBatchOnError = false; + + protected PingTarget pingTarget = null; + + + /** + * Constructor for a Statement. + * + * @param c + * the Connection instantation that creates us + * @param catalog + * the database name in use when we were created + * + * @throws SQLException + * if an error occurs. + */ + public 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 { + synchronized (this.cancelTimeoutMutex) { + cancelConn = this.connection.duplicate(); + cancelStmt = cancelConn.createStatement(); + cancelStmt.execute("KILL QUERY " + + this.connection.getIO().getThreadId()); + this.wasCancelled = true; + } + } catch (NullPointerException npe) { + // Case when connection closed while starting to cancel + // We can't easily synchronize this, because then one thread + // can't cancel() a running query + + throw SQLError.createSQLException(Messages + .getString("Statement.49"), //$NON-NLS-1$ + SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$ + } finally { + if (cancelStmt != null) { + cancelStmt.close(); + } + + if (cancelConn != null) { + cancelConn.close(); + } + } + + } + } + + // --------------------------JDBC 2.0----------------------------- + + /** + * Checks if closed() has been called, and throws an exception if so + * + * @throws SQLException + * if this statement has been closed + */ + protected void checkClosed() throws SQLException { + if (this.isClosed) { + throw SQLError.createSQLException(Messages + .getString("Statement.49"), //$NON-NLS-1$ + SQLError.SQL_STATE_CONNECTION_NOT_OPEN); //$NON-NLS-1$ + } + } + + /** + * Checks if the given SQL query with the given first non-ws char is a DML + * statement. Throws an exception if it is. + * + * @param sql + * the SQL to check + * @param firstStatementChar + * the UC first non-ws char of the statement + * + * @throws SQLException + * if the statement contains DML + */ + protected void checkForDml(String sql, char firstStatementChar) + throws SQLException { + if ((firstStatementChar == 'I') || (firstStatementChar == 'U') + || (firstStatementChar == 'D') || (firstStatementChar == 'A') + || (firstStatementChar == 'C')) { + String noCommentSql = StringUtils.stripComments(sql, + "'\"", "'\"", true, false, true, true); + + if (StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "INSERT") //$NON-NLS-1$ + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "UPDATE") //$NON-NLS-1$ + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DELETE") //$NON-NLS-1$ + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "DROP") //$NON-NLS-1$ + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "CREATE") //$NON-NLS-1$ + || StringUtils.startsWithIgnoreCaseAndWs(noCommentSql, "ALTER")) { //$NON-NLS-1$ + throw SQLError.createSQLException(Messages + .getString("Statement.57"), //$NON-NLS-1$ + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ + } + } + } + + /** + * Method checkNullOrEmptyQuery. + * + * @param sql + * the SQL to check + * + * @throws SQLException + * if query is null or empty. + */ + protected void checkNullOrEmptyQuery(String sql) throws SQLException { + if (sql == null) { + throw SQLError.createSQLException(Messages + .getString("Statement.59"), //$NON-NLS-1$ + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$ + } + + if (sql.length() == 0) { + throw SQLError.createSQLException(Messages + .getString("Statement.61"), //$NON-NLS-1$ + SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * JDBC 2.0 Make the set of commands in the current batch empty. This method + * is optional. + * + * @exception SQLException + * if a database-access error occurs, or the driver does not + * support batch statements + */ + public synchronized void clearBatch() throws SQLException { + if (this.batchedArgs != null) { + this.batchedArgs.clear(); + } + } + + /** + * After this call, getWarnings returns null until a new warning is reported + * for this Statement. + * + * @exception SQLException + * if a database access error occurs (why?) + */ + public void clearWarnings() throws SQLException { + this.warningChain = null; + } + + /** + * In many cases, it is desirable to immediately release a Statement's + * database and JDBC resources instead of waiting for this to happen when it + * is automatically closed. The close method provides this immediate + * release. + * + *+ * Note: A Statement is automatically closed when it is garbage + * collected. When a Statement is closed, its current ResultSet, if one + * exists, is also closed. + *
+ * + * @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); + + if (this.maxRows > -1) { + pStmt.setMaxRows(this.maxRows); + } + + pStmt.execute(); + + // + // Need to be able to get resultset irrespective if we issued DML or + // not to make this work. + // + 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()) { + synchronized (this.cancelTimeoutMutex) { + 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); + } + } + + if (firstNonWsChar == '/') { + if (sql.startsWith(PING_MARKER)) { + doPingInstead(); + + return true; + } + } + + 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; + + String oldCatalog = null; + + try { + if (locallyScopedConn.getEnableQueryTimeouts() && + this.timeoutInMillis != 0 + && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(); + Connection.getCancelTimer().schedule(timeoutTask, + this.timeoutInMillis); + } + + + + if (!locallyScopedConn.getCatalog().equals( + this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Check if we have cached metadata for this query... + // + if (locallyScopedConn.getCacheResultSetMetadata()) { + cachedMetaData = locallyScopedConn.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) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + this.wasCancelled = false; + throw new MySQLTimeoutException(); + } + } + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + } + } + + this.lastInsertId = rs.getUpdateID(); + + if (rs != null) { + this.results = rs; + + rs.setFirstCharOfQuery(firstNonWsChar); + + if (rs.reallyResult()) { + if (cachedMetaData != null) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData, + this.results); + } else { + if (this.connection.getCacheResultSetMetadata()) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, + null /* will be created */, this.results); + } + } + } + } + + return ((rs != null) && rs.reallyResult()); + } + } + + /** + * @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()) { + if (this.batchedArgs == null || this.batchedArgs.size() == 0) { + return new int[0]; + } + + 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(); + } + + java.sql.Statement batchStmt = null; + + try { + int[] updateCounts = new int[nbrCommands]; + + for (int i = 0; i < nbrCommands; i++) { + updateCounts[i] = -3; + } + + int commandIndex = 0; + + StringBuffer queryBuf = new StringBuffer(); + + + + batchStmt = locallyScopedConn.createStatement(); + + + 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 { + try { + if (batchStmt != null) { + batchStmt.close(); + } + } 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()) { + synchronized (this.cancelTimeoutMutex) { + 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, + findStartOfStatement(sql)); + + if (sql.charAt(0) == '/') { + if (sql.startsWith(PING_MARKER)) { + doPingInstead(); + + return this.results; + } + } + + checkForDml(sql, firstStatementChar); + + if (this.results != null) { + if (!locallyScopedConn.getHoldResultsOpenOverStatementClose()) { + this.results.realClose(false); + } + } + + CachedResultSetMetaData cachedMetaData = null; + + // If there isn't a limit clause in the SQL + // then limit the number of rows to return in + // an efficient manner. Only do this if + // setMaxRows() hasn't been used on any Statements + // generated from the current Connection (saves + // a query, and network traffic). + + if (useServerFetch()) { + this.results = createResultSetUsingServerFetch(sql); + + return this.results; + } + + CancelTask timeoutTask = null; + + String oldCatalog = null; + + try { + if (locallyScopedConn.getEnableQueryTimeouts() && + this.timeoutInMillis != 0 + && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(); + Connection.getCancelTimer().schedule(timeoutTask, + this.timeoutInMillis); + } + + if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Check if we have cached metadata for this query... + // + if (locallyScopedConn.getCacheResultSetMetadata()) { + cachedMetaData = locallyScopedConn.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) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + this.wasCancelled = false; + throw new MySQLTimeoutException(); + } + } + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + } + + this.lastInsertId = this.results.getUpdateID(); + + if (cachedMetaData != null) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, cachedMetaData, + this.results); + } else { + if (this.connection.getCacheResultSetMetadata()) { + locallyScopedConn.initializeResultsMetadataFromCache(sql, + null /* will be created */, this.results); + } + } + + return this.results; + } + } + + protected void doPingInstead() throws SQLException { + if (this.pingTarget != null) { + this.pingTarget.doPing(); + } else { + this.connection.ping(); + } + + ResultSet fakeSelectOneResultSet = generatePingResultSet(); + this.results = fakeSelectOneResultSet; + } + + protected ResultSet generatePingResultSet() throws SQLException { + Field[] fields = { new Field(null, "1", Types.BIGINT, 1) }; + ArrayList rows = new ArrayList(); + byte[] colVal = new byte[] { (byte) '1' }; + + rows.add(new byte[][] { colVal }); + + return (ResultSet) DatabaseMetaData.buildResultSet(fields, rows, + this.connection); + } + + /** + * 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, + findStartOfStatement(sql)); + + ResultSet rs = null; + + synchronized (locallyScopedConn.getMutex()) { + synchronized (this.cancelTimeoutMutex) { + 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; + + String oldCatalog = null; + + try { + if (locallyScopedConn.getEnableQueryTimeouts() && + this.timeoutInMillis != 0 + && locallyScopedConn.versionMeetsMinimum(5, 0, 0)) { + timeoutTask = new CancelTask(); + Connection.getCancelTimer().schedule(timeoutTask, + this.timeoutInMillis); + } + + if (!locallyScopedConn.getCatalog().equals(this.currentCatalog)) { + oldCatalog = locallyScopedConn.getCatalog(); + locallyScopedConn.setCatalog(this.currentCatalog); + } + + // + // Only apply max_rows to selects + // + if (locallyScopedConn.useMaxRows()) { + 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) { + if (timeoutTask.caughtWhileCancelling != null) { + throw timeoutTask.caughtWhileCancelling; + } + + timeoutTask.cancel(); + timeoutTask = null; + } + + synchronized (this.cancelTimeoutMutex) { + if (this.wasCancelled) { + this.wasCancelled = false; + throw new MySQLTimeoutException(); + } + } + } finally { + if (timeoutTask != null) { + timeoutTask.cancel(); + } + + if (oldCatalog != null) { + locallyScopedConn.setCatalog(oldCatalog); + } + } + } + + this.results = rs; + + rs.setFirstCharOfQuery(firstStatementChar); + + this.updateCount = rs.getUpdateCount(); + + int truncatedUpdateCount = 0; + + if (this.updateCount > Integer.MAX_VALUE) { + truncatedUpdateCount = Integer.MAX_VALUE; + } else { + truncatedUpdateCount = (int) this.updateCount; + } + + this.lastInsertId = rs.getUpdateID(); + + return truncatedUpdateCount; + } + + /** + * @see 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); + } + + + + /** + * 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. + * + *+ * 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()". + *
+ * + * @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. + * + *+ * This method returns longs as MySQL server versions newer than 3.22.4 + * return 64-bit values for update counts + *
+ * + * @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. + * + *+ * The Warning chain is automatically cleared each time a statement is + * (re)executed. + *
+ * + *+ * Note: If you are processing a ResultSet then any warnings + * associated with ResultSet reads will be chained on the ResultSet object. + *
+ * + * @return the first java.sql.SQLWarning or null + * + * @exception SQLException + * if a database access error occurs + */ + public java.sql.SQLWarning getWarnings() throws SQLException { + checkClosed(); + + if (this.connection != null && !this.connection.isClosed() + && this.connection.versionMeetsMinimum(4, 1, 0)) { + SQLWarning pendingWarningsFromServer = SQLError + .convertShowWarningsToSQLWarnings(this.connection); + + if (this.warningChain != null) { + this.warningChain.setNextWarning(pendingWarningsFromServer); + } else { + this.warningChain = pendingWarningsFromServer; + } + + return this.warningChain; + } + + return this.warningChain; + } + + + + /** + * Closes this statement, and frees resources. + * + * @param calledExplicitly + * was this called from close()? + * + * @throws SQLException + * if an error occurs + */ + protected void realClose(boolean calledExplicitly, boolean closeOpenResults) + throws SQLException { + if (this.isClosed) { + return; + } + + if (this.useUsageAdvisor) { + if (!calledExplicitly) { + String message = Messages.getString("Statement.63") //$NON-NLS-1$ + + Messages.getString("Statement.64"); //$NON-NLS-1$ + + this.eventSink.consumeEvent(new ProfilerEvent( + ProfilerEvent.TYPE_WARN, + "", //$NON-NLS-1$ + this.currentCatalog, this.connectionId, this.getId(), + -1, System.currentTimeMillis(), 0, + Constants.MILLIS_I18N, null, this.pointOfOrigin, + message)); + } + } + + if (this.results != null) { + if (closeOpenResults) { + closeOpenResults = !this.holdResultsOpenOverClose; + } + + if (closeOpenResults && this.connection != null + && !this.connection.getHoldResultsOpenOverStatementClose()) { + try { + this.results.close(); + } catch (Exception ex) { + ; + } + + this.closeAllOpenResults(); + } + } + + if (this.connection != null) { + if (this.maxRowsChanged) { + this.connection.unsetMaxRows(this); + } + + if (!this.connection.getDontTrackOpenResources()) { + this.connection.unregisterStatement(this); + } + } + + this.isClosed = true; + + this.results = null; + this.connection = null; + this.warningChain = null; + this.openResults = null; + this.batchedGeneratedKeys = null; + this.cancelTimeoutMutex = null; + this.pingTarget = null; + } + + /** + * setCursorName defines the SQL cursor name that will be used by subsequent + * execute methods. This name can then be used in SQL positioned + * update/delete statements to identify the current row in the ResultSet + * generated by this statement. If a database doesn't support positioned + * update/delete, this method is a no-op. + * + *+ * Note: This MySQL driver does not support cursors. + *
+ * + * @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 + * <= rows <= 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; + } + + protected int findStartOfStatement(String sql) { + int statementStartPos = 0; + + if (StringUtils.startsWithIgnoreCaseAndWs(sql, "/*")) { + statementStartPos = sql.indexOf("*/"); + + if (statementStartPos == -1) { + statementStartPos = 0; + } else { + statementStartPos += 2; + } + } else if (StringUtils.startsWithIgnoreCaseAndWs(sql, "--") + || StringUtils.startsWithIgnoreCaseAndWs(sql, "#")) { + statementStartPos = sql.indexOf('\n'); + + if (statementStartPos == -1) { + statementStartPos = sql.indexOf('\r'); + + if (statementStartPos == -1) { + statementStartPos = 0; + } + } + } + + return statementStartPos; + } + + protected synchronized void setPingTarget(PingTarget pingTarget) { + this.pingTarget = pingTarget; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/StringUtils.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/StringUtils.java new file mode 100644 index 00000000..c91858e2 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/StringUtils.java @@ -0,0 +1,1621 @@ +/* + 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; +import java.io.StringReader; +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) { + return firstNonWsCharUc(searchIn, 0); + } + + public static char firstNonWsCharUc(String searchIn, int startAt) { + if (searchIn == null) { + return 0; + } + + int length = searchIn.length(); + + for (int i = startAt; i < length; i++) { + char c = searchIn.charAt(i); + + if (!Character.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, int offset, int endPos) throws NumberFormatException { + int base = 10; + + int s = offset; + + /* Skip white space. */ + while (Character.isWhitespace((char) buf[s]) && (s < endPos)) { + ++s; + } + + if (s == endPos) { + 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 < endPos; 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 int getInt(byte[] buf) throws NumberFormatException { + return getInt(buf, 0, buf.length); + } + + 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 = -1; + 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 (markerTypeFound != -1 && c == markerCloses.charAt(markerTypeFound) && !escaped) { + contextMarker = Character.MIN_VALUE; + markerTypeFound = -1; + } 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; + // This test looks complex, but remember that in certain locales, upper case + // of two different codepoints coverts to same codepoint, and vice-versa. + } else if ((Character.toUpperCase(c) == Character.toUpperCase(target.charAt(0)) || + Character.toLowerCase(c) == Character.toLowerCase(target.charAt(0))) && !escaped + && contextMarker == Character.MIN_VALUE) { + if (startsWithIgnoreCase(src, i, target)) + return i; + } + } + + 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(); + } + + public static int lastIndexOf(byte[] s, char c) { + if (s == null) { + return -1; + } + + for (int i = s.length - 1; i >= 0; i--) { + if (s[i] == c) { + return i; + } + } + + return -1; + } + + public static int indexOf(byte[] s, char c) { + if (s == null) { + return -1; + } + + int length = s.length; + + for (int i = 0; i < length; i++) { + if (s[i] == c) { + return i; + } + } + + return -1; + } + + /** + * Returns the given string, with comments removed + * + * @param src + * the source string + * @param stringOpens + * characters which delimit the "open" of a string + * @param stringCloses + * characters which delimit the "close" of a string, in + * counterpart order tostringOpens
+ * @param slashStarComments
+ * strip slash-star type "C" style comments
+ * @param slashSlashComments
+ * strip slash-slash C++ style comments to end-of-line
+ * @param hashComments
+ * strip #-style comments to end-of-line
+ * @param dashDashComments
+ * strip "--" style comments to end-of-line
+ * @return the input string with all comment-delimited data removed
+ */
+ public static String stripComments(String src, String stringOpens,
+ String stringCloses, boolean slashStarComments,
+ boolean slashSlashComments, boolean hashComments,
+ boolean dashDashComments) {
+ if (src == null) {
+ return null;
+ }
+
+ StringBuffer buf = new StringBuffer(src.length());
+
+ // It's just more natural to deal with this as a stream
+ // when parsing..This code is currently only called when
+ // parsing the kind of metadata that developers are strongly
+ // recommended to cache anyways, so we're not worried
+ // about the _1_ extra object allocation if it cleans
+ // up the code
+
+ StringReader sourceReader = new StringReader(src);
+
+ int contextMarker = Character.MIN_VALUE;
+ boolean escaped = false;
+ int markerTypeFound = -1;
+
+ int ind = 0;
+
+ int currentChar = 0;
+
+ try {
+ while ((currentChar = sourceReader.read()) != -1) {
+
+ if (false && currentChar == '\\') {
+ escaped = !escaped;
+ } else if (markerTypeFound != -1 && currentChar == stringCloses.charAt(markerTypeFound)
+ && !escaped) {
+ contextMarker = Character.MIN_VALUE;
+ markerTypeFound = -1;
+ } else if ((ind = stringOpens.indexOf(currentChar)) != -1
+ && !escaped && contextMarker == Character.MIN_VALUE) {
+ markerTypeFound = ind;
+ contextMarker = currentChar;
+ }
+
+ if (contextMarker == Character.MIN_VALUE && currentChar == '/'
+ && (slashSlashComments || slashStarComments)) {
+ currentChar = sourceReader.read();
+ if (currentChar == '*' && slashStarComments) {
+ int prevChar = 0;
+ while ((currentChar = sourceReader.read()) != '/'
+ || prevChar != '*') {
+ if (currentChar == '\r') {
+
+ currentChar = sourceReader.read();
+ if (currentChar == '\n') {
+ currentChar = sourceReader.read();
+ }
+ } else {
+ if (currentChar == '\n') {
+
+ currentChar = sourceReader.read();
+ }
+ }
+ if (currentChar < 0)
+ break;
+ prevChar = currentChar;
+ }
+ continue;
+ } else if (currentChar == '/' && slashSlashComments) {
+ while ((currentChar = sourceReader.read()) != '\n'
+ && currentChar != '\r' && currentChar >= 0)
+ ;
+ }
+ } else if (contextMarker == Character.MIN_VALUE
+ && currentChar == '#' && hashComments) {
+ // Slurp up everything until the newline
+ while ((currentChar = sourceReader.read()) != '\n'
+ && currentChar != '\r' && currentChar >= 0)
+ ;
+ } else if (contextMarker == Character.MIN_VALUE
+ && currentChar == '-' && dashDashComments) {
+ currentChar = sourceReader.read();
+
+ if (currentChar == -1 || currentChar != '-') {
+ buf.append('-');
+
+ if (currentChar != -1) {
+ buf.append(currentChar);
+ }
+
+ continue;
+ }
+
+ // Slurp up everything until the newline
+
+ while ((currentChar = sourceReader.read()) != '\n'
+ && currentChar != '\r' && currentChar >= 0)
+ ;
+ }
+
+ if (currentChar != -1) {
+ buf.append((char) currentChar);
+ }
+ }
+ } catch (IOException ioEx) {
+ // we'll never see this from a StringReader
+ }
+
+ return buf.toString();
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/TimeUtil.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/TimeUtil.java
new file mode 100644
index 00000000..46685296
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/TimeUtil.java
@@ -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();
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/UpdatableResultSet.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/UpdatableResultSet.java
new file mode 100644
index 00000000..df1bb879
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/UpdatableResultSet.java
@@ -0,0 +1,2506 @@
+/*
+ Copyright (C) 2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc;
+
+import 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;
+
+ /** Reason the result set is not updatable */
+ private String notUpdatableReason = null;
+
+ /** 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;
+
+ private boolean populateInserterWithDefaultValues = false;
+
+ /**
+ * 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();
+ this.populateInserterWithDefaultValues =
+ this.connection.getPopulateInsertRowWithDefaultValues();
+ }
+
+ /**
+ * JDBC 2.0
+ *
+ * + * Move to an absolute row number in the result set. + *
+ * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + *+ * Note: Calling absolute(1) is the same as calling first(). Calling + * absolute(-1) is the same as calling last(). + *
+ * + * @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 + * + *+ * Moves to the end of the result set, just after the last row. Has no + * effect if the result set contains no rows. + *
+ * + * @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 + * + *+ * Moves to the front of the result set, just before the first row. Has no + * effect if the result set contains no rows. + *
+ * + * @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! + */ + protected void checkUpdatability() throws SQLException { + if (this.fields == null) { + // we've been created to be populated with cached + // metadata, and we don't have the metadata yet, + // we'll be called again by + // Connection.initializeResultsMetadataFromCache() + // when the metadata has been made available + + return; + } + + String singleTableName = null; + String catalogName = null; + + int primaryKeyCount = 0; + + // We can only do this if we know that there is a currently + // selected database, or if we're talking to a > 4.1 version + // of MySQL server (as it returns database names in field + // info) + // + if ((this.catalog == null) || (this.catalog.length() == 0)) { + this.catalog = this.fields[0].getDatabaseName(); + + if ((this.catalog == null) || (this.catalog.length() == 0)) { + throw SQLError.createSQLException(Messages + .getString("UpdatableResultSet.43") //$NON-NLS-1$ + , SQLError.SQL_STATE_ILLEGAL_ARGUMENT); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + if (this.fields.length > 0) { + singleTableName = this.fields[0].getOriginalTableName(); + catalogName = this.fields[0].getDatabaseName(); + + if (singleTableName == null) { + singleTableName = this.fields[0].getTableName(); + catalogName = this.catalog; + } + + if (singleTableName != null && singleTableName.length() == 0) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); + + return; + } + + if (this.fields[0].isPrimaryKey()) { + primaryKeyCount++; + } + + // + // 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 (otherTableName != null && otherTableName.length() == 0) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); + + return; + } + + if ((singleTableName == null) + || !otherTableName.equals(singleTableName)) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.0"); + + return; + } + + // Can't reference more than one database + if ((catalogName == null) + || !otherCatalogName.equals(catalogName)) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.1"); + + return; + } + + if (this.fields[i].isPrimaryKey()) { + primaryKeyCount++; + } + } + + if ((singleTableName == null) || (singleTableName.length() == 0)) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.2"); + + return; + } + } else { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.3"); + + return; + } + + 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; + } + } + + int existingPrimaryKeysCount = primaryKeyNames.size(); + + if (existingPrimaryKeysCount == 0) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.5"); + + return; // we can't update tables w/o keys + } + + // + // 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; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.6", + new Object[] {originalName}); + + return; + } + } + } + } + } + + this.isUpdatable = primaryKeyNames.isEmpty(); + + if (!this.isUpdatable) { + if (existingPrimaryKeysCount > 1) { + this.notUpdatableReason = Messages.getString("NotUpdatableReason.7"); + } else { + this.notUpdatableReason = Messages.getString("NotUpdatableReason.4"); + } + + return; + } + } + + // + // Must have at least one primary key + // + if (primaryKeyCount == 0) { + this.isUpdatable = false; + this.notUpdatableReason = Messages.getString("NotUpdatableReason.4"); + + return; + } + + this.isUpdatable = true; + this.notUpdatableReason = null; + + return; + } + + /** + * 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(this.notUpdatableReason); + } + + 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 + * + *+ * Moves to the first row in the result set. + *
+ * + * @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(this.notUpdatableReason); + } + + 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.inserter.setBytesNoEscapeNoQuotes(i + 1, newRow[i]); + } + } + + refreshRow(this.inserter, newRow); + + this.rowData.addRow(newRow); + resetInserter(); + } + + + /** + * JDBC 2.0 + * + *+ * Determine if the cursor is after the last row in the result set. + *
+ * + * @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 + * + *+ * Determine if the cursor is before the first row in the result set. + *
+ * + * @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 + * + *+ * Determine if the cursor is on the first row of the result set. + *
+ * + * @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 + * + *+ * 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. + *
+ * + * @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 + * + *+ * Moves to the last row in the result set. + *
+ * + * @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(this.notUpdatableReason); + } + + 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(this.notUpdatableReason); + } + + if (this.inserter == null) { + if (this.insertSQL == null) { + generateStatements(); + } + + this.inserter = this.connection + .clientPrepareStatement(this.insertSQL); + if (this.populateInserterWithDefaultValues) { + 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.populateInserterWithDefaultValues) { + this.inserter.setBytesNoEscapeNoQuotes(i + 1, + "DEFAULT".getBytes()); + this.thisRow[i] = null; + } else { + if (this.defaultColumnValue[i] != null) { + Field f = this.fields[i]; + + switch (f.getMysqlType()) { + case MysqlDefs.FIELD_TYPE_DATE: + case MysqlDefs.FIELD_TYPE_DATETIME: + case MysqlDefs.FIELD_TYPE_NEWDATE: + case MysqlDefs.FIELD_TYPE_TIME: + case MysqlDefs.FIELD_TYPE_TIMESTAMP: + + 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; + } + } + } + } + + /** + * 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. + * + *+ * 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 + *
+ * + * @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. + * + *+ * 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 + *
+ * + * @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 + * + *+ * Moves to the previous row in the result set. + *
+ * + *+ * Note: previous() is not the same as relative(-1) since it makes sense to + * call previous() when there is no current row. + *
+ * + * @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, Constants.MILLIS_I18N, + 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$ + } + + refreshRow(this.updater, this.thisRow); + } + + private synchronized void refreshRow(PreparedStatement updateInsertStmt, + Object[] rowToRefresh) throws SQLException { + 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 && !this.onInsertRow) { + dataFrom = (byte[]) rowToRefresh[index]; + } else { + dataFrom = updateInsertStmt.getBytesRepresentation(index); + + // Primary keys not set? + if (updateInsertStmt.isNull(index) || (dataFrom.length == 0)) { + dataFrom = (byte[]) rowToRefresh[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 && !this.onInsertRow) { + dataFrom = (byte[]) rowToRefresh[index]; + } else { + dataFrom = updateInsertStmt.getBytesRepresentation(index); + + // Primary keys not set? + if (updateInsertStmt.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()) { + rowToRefresh[i] = null; + } else { + rowToRefresh[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 + * + *+ * 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. + *
+ * + *+ * 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. + *
+ * + * @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(this.notUpdatableReason); + } + + 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); + } +} \ No newline at end of file diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Util.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Util.java new file mode 100644 index 00000000..1df01186 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/Util.java @@ -0,0 +1,372 @@ +/* + Copyright (C) 2002-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ +package com.mysql.jdbc; + +import java.io.ObjectInputStream; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.TimeZone; + +/** + * Various utility methods for the driver. + * + * @author Mark Matthews + */ +public class Util { + + protected static Method systemNanoTimeMethod; + + private static boolean isColdFusion = false; + + static { + try { + systemNanoTimeMethod = System.class.getMethod("nanoTime", null); + } catch (SecurityException e) { + systemNanoTimeMethod = null; + } catch (NoSuchMethodException e) { + systemNanoTimeMethod = null; + } + + // + // Detect the ColdFusion MX environment + // + // Unfortunately, no easy-to-discern classes are available + // to our classloader to check... + // + + String loadedFrom = stackTraceToString(new Throwable()); + + if (loadedFrom != null) { + isColdFusion = loadedFrom.indexOf("coldfusion") != -1; + } else { + isColdFusion = false; + } + } + + public static boolean isColdFusion() { + return isColdFusion; + } + + protected static boolean nanoTimeAvailable() { + return systemNanoTimeMethod != null; + } + + // cache this ourselves, as the method call is statically-synchronized in all but JDK6! + + private static final TimeZone DEFAULT_TIMEZONE = TimeZone.getDefault(); + + static final TimeZone getDefaultTimeZone() { + return (TimeZone)DEFAULT_TIMEZONE.clone(); + } + + class RandStructcture { + long maxValue; + + double maxValueDbl; + + long seed1; + + long seed2; + } + + + 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(); + } + + /** + * Does a network interface exist locally with the given hostname? + * + * @param hostname the hostname (or IP address in string form) to check + * @return true if it exists, false if no, or unable to determine due to VM version support + * of java.net.NetworkInterface + */ + public static boolean interfaceExists(String hostname) { + try { + Class networkInterfaceClass = Class.forName("java.net.NetworkInterface"); + return networkInterfaceClass.getMethod("getByName", null).invoke(networkInterfaceClass, new Object[] { hostname }) != null; + } catch (Throwable t) { + return false; + } + } + + public static long getCurrentTimeNanosOrMillis() { + if (systemNanoTimeMethod != null) { + try { + return ((Long)systemNanoTimeMethod.invoke(null, null)).longValue(); + } catch (IllegalArgumentException e) { + // ignore - fall through to currentTimeMillis() + } catch (IllegalAccessException e) { + // ignore - fall through to currentTimeMillis() + } catch (InvocationTargetException e) { + // ignore - fall through to currentTimeMillis() + } + } + + return System.currentTimeMillis(); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WatchableOutputStream.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WatchableOutputStream.java new file mode 100644 index 00000000..67ad5e83 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WatchableOutputStream.java @@ -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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WatchableWriter.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WatchableWriter.java new file mode 100644 index 00000000..d8dddc75 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WatchableWriter.java @@ -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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WriterWatcher.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WriterWatcher.java new file mode 100644 index 00000000..569ea218 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/WriterWatcher.java @@ -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); +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/3-0-Compat.properties b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/3-0-Compat.properties new file mode 100644 index 00000000..34187eac --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/3-0-Compat.properties @@ -0,0 +1,18 @@ +# +# 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 +useFastDateParsing=false +populateInsertRowWithDefaultValues=false \ No newline at end of file diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/clusterBase.properties b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/clusterBase.properties new file mode 100644 index 00000000..e87a6046 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/clusterBase.properties @@ -0,0 +1,4 @@ +# Basic properties for clusters +autoReconnect=true +failOverReadOnly=false +roundRobinLoadBalance=true \ No newline at end of file diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/coldFusion.properties b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/coldFusion.properties new file mode 100644 index 00000000..22bdec5c --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/coldFusion.properties @@ -0,0 +1,25 @@ +# +# Properties for optimal usage in ColdFusion +# +# Automagically pulled in if "autoConfigureForColdFusion" is "true" +# which is the default configuration of the driver +# + +# +# CF uses a _lot_ of RSMD.isCaseSensitive() - this optimizes it +# + +useDynamicCharsetInfo=false + +# +# CF's pool tends to be "chatty" like DBCP +# + +alwaysSendSetIsolation=false +useLocalSessionState=true + +# +# CF's pool seems to loose connectivity on page restart +# + +autoReconnect=true diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/fullDebug.properties b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/fullDebug.properties new file mode 100644 index 00000000..c1fcdc83 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/fullDebug.properties @@ -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 diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/maxPerformance.properties b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/maxPerformance.properties new file mode 100644 index 00000000..38d9071c --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/maxPerformance.properties @@ -0,0 +1,29 @@ +# +# 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 + +# Can cause high-GC pressure if timeouts are used on every +# query +enableQueryTimeouts=false \ No newline at end of file diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties new file mode 100644 index 00000000..b4b31a13 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/configs/solarisMaxPerformance.properties @@ -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 diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLDataException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLDataException.java new file mode 100644 index 00000000..53a6821c --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLDataException.java @@ -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); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java new file mode 100644 index 00000000..fb6529f8 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLIntegrityConstraintViolationException.java @@ -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); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java new file mode 100644 index 00000000..dde410ff --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLInvalidAuthorizationSpecException.java @@ -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); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java new file mode 100644 index 00000000..447b3fee --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLNonTransientConnectionException.java @@ -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); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java new file mode 100644 index 00000000..8e38ae2c --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLNonTransientException.java @@ -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); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java new file mode 100644 index 00000000..6a52aa39 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLSyntaxErrorException.java @@ -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); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java new file mode 100644 index 00000000..ca8460b9 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTimeoutException.java @@ -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(); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java new file mode 100644 index 00000000..cb620e01 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransactionRollbackException.java @@ -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(); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java new file mode 100644 index 00000000..a139cd5e --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransientConnectionException.java @@ -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(); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransientException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransientException.java new file mode 100644 index 00000000..f72b2ed2 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/exceptions/MySQLTransientException.java @@ -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(); + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java new file mode 100644 index 00000000..f2ca9256 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/c3p0/MysqlConnectionTester.java @@ -0,0 +1,134 @@ +/* + 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) { + try { + if (pingMethod != null) { + if (con instanceof com.mysql.jdbc.Connection) { + // We've been passed an instance of a MySQL connection -- + // no need for reflection + ((com.mysql.jdbc.Connection) con).ping(); + } else { + // Assume the connection is a C3P0 proxy + C3P0ProxyConnection castCon = (C3P0ProxyConnection) con; + castCon.rawConnectionOperation(pingMethod, + C3P0ProxyConnection.RAW_CONNECTION, NO_ARGS_ARRAY); + } + } else { + Statement pingStatement = null; + + 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; + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java new file mode 100644 index 00000000..c9ab3f3c --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/jboss/ExtendedMysqlExceptionSorter.java @@ -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); + } + +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java new file mode 100644 index 00000000..06a792cd --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/integration/jboss/MysqlValidConnectionChecker.java @@ -0,0 +1,129 @@ +/* + 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, null); + + 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, null); + + 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 = conn.createStatement(); + + 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 + } + } + } + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java new file mode 100644 index 00000000..fd24fc34 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/CallableStatementWrapper.java @@ -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; + } + +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java new file mode 100644 index 00000000..f8e552fb --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/ConnectionWrapper.java @@ -0,0 +1,839 @@ +/* + 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(). + * + *+ * 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. + *
+ * + *+ * All sqlExceptions thrown by the physical connection are intercepted and sent + * to connectionEvent listeners before being thrown to client. + *
+ * + * @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); + } + } + + /** + * 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(); + } + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java new file mode 100644 index 00000000..242f62d9 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlConnectionPoolDataSource.java @@ -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 WolffReferenceable
.
+ *
+ * @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);
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java
new file mode 100644
index 00000000..615b19c3
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlDataSourceFactory.java
@@ -0,0 +1,151 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package com.mysql.jdbc.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";
+
+ /**
+ * The class name for a MysqlXADataSource
+ */
+
+ protected final static String XA_DATA_SOURCE_CLASS_NAME = "com.mysql.jdbc.jdbc2.optional.MysqlXADataSource";
+
+ /**
+ * DOCUMENT ME!
+ *
+ * @param refObj
+ * 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) ||
+ className.equals(XA_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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java
new file mode 100644
index 00000000..ff41d796
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlPooledConnection.java
@@ -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 XAConnection
object may be enlisted in a distributed
+ * transaction by means of an XAResource
object. A transaction
+ * manager, usually part of a middle tier server, manages an
+ * XAConnection
object through the XAResource
+ * object.
+ *
+ * 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;
+
+ protected boolean logXaCommands;
+
+ static {
+ HashMap temp = new HashMap();
+
+ temp.put(new Integer(1397), new Integer(XAException.XAER_NOTA));
+ temp.put(new Integer(1398), new Integer(XAException.XAER_INVAL));
+ temp.put(new Integer(1399), new Integer(XAException.XAER_RMFAIL));
+ temp.put(new Integer(1400), new Integer(XAException.XAER_OUTSIDE));
+ temp.put(new Integer(1401), new Integer(XAException.XAER_RMERR));
+ temp.put(new Integer(1402), new Integer(XAException.XA_RBROLLBACK));
+
+ MYSQL_ERROR_CODES_TO_XA_ERROR_CODES = Collections.unmodifiableMap(temp);
+ }
+
+ /**
+ * @param connection
+ */
+ public MysqlXAConnection(com.mysql.jdbc.Connection connection, boolean logXaCommands)
+ throws SQLException {
+ super(connection);
+ this.underlyingConnection = connection;
+ this.log = connection.getLog();
+ this.logXaCommands = logXaCommands;
+ }
+
+ /**
+ * Retrieves an XAResource
object that the transaction
+ * manager will use to manage this XAConnection
object's
+ * participation in a distributed transaction.
+ *
+ * @return the XAResource
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.logXaCommands) {
+ 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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java
new file mode 100644
index 00000000..a7c2a26c
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXADataSource.java
@@ -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, getLogXaCommands());
+ }
+}
\ No newline at end of file
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java
new file mode 100644
index 00000000..154a48fd
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXAException.java
@@ -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
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java
new file mode 100644
index 00000000..51e5d6ea
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/MysqlXid.java
@@ -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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java
new file mode 100644
index 00000000..390b7016
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/PreparedStatementWrapper.java
@@ -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, 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.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
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java
new file mode 100644
index 00000000..fe70e8ad
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/StatementWrapper.java
@@ -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);
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java
new file mode 100644
index 00000000..b3b49cf0
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/SuspendableXAConnection.java
@@ -0,0 +1,154 @@
+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,
+ connectionToWrap.getLogXaCommands());
+ }
+
+ 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();
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java
new file mode 100644
index 00000000..282175ed
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/jdbc2/optional/WrapperBase.java
@@ -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
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/CommonsLogger.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/CommonsLogger.java
new file mode 100644
index 00000000..8388510d
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/CommonsLogger.java
@@ -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);
+ }
+
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Jdk14Logger.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Jdk14Logger.java
new file mode 100644
index 00000000..1fe93ed4
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Jdk14Logger.java
@@ -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);
+ }
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Log.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Log.java
new file mode 100644
index 00000000..80a48c4c
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Log.java
@@ -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
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Log4JLogger.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Log4JLogger.java
new file mode 100644
index 00000000..d2dc355a
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/Log4JLogger.java
@@ -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);
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/LogFactory.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/LogFactory.java
new file mode 100644
index 00000000..d5c869e5
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/LogFactory.java
@@ -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);
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/LogUtils.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/LogUtils.java
new file mode 100644
index 00000000..c1c51e4f
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/LogUtils.java
@@ -0,0 +1,171 @@
+/*
+ Copyright (C) 2005-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ 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;
+
+ case ProfilerEvent.TYPE_SLOW_QUERY:
+ msgBuf.append("SLOW QUERY");
+ appendLocationInfo = false;
+
+ break;
+
+ default:
+ msgBuf.append("UNKNOWN");
+ }
+
+ msgBuf.append("] ");
+ msgBuf.append(findCallingClassAndMethod(locationException));
+ msgBuf.append(" duration: ");
+ msgBuf.append(evt.getEventDuration());
+ msgBuf.append(" ");
+ msgBuf.append(evt.getDurationUnits());
+ msgBuf.append(", connection-id: ");
+ msgBuf.append(evt.getConnectionId());
+ msgBuf.append(", statement-id: ");
+ msgBuf.append(evt.getStatementId());
+ 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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/NullLogger.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/NullLogger.java
new file mode 100644
index 00000000..44030266
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/NullLogger.java
@@ -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
+
+ }
+
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/StandardLogger.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/StandardLogger.java
new file mode 100644
index 00000000..fcc15867
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/log/StandardLogger.java
@@ -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);
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/profiler/ProfileEventSink.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/profiler/ProfileEventSink.java
new file mode 100644
index 00000000..4fe8fcbe
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/profiler/ProfileEventSink.java
@@ -0,0 +1,91 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package com.mysql.jdbc.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");
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/profiler/ProfilerEvent.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/profiler/ProfilerEvent.java
new file mode 100644
index 00000000..09646fcc
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/profiler/ProfilerEvent.java
@@ -0,0 +1,540 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ */
+
+package com.mysql.jdbc.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;
+
+ /**
+ * Profiler event for slow query
+ */
+ public static final byte TYPE_SLOW_QUERY = 6;
+
+ /**
+ * 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 long eventDuration;
+
+ /**
+ * What units was the duration measured in?
+ */
+ protected String durationUnits;
+
+ /**
+ * The hostname the event occurred on (as an index into a dictionary, used
+ * by 'remote' profilers for efficiency)?
+ */
+ 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, long eventDuration, String durationUnits,
+ String eventCreationPointDesc, Throwable eventCreationPoint,
+ String message) {
+ this.eventType = eventType;
+ this.connectionId = connectionId;
+ this.statementId = statementId;
+ this.resultSetId = resultSetId;
+ this.eventCreationTime = eventCreationTime;
+ this.eventDuration = eventDuration;
+ this.durationUnits = durationUnits;
+ 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;
+ case TYPE_SLOW_QUERY:
+ buf.append("SLOW QUERY");
+ break;
+ default:
+ buf.append("UNKNOWN");
+ }
+
+ buf.append(" created: ");
+ buf.append(new Date(this.eventCreationTime));
+ buf.append(" duration: ");
+ buf.append(this.eventDuration);
+ 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;
+ long eventDuration = readLong(buf, pos);
+ pos += 4;
+
+ byte[] eventDurationUnits = readBytes(buf, pos);
+ pos += 4;
+
+ if (eventDurationUnits != null) {
+ pos += eventDurationUnits.length;
+ }
+
+ int eventCreationPointIndex = readInt(buf, pos);
+ pos += 4;
+ byte[] eventCreationAsBytes = readBytes(buf, pos);
+ 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, eventDuration,
+ new String(eventDurationUnits, "ISO8859_1"),
+ 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[] durationUnitsAsBytes = null;
+
+ if (durationUnits != null) {
+ durationUnitsAsBytes = this.durationUnits.getBytes("ISO8859_1");
+ len += (4 + durationUnitsAsBytes.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 = writeLong(this.eventDuration, buf, pos);
+ pos = writeBytes(durationUnitsAsBytes, 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 long getEventDuration() {
+ return this.eventDuration;
+ }
+
+ /**
+ * Returns the units for getEventDuration()
+ */
+ public String getDurationUnits() {
+ return this.durationUnits;
+ }
+
+ /**
+ * Returns the event type flag
+ *
+ * @return the event type flag
+ */
+ 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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/trace/Tracer.aj b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/trace/Tracer.aj
new file mode 100644
index 00000000..ec24be39
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/trace/Tracer.aj
@@ -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(")");
+ }
+
+}
+
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/BaseBugReport.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/BaseBugReport.java
new file mode 100644
index 00000000..42e21f4a
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/BaseBugReport.java
@@ -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.
+ *
+ *
+ * MySQL AB + *
+ * 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'. + * + *
+ * 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, which will use + * the default JDBC URL of 'jdbc:mysql:///test'. + * + *
+ * If you need to use a JDBC URL that is different than 'jdbc:mysql:///test', + * then override the method 'getUrl' as well. + * + *
+ * 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). + * + *
+ * 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(); + * } + *+ * + *
+ * 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
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java
new file mode 100644
index 00000000..ebb8cb30
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ErrorMappingsDocGenerator.java
@@ -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();
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/LRUCache.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/LRUCache.java
new file mode 100644
index 00000000..18406687
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/LRUCache.java
@@ -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);
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/PropertiesDocGenerator.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/PropertiesDocGenerator.java
new file mode 100644
index 00000000..8299ce48
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/PropertiesDocGenerator.java
@@ -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());
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ReadAheadInputStream.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ReadAheadInputStream.java
new file mode 100644
index 00000000..3e719cf0
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ReadAheadInputStream.java
@@ -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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ResultSetUtil.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ResultSetUtil.java
new file mode 100644
index 00000000..2009ceeb
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ResultSetUtil.java
@@ -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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ServerController.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ServerController.java
new file mode 100644
index 00000000..ec0ca85e
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/ServerController.java
@@ -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;
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/TimezoneDump.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/TimezoneDump.java
new file mode 100644
index 00000000..2a9a5dd4
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/TimezoneDump.java
@@ -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);
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java
new file mode 100644
index 00000000..64c276a2
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/com/mysql/jdbc/util/VersionFSHierarchyMaker.java
@@ -0,0 +1,133 @@
+/*
+ Copyright (C) 2005-2006 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ 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;
+
+ String jvmVersion = removeWhitespaceChars(System.getProperty("java.version"));
+ String jvmVendor = removeWhitespaceChars(System.getProperty("java.vendor"));
+ String osName = removeWhitespaceChars(System.getProperty("os.name"));
+ String osArch = removeWhitespaceChars(System.getProperty("os.arch"));
+ String osVersion = removeWhitespaceChars(System.getProperty("os.version"));
+
+ jdbcUrl = System.getProperty("com.mysql.jdbc.testsuite.url");
+
+ String mysqlVersion = "not-available";
+
+ try {
+ Connection conn = new NonRegisteringDriver().connect(jdbcUrl, null);
+
+ ResultSet rs = conn.createStatement().executeQuery("SELECT VERSION()");
+ rs.next();
+ mysqlVersion = removeWhitespaceChars(rs.getString(1));
+ } catch (Throwable t) {
+ mysqlVersion = "no-server-running-on-" + removeWhitespaceChars(jdbcUrl);
+ }
+
+ String jvmSubdirName = jvmVendor + "-" + jvmVersion;
+ String osSubdirName = osName + "-" + osArch + "-" + osVersion;
+
+ 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");
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/doc/sources/connPropsToDocbook.xsl b/src/lib/mysql-connector-java-5.0.8/src/doc/sources/connPropsToDocbook.xsl
new file mode 100644
index 00000000..c80b3307
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/doc/sources/connPropsToDocbook.xsl
@@ -0,0 +1,94 @@
+
+
This is a binary-only release. Source code +is available from +http://eclipse.org/aspectj
+ +The AspectJ compiler and core tools are distributed under the +Common Public License version 1.0 available + here + and copied below. + This license has been approved by +the Open Source Initiative as +conforming to the Open +Source Definition. +More information about the history and rationale behind this license +can be found at the + eclipse web site.
+ +
+Those portions of this distribution in the org.apache
+Java namespace are available under the terms of
+the Apache Software License, Version 1.1
+(See
+ http://jakarta.apache.org).
+
Common Public License - v 1.0 +
+
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. +
+
1. DEFINITIONS +
"Contribution" means: + +
+
"Contributor" means any person or entity that distributes the Program. +
+
"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. +
+
"Program" means the Contributions distributed in accordance with this Agreement. +
+
"Recipient" means anyone who receives the Program under this Agreement, including all Contributors. +
+
2. GRANT OF RIGHTS + +
3. REQUIREMENTS +
A Contributor may choose to distribute the Program in object code form under its own license agreement, provided that: + +
When the Program is made available in source code form: + +
+
Contributors may not remove or alter any copyright notices contained within the Program.
+
+
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. +
+
4. COMMERCIAL DISTRIBUTION +
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. +
+
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. +
+
5. NO WARRANTY +
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 solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. +
+
6. DISCLAIMER OF LIABILITY +
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 (INCLUDING WITHOUT LIMITATION LOST PROFITS), 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. +
+
7. GENERAL +
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. +
+
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. +
+
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. +
+
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 publish new versions (including revisions) of this Agreement from time to 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. 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 version. 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, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. +
+
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. +
+
+ +
numIterations
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
+ * additionalMessage
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");
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/testsuite/perf/LoadStorePerfTest.java b/src/lib/mysql-connector-java-5.0.8/src/testsuite/perf/LoadStorePerfTest.java
new file mode 100644
index 00000000..673589f6
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/testsuite/perf/LoadStorePerfTest.java
@@ -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;
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/testsuite/perf/RetrievalPerfTest.java b/src/lib/mysql-connector-java-5.0.8/src/testsuite/perf/RetrievalPerfTest.java
new file mode 100644
index 00000000..62d25c49
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/testsuite/perf/RetrievalPerfTest.java
@@ -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);
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/AppletRegressionTest.java b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/AppletRegressionTest.java
new file mode 100644
index 00000000..18112684
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/AppletRegressionTest.java
@@ -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) {
+ }
+ }
+}
diff --git a/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/BlobRegressionTest.java b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/BlobRegressionTest.java
new file mode 100644
index 00000000..e26ca82f
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/BlobRegressionTest.java
@@ -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
diff --git a/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/CachedRowsetTest.java b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/CachedRowsetTest.java
new file mode 100644
index 00000000..1f37e81d
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/CachedRowsetTest.java
@@ -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
diff --git a/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/CallableStatementRegressionTest.java b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/CallableStatementRegressionTest.java
new file mode 100644
index 00000000..9a4f13f1
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/CallableStatementRegressionTest.java
@@ -0,0 +1,1248 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.InputStream;
+import java.sql.CallableStatement;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.List;
+import java.util.Properties;
+
+import com.mysql.jdbc.DatabaseMetaData;
+import com.mysql.jdbc.NonRegisteringDriver;
+import com.mysql.jdbc.SQLError;
+import com.mysql.jdbc.StringUtils;
+
+import testsuite.BaseTestCase;
+
+/**
+ * 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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ 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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+ try {
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug3540");
+ this.stmt
+ .executeUpdate("CREATE PROCEDURE testBug3540(x int, out y int)\n"
+ + "BEGIN\n" + "SELECT 1;" + "end\n");
+
+ this.rs = this.conn.getMetaData().getProcedureColumns(null,
+ null, "testBug3540%", "%");
+
+ 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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ 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 {
+ if (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ boolean doASelect = true; // SELECT currently causes the server to
+ // hang on the
+ // last execution of this testcase, filed as BUG#9405
+
+
+ if (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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ 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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ 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.INTEGER, 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.INTEGER, cStmt.getParameterMetaData().getParameterType(1));
+ assertEquals(Types.INTEGER, cStmt.getParameterMetaData().getParameterType(2));
+ }
+ } finally {
+ if (this.rs != null) {
+ this.rs.close();
+ this.rs = null;
+ }
+
+ if (cStmt != null) {
+ cStmt.close();
+ }
+
+ this.stmt.executeUpdate("DROP FUNCTION IF EXISTS testBug10310");
+ }
+ }
+
+ /**
+ * Tests fix for Bug#12417 - stored procedure catalog name is case-sensitive
+ * on Windows (this is actually a server bug, but we have a workaround in
+ * place for it now).
+ *
+ * @throws Exception
+ * if the test fails.
+ */
+ public void testBug12417() throws Exception {
+ if (serverSupportsStoredProcedures() && 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((Properties)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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+ CallableStatement storedProc = null;
+
+ try {
+ this.stmt
+ .executeUpdate("DROP PROCEDURE IF EXISTS testInOutParam");
+ this.stmt
+ .executeUpdate("create procedure testInOutParam(IN p1 VARCHAR(255), INOUT p2 INT)\n"
+ + "begin\n"
+ + " DECLARE z INT;\n"
+ + "SET z = p2 + 1;\n"
+ + "SET p2 = z;\n"
+ + "SELECT p1;\n"
+ + "SELECT CONCAT('zyxw', p1);\n" + "end\n");
+
+ 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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ 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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ CallableStatement cstmt = null;
+
+ try {
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug21462");
+ this.stmt.executeUpdate("CREATE PROCEDURE testBug21462() BEGIN SELECT 1; END");
+ cstmt = this.conn.prepareCall("{CALL testBug21462}");
+ cstmt.execute();
+ } finally {
+ if (cstmt != null) {
+ cstmt.close();
+ }
+
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug21462");
+ }
+
+ }
+
+ /**
+ * Tests fix for BUG#22024 - Newlines causing whitespace to span confuse
+ * procedure parser when getting parameter metadata for stored procedures.
+ *
+ * @throws Exception if the test fails
+ */
+ public void testBug22024() throws Exception {
+ if (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ CallableStatement cstmt = null;
+
+ try {
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
+ this.stmt.executeUpdate("CREATE PROCEDURE testBug22024(\r\n)\r\n BEGIN SELECT 1; END");
+ cstmt = this.conn.prepareCall("{CALL testBug22024()}");
+ cstmt.execute();
+
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
+ this.stmt.executeUpdate("CREATE PROCEDURE testBug22024(\r\na INT)\r\n BEGIN SELECT 1; END");
+ cstmt = this.conn.prepareCall("{CALL testBug22024(?)}");
+ cstmt.setInt(1, 1);
+ cstmt.execute();
+ } finally {
+ if (cstmt != null) {
+ cstmt.close();
+ }
+
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug22024");
+ }
+
+ }
+
+ /**
+ * Tests workaround for server crash when calling stored procedures
+ * via a server-side prepared statement (driver now detects
+ * prepare(stored procedure) and substitutes client-side prepared statement).
+ *
+ * @throws Exception if the test fails
+ */
+ public void testBug22297() throws Exception {
+ if (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ 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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ 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 (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+
+ this.stmt.executeUpdate("Drop procedure if exists p");
+ this.stmt
+ .executeUpdate("create procedure p () begin select 1; select 2; end;");
+ PreparedStatement ps = null;
+
+ try {
+ ps = this.conn.prepareStatement("call p()");
+
+ ps.execute();
+ this.rs = ps.getResultSet();
+ assertTrue(this.rs.next());
+ assertEquals(1, this.rs.getInt(1));
+ assertTrue(ps.getMoreResults());
+ this.rs = ps.getResultSet();
+ assertTrue(this.rs.next());
+ assertEquals(2, this.rs.getInt(1));
+ assertTrue(!ps.getMoreResults());
+ } finally {
+ if (this.rs != null) {
+ this.rs.close();
+ this.rs = null;
+ }
+
+ if (ps != null) {
+ ps.close();
+ }
+ }
+
+ }
+
+ /**
+ * Tests fix for BUG#25379 - INOUT parameters in CallableStatements get doubly-escaped.
+ *
+ * @throws Exception if the test fails.
+ */
+ public void testBug25379() throws Exception {
+ if (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ createTable("testBug25379", "(col char(40))");
+
+ try {
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS sp_testBug25379");
+ this.stmt.executeUpdate("CREATE PROCEDURE sp_testBug25379 (INOUT invalue char(255))"
+ + "\nBEGIN"
+ + "\ninsert into testBug25379(col) values(invalue);"
+ + "\nEND");
+
+
+ CallableStatement cstmt = this.conn.prepareCall("{call sp_testBug25379(?)}");
+ cstmt.setString(1,"'john'");
+ cstmt.executeUpdate();
+ assertEquals("'john'", cstmt.getString(1));
+ assertEquals("'john'", getSingleValue("testBug25379", "col", "").toString());
+ } finally {
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS sp_testBug25379");
+ }
+ }
+
+ /**
+ * Tests fix for BUG#25715 - CallableStatements with OUT/INOUT parameters that
+ * are "binary" have extra 7 bytes (which happens to be the _binary introducer!)
+ *
+ * @throws Exception if the test fails.
+ */
+ public void testBug25715() throws Exception {
+ if (!serverSupportsStoredProcedures()) {
+ return; // no stored procs
+ }
+
+ if (isRunningOnJdk131()) {
+ return; // no such method to test
+ }
+
+ createProcedure("spbug25715", "(INOUT mblob MEDIUMBLOB)" + "BEGIN"
+ + " SELECT 1 FROM DUAL WHERE 1=0;" + "\nEND");
+ CallableStatement cstmt = null;
+
+ try {
+ cstmt = this.conn.prepareCall("{call spbug25715(?)}");
+
+ byte[] buf = new byte[65];
+ for (int i = 0; i < 65; i++)
+ buf[i] = 1;
+ int il = buf.length;
+
+ int[] typesToTest = new int[] { Types.BIT, Types.BINARY, Types.BLOB, Types.JAVA_OBJECT,
+ Types.LONGVARBINARY, Types.VARBINARY };
+
+ for (int i = 0; i < typesToTest.length; i++) {
+
+ cstmt.setBinaryStream("mblob", new ByteArrayInputStream(buf),
+ buf.length);
+ cstmt.registerOutParameter("mblob", typesToTest[i]);
+
+ cstmt.executeUpdate();
+
+ InputStream is = cstmt.getBlob("mblob").getBinaryStream();
+ ByteArrayOutputStream bOut = new ByteArrayOutputStream();
+
+ int bytesRead = 0;
+ byte[] readBuf = new byte[256];
+
+ while ((bytesRead = is.read(readBuf)) != -1) {
+ bOut.write(readBuf, 0, bytesRead);
+ }
+
+ byte[] fromSelectBuf = bOut.toByteArray();
+
+ int ol = fromSelectBuf.length;
+
+ assertEquals(il, ol);
+ }
+
+ cstmt.close();
+ } finally {
+ closeMemberJDBCResources();
+
+ if (cstmt != null) {
+ cstmt.close();
+ }
+ }
+
+ }
+
+ protected boolean serverSupportsStoredProcedures() throws SQLException {
+ return versionMeetsMinimum(5, 0);
+ }
+
+ public void testBug26143() throws Exception {
+ if (!serverSupportsStoredProcedures()) {
+ return; // no stored procedure support
+ }
+
+ this.stmt.executeUpdate("DROP PROCEDURE IF EXISTS testBug26143");
+
+ this.stmt.executeUpdate("CREATE DEFINER=CURRENT_USER PROCEDURE testBug26143(I INT) COMMENT 'abcdefg'"
+ + "\nBEGIN\n"
+ + "SELECT I * 10;"
+ + "\nEND");
+
+ this.conn.prepareCall("{call testBug26143(?)").close();
+ }
+
+ /**
+ * Tests fix for BUG#26959 - comments confuse procedure parser.
+ *
+ * @throws Exception if the test fails
+ */
+ public void testBug26959() throws Exception {
+ if (!serverSupportsStoredProcedures()) {
+ return;
+ }
+
+ createProcedure(
+ "testBug26959",
+ "(_ACTION varchar(20),"
+ + "\n`/*dumb-identifier-1*/` int,"
+ + "\n`#dumb-identifier-2` int,"
+ + "\n`--dumb-identifier-3` int,"
+ + "\n_CLIENT_ID int, -- ABC"
+ + "\n_LOGIN_ID int, # DEF"
+ + "\n_WHERE varchar(2000),"
+ + "\n_SORT varchar(2000),"
+ + "\n out _SQL varchar(/* inline right here - oh my gosh! */ 8000),"
+ + "\n _SONG_ID int,"
+ + "\n _NOTES varchar(2000),"
+ + "\n out _RESULT varchar(10)"
+ + "\n /*"
+ + "\n , -- Generic result parameter"
+ + "\n out _PERIOD_ID int, -- Returns the period_id. Useful when using @PREDEFLINK to return which is the last period"
+ + "\n _SONGS_LIST varchar(8000),"
+ + "\n _COMPOSERID int,"
+ + "\n _PUBLISHERID int,"
+ + "\n _PREDEFLINK int -- If the user is accessing through a predefined link: 0=none 1=last period"
+ + "\n */) BEGIN SELECT 1; END");
+
+ createProcedure(
+ "testBug26959_1",
+ "(`/*id*/` /* before type 1 */ varchar(20),"
+ + "/* after type 1 */ OUT result2 DECIMAL(/*size1*/10,/*size2*/2) /* p2 */)"
+ + "BEGIN SELECT action, result; END");
+
+ try {
+ this.conn.prepareCall(
+ "{call testBug26959(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)}")
+ .close();
+ this.rs = this.conn.getMetaData().getProcedureColumns(
+ this.conn.getCatalog(), null, "testBug26959", "%");
+
+ String[] parameterNames = new String[] { "_ACTION",
+ "/*dumb-identifier-1*/", "#dumb-identifier-2",
+ "--dumb-identifier-3", "_CLIENT_ID", "_LOGIN_ID", "_WHERE",
+ "_SORT", "_SQL", "_SONG_ID", "_NOTES", "_RESULT" };
+
+ int[] parameterTypes = new int[] { Types.VARCHAR, Types.INTEGER,
+ Types.INTEGER, Types.INTEGER, Types.INTEGER, Types.INTEGER,
+ Types.VARCHAR, Types.VARCHAR, Types.VARCHAR, Types.INTEGER,
+ Types.VARCHAR, Types.VARCHAR };
+
+ int[] direction = new int[] { DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnOut,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnOut };
+
+ int[] precision = new int[] { 20, 10, 10, 10, 10, 10, 2000, 2000,
+ 8000, 10, 2000, 10 };
+
+ int index = 0;
+
+ while (this.rs.next()) {
+ assertEquals(parameterNames[index], this.rs
+ .getString("COLUMN_NAME"));
+ assertEquals(parameterTypes[index], this.rs.getInt("DATA_TYPE"));
+ assertEquals(precision[index], this.rs.getInt("PRECISION"));
+ assertEquals(direction[index], this.rs.getInt("COLUMN_TYPE"));
+ index++;
+ }
+
+ this.rs.close();
+
+ index = 0;
+ parameterNames = new String[] { "/*id*/", "result2" };
+ parameterTypes = new int[] { Types.VARCHAR, Types.DECIMAL };
+ precision = new int[] { 20, 10 };
+ direction = new int[] { DatabaseMetaData.procedureColumnIn,
+ DatabaseMetaData.procedureColumnOut };
+ int[] scale = new int[] { 0, 2 };
+
+ this.conn.prepareCall("{call testBug26959_1(?, ?)}").close();
+
+ this.rs = this.conn.getMetaData().getProcedureColumns(
+ this.conn.getCatalog(), null, "testBug26959_1", "%");
+
+ while (this.rs.next()) {
+ assertEquals(parameterNames[index], this.rs
+ .getString("COLUMN_NAME"));
+ assertEquals(parameterTypes[index], this.rs.getInt("DATA_TYPE"));
+ assertEquals(precision[index], this.rs.getInt("PRECISION"));
+ assertEquals(scale[index], this.rs.getInt("SCALE"));
+ assertEquals(direction[index], this.rs.getInt("COLUMN_TYPE"));
+
+ index++;
+ }
+ } finally {
+ closeMemberJDBCResources();
+ }
+ }
+
+ /**
+ * Tests fix for BUG#27400 - CALL [comment] some_proc() doesn't work
+ */
+ public void testBug27400() throws Exception {
+ if (!serverSupportsStoredProcedures()) {
+ return; // SPs not supported
+ }
+
+ createProcedure("testBug27400", "(a INT, b VARCHAR(32)) BEGIN SELECT 1; END");
+
+ CallableStatement cStmt = null;
+
+ try {
+ cStmt = this.conn.prepareCall("{CALL /* SOME COMMENT */ testBug27400( /* does this work too? */ ?, ?)} # and a commented ? here too");
+ assertTrue(cStmt.toString().indexOf("/*") != -1); // we don't want to strip the comments
+ cStmt.setInt(1, 1);
+ cStmt.setString(2, "bleh");
+ cStmt.execute();
+ } finally {
+ if (cStmt != null) {
+ cStmt.close();
+ }
+ }
+ }
+
+ /**
+ * Tests fix for BUG#28689 - CallableStatement.executeBatch()
+ * doesn't work when connection property "noAccessToProcedureBodies"
+ * has been set to "true".
+ *
+ * The fix involves changing the behavior of "noAccessToProcedureBodies",
+ * in that the driver will now report all paramters as "IN" paramters
+ * but allow callers to call registerOutParameter() on them.
+ *
+ * @throws Exception
+ */
+ public void testBug28689() throws Exception {
+ if (!versionMeetsMinimum(5, 0)) {
+ return; // no stored procedures
+ }
+
+ createTable("testBug28689", "(" +
+
+ "`id` int(11) NOT NULL auto_increment,"
+ + "`usuario` varchar(255) default NULL,"
+ + "PRIMARY KEY (`id`)"
+ + ")");
+
+ this.stmt.executeUpdate("INSERT INTO testBug28689 (usuario) VALUES ('AAAAAA')");
+
+ createProcedure("sp_testBug28689", "(tid INT)"
+ + "\nBEGIN"
+ + "\nUPDATE testBug28689 SET usuario = 'BBBBBB' WHERE id = tid;"
+ + "\nEND");
+
+ Connection noProcedureBodiesConn = getConnectionWithProps("noAccessToProcedureBodies=true");
+ CallableStatement cStmt = null;
+
+ try {
+ cStmt = noProcedureBodiesConn.prepareCall("{CALL sp_testBug28689(?)}");
+ cStmt.setInt(1, 1);
+ cStmt.addBatch();
+ cStmt.executeBatch();
+
+ assertEquals("BBBBBB", getSingleIndexedValueWithQuery(noProcedureBodiesConn, 1, "SELECT `usuario` FROM testBug28689 WHERE id=1"));
+ } finally {
+ if (cStmt != null) {
+ cStmt.close();
+ }
+
+ if (noProcedureBodiesConn != null) {
+ noProcedureBodiesConn.close();
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/ConnectionRegressionTest.java b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/ConnectionRegressionTest.java
new file mode 100644
index 00000000..8d3e3717
--- /dev/null
+++ b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/ConnectionRegressionTest.java
@@ -0,0 +1,2233 @@
+/*
+ Copyright (C) 2002-2007 MySQL AB
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
+
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+
+
+ */
+package testsuite.regression;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.DriverPropertyInfo;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+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;
+import com.mysql.jdbc.log.StandardLogger;
+
+/**
+ * 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((Properties)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((Properties)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));
+
+ HashMap methodsToSkipMap = new HashMap();
+
+ // Needs an actual URL
+ methodsToSkipMap.put("getURL", null);
+
+ // Java6 JDBC4.0 methods we don't implement
+ methodsToSkipMap.put("getNCharacterStream", null);
+ methodsToSkipMap.put("getNClob", null);
+ methodsToSkipMap.put("getNString", null);
+ methodsToSkipMap.put("getRowId", null);
+ methodsToSkipMap.put("getSQLXML", null);
+
+ for (int j = 0; j < 2; j++) {
+ for (int i = 0; i < getMethods.length; i++) {
+ String methodName = getMethods[i].getName();
+
+ if (methodName.startsWith("get")
+ && !methodsToSkipMap.containsKey(methodName)) {
+ 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");
+
+ if (!isRunningOnJdk131()) {
+ 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();
+ }
+ }
+ }
+
+ /**
+ * Tests to insure proper behavior for BUG#24706.
+ *
+ * @throws Exception if the test fails.
+ */
+ public void testBug24706() throws Exception {
+ if (!versionMeetsMinimum(5, 0)) {
+ return; // server status isn't there to support this feature
+ }
+
+ Properties props = new Properties();
+ props.setProperty("elideSetAutoCommits", "true");
+ props.setProperty("logger", "StandardLogger");
+ props.setProperty("profileSQL", "true");
+ Connection c = null;
+
+ StringBuffer logBuf = new StringBuffer();
+
+ StandardLogger.bufferedLog = logBuf;
+
+ try {
+ c = getConnectionWithProps(props);
+ c.setAutoCommit(true);
+ c.createStatement().execute("SELECT 1");
+ c.setAutoCommit(true);
+ c.setAutoCommit(false);
+ c.createStatement().execute("SELECT 1");
+ c.setAutoCommit(false);
+
+ // We should only see _one_ "set autocommit=" sent to the server
+
+ String log = logBuf.toString();
+ int searchFrom = 0;
+ int count = 0;
+ int found = 0;
+
+ while ((found = log.indexOf("SET autocommit=", searchFrom)) != -1) {
+ searchFrom = found + 1;
+ count++;
+ }
+
+ // The SELECT doesn't actually start a transaction, so being pedantic the
+ // driver issues SET autocommit=0 again in this case.
+ assertEquals(2, count);
+ } finally {
+ StandardLogger.bufferedLog = null;
+
+ if (c != null) {
+ c.close();
+ }
+
+ }
+ }
+
+ /**
+ * Tests fix for BUG#25514 - Timer instance used for Statement.setQueryTimeout()
+ * created per-connection, rather than per-VM, causing memory leak.
+ *
+ * @throws Exception if the test fails.
+ */
+ public void testBug25514() throws Exception {
+
+ for (int i = 0; i < 10; i++) {
+ getConnectionWithProps((Properties)null).close();
+ }
+
+ ThreadGroup root = Thread.currentThread().getThreadGroup().getParent();
+
+ while (root.getParent() != null) {
+ root = root.getParent();
+ }
+
+ int numThreadsNamedTimer = findNamedThreadCount(root, "Timer");
+
+ if (numThreadsNamedTimer == 0) {
+ numThreadsNamedTimer = findNamedThreadCount(root, "MySQL Statement Cancellation Timer");
+ }
+
+ // Notice that this seems impossible to test on JDKs prior to 1.5, as there is no
+ // reliable way to find the TimerThread, so we have to rely on new JDKs for this
+ // test.
+ assertTrue("More than one timer for cancel was created", numThreadsNamedTimer <= 1);
+ }
+
+ private int findNamedThreadCount(ThreadGroup group, String nameStart) {
+
+ int count = 0;
+
+ int numThreads = group.activeCount();
+ Thread[] threads = new Thread[numThreads*2];
+ numThreads = group.enumerate(threads, false);
+
+ for (int i=0; i+ * "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." + *
+ * + *+ * "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." + *
+ * + *+ * "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." + *
+ * The JDBC 3.0 specification states the same in other words: + * + *+ * "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." + *
+ * + *+ * "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." + *
+ * + *+ * "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." + *
+ * 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()); + } + } +} diff --git a/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/ResultSetRegressionTest.java b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/ResultSetRegressionTest.java new file mode 100644 index 00000000..001ec3f2 --- /dev/null +++ b/src/lib/mysql-connector-java-5.0.8/src/testsuite/regression/ResultSetRegressionTest.java @@ -0,0 +1,4465 @@ +/* + Copyright (C) 2002-2007 MySQL AB + + This program is free software; you can redistribute it and/or modify + it under the terms of version 2 of the GNU General Public License as + published by the Free Software Foundation. + + There are special exceptions to the terms and conditions of the GPL + as it is applied to this software. View the full text of the + exception in file EXCEPTIONS-CONNECTOR-J in the directory of this + software distribution. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + + */ +package testsuite.regression; + +import java.io.Reader; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.sql.CallableStatement; +import java.sql.Clob; +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.Messages; +import com.mysql.jdbc.MysqlDataTruncation; +import com.mysql.jdbc.NotUpdatable; +import com.mysql.jdbc.SQLError; +import com.mysql.jdbc.StringUtils; +import com.mysql.jdbc.log.StandardLogger; + +/** + * 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()); + assertEquals("2004-11-20", this.rs.getString("field1")); + 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(); + } + } + + /** + * Tests for a server bug - needs to be revisited when the server is fixed. + * + * @throws Exception + * if the test fails. + */ + public void testBug24710() throws Exception { + if (!versionMeetsMinimum(6, 0)) { + return; + } + + createTable("testBug24710", "(x varbinary(256))"); + + try { + this.stmt + .executeUpdate("insert into testBug24710(x) values(0x0000000000)," + + "(0x1111111111)," + + "(0x2222222222)," + + "(0x3333333333)," + + "(0x4444444444)," + + "(0x5555555555)," + + "(0x6666666666)," + + "(0x7777777777)," + + "(0x8888888888)," + + "(0x9999999999)," + + "(0xaaaaaaaaaa)," + + "(0xbbbbbbbbbb)," + + "(0xcccccccccc)," + + "(0xdddddddddd)," + + "(0xeeeeeeeeee)," + + "(0xffffffffff)"); + + this.rs = this.stmt + .executeQuery("select t1.x t1x,(select x from testBug24710 t2 where t2.x=t1.x) t2x from testBug24710 t1"); + + assertEquals(Types.VARBINARY, this.rs.getMetaData() + .getColumnType(1)); + assertEquals(Types.VARBINARY, this.rs.getMetaData() + .getColumnType(2)); + + this.rs = ((com.mysql.jdbc.Connection) this.conn) + .serverPrepare( + "select t1.x t1x,(select x from testBug24710 t2 where t2.x=t1.x) t2x from testBug24710 t1") + .executeQuery(); + + assertEquals(Types.VARBINARY, this.rs.getMetaData() + .getColumnType(1)); + assertEquals(Types.VARBINARY, this.rs.getMetaData() + .getColumnType(2)); + } finally { + closeMemberJDBCResources(); + } + } + + /** + * Tests fix for BUG#25328 - BIT(> 1) is returned as java.lang.String + * from ResultSet.getObject() rather than byte[]. + * + * @throws Exception if the test fails. + */ + public void testbug25328() throws Exception { + if (!versionMeetsMinimum(5, 0)) { + return; + } + + createTable("testBug25382", "(BINARY_VAL BIT(64) NULL)"); + + byte[] bytearr = new byte[8]; + + this.pstmt = this.conn + .prepareStatement("INSERT INTO testBug25382 VALUES(?)"); + try { + + this.pstmt.setObject(1, bytearr, java.sql.Types.BINARY); + assertEquals(1, this.pstmt.executeUpdate()); + this.pstmt.clearParameters(); + + this.rs = this.stmt.executeQuery("Select BINARY_VAL from testBug25382"); + this.rs.next(); + assertEquals(this.rs.getObject(1).getClass(), bytearr.getClass()); + } finally { + closeMemberJDBCResources(); + } + } + + /** + * Tests fix for BUG#25517 - Statement.setMaxRows() is not effective on + * result sets materialized from cursors. + * + * @throws Exception + * if the test fails + */ + public void testBug25517() throws Exception { + Connection fetchConn = null; + Statement fetchStmt = null; + + createTable("testBug25517", "(field1 int)"); + + StringBuffer insertBuf = new StringBuffer("INSERT INTO testBug25517 VALUES (1)"); + + for (int i = 0; i < 100; i++) { + insertBuf.append(",(" + i + ")"); + } + + this.stmt.executeUpdate(insertBuf.toString()); + + try { + Properties props = new Properties(); + props.setProperty("useServerPrepStmts", "true"); + props.setProperty("useCursorFetch", "true"); + + fetchConn = getConnectionWithProps(props); + fetchStmt = fetchConn.createStatement(); + + //int[] maxRows = new int[] {1, 4, 5, 11, 12, 13, 16, 50, 51, 52, 100}; + int[] fetchSizes = new int[] {1, 4, 10, 25, 100}; + List maxRows = new ArrayList(); + maxRows.add(new Integer(1)); + + for (int i = 0; i < fetchSizes.length; i++) { + if (fetchSizes[i] != 1) { + maxRows.add(new Integer(fetchSizes[i] - 1)); + } + + maxRows.add(new Integer(fetchSizes[i])); + + if (i != fetchSizes.length - 1) { + maxRows.add(new Integer(fetchSizes[i] + 1)); + } + } + + for (int fetchIndex = 0; fetchIndex < fetchSizes.length; fetchIndex++) { + fetchStmt.setFetchSize(fetchSizes[fetchIndex]); + + for (int maxRowIndex = 0; maxRowIndex < maxRows.size(); maxRowIndex++) { + + int maxRowsToExpect = ((Integer)maxRows.get(maxRowIndex)).intValue(); + fetchStmt.setMaxRows(maxRowsToExpect); + + int rowCount = 0; + + this.rs = fetchStmt.executeQuery("SELECT * FROM testBug25517"); + + while (this.rs.next()) { + rowCount++; + } + + assertEquals(maxRowsToExpect, rowCount); + } + } + + this.pstmt = fetchConn.prepareStatement("SELECT * FROM testBug25517"); + + for (int fetchIndex = 0; fetchIndex < fetchSizes.length; fetchIndex++) { + this.pstmt.setFetchSize(fetchSizes[fetchIndex]); + + for (int maxRowIndex = 0; maxRowIndex < maxRows.size(); maxRowIndex++) { + + int maxRowsToExpect = ((Integer)maxRows.get(maxRowIndex)).intValue(); + this.pstmt.setMaxRows(maxRowsToExpect); + + int rowCount = 0; + + this.rs = this.pstmt.executeQuery(); + + while (this.rs.next()) { + rowCount++; + } + + assertEquals(maxRowsToExpect, rowCount); + } + } + + } finally { + closeMemberJDBCResources(); + + if (fetchStmt != null) { + fetchStmt.close(); + } + + if (fetchConn != null) { + fetchConn.close(); + } + } + } + + /** + * Tests fix for BUG#25787 - java.util.Date should be serialized for PreparedStatement.setObject(). + * + * We add a new configuration option "treatUtilDateAsTimestamp", which is false by default, + * as (1) We already had specific behavior to treat java.util.Date as a java.sql.Timestamp because + * it's useful to many folks, and (2) that behavior will very likely be in JDBC-post-4.0 as a + * requirement. + * + * @throws Exception if the test fails. + */ + public void testBug25787() throws Exception { + createTable("testBug25787", "(MY_OBJECT_FIELD BLOB)"); + + Connection deserializeConn = null; + + Properties props = new Properties(); + props.setProperty("autoDeserialize", "true"); + props.setProperty("treatUtilDateAsTimestamp", "false"); + + try { + deserializeConn = getConnectionWithProps(props); + + this.pstmt = deserializeConn.prepareStatement("INSERT INTO testBug25787 (MY_OBJECT_FIELD) VALUES (?)"); + java.util.Date dt = new java.util.Date(); + + this.pstmt.setObject(1, dt); + this.pstmt.execute(); + + this.rs = deserializeConn.createStatement().executeQuery("SELECT MY_OBJECT_FIELD FROM testBug25787"); + this.rs.next(); + assertEquals("java.util.Date", this.rs.getObject(1).getClass().getName()); + assertEquals(dt, this.rs.getObject(1)); + } finally { + closeMemberJDBCResources(); + } + } + + public void testTruncationDisable() throws Exception { + Properties props = new Properties(); + props.setProperty("jdbcCompliantTruncation", "false"); + 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(); + } + + } + + public void testUsageAdvisorOnZeroRowResultSet() throws Exception { + Connection advisorConn = null; + Statement advisorStmt = null; + + try { + Properties props = new Properties(); + props.setProperty("useUsageAdvisor", "true"); + + advisorConn = getConnectionWithProps(props); + + advisorStmt = advisorConn.createStatement(); + + StringBuffer advisorBuf = new StringBuffer(); + StandardLogger.bufferedLog = advisorBuf; + + this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0"); + this.rs.next(); + this.rs.close(); + + advisorStmt.close(); + + advisorStmt = advisorConn.createStatement(ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY); + + advisorStmt.setFetchSize(Integer.MIN_VALUE); + + this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0"); + this.rs.next(); + this.rs.close(); + + StandardLogger.bufferedLog = null; + + if (versionMeetsMinimum(5, 0, 2)) { + advisorConn.close(); + + props.setProperty("useCursorFetch", "true"); + props.setProperty("useServerPrepStmts", "true"); + + advisorConn = getConnectionWithProps(props); + + advisorStmt = advisorConn.createStatement(); + advisorStmt.setFetchSize(1); + + StandardLogger.bufferedLog = advisorBuf; + + this.rs = advisorStmt.executeQuery("SELECT 1, 2 LIMIT 0"); + this.rs.next(); + this.rs.close(); + } + + assertEquals("", advisorBuf.toString()); + } finally { + StandardLogger.bufferedLog = null; + + closeMemberJDBCResources(); + + if (advisorStmt != null) { + advisorStmt.close(); + } + + if (advisorConn != null) { + advisorConn.close(); + } + } + } + + public void testBug25894() throws Exception { + createTable("bug25894", "("+ + "tinyInt_type TINYINT DEFAULT 1,"+ + "tinyIntU_type TINYINT UNSIGNED DEFAULT 1,"+ + "smallInt_type SMALLINT DEFAULT 1,"+ + "smallIntU_type SMALLINT UNSIGNED DEFAULT 1,"+ + "mediumInt_type MEDIUMINT DEFAULT 1,"+ + "mediumIntU_type MEDIUMINT UNSIGNED DEFAULT 1,"+ + "int_type INT DEFAULT 1,"+ + "intU_type INT UNSIGNED DEFAULT 1,"+ + "bigInt_type BIGINT DEFAULT 1,"+ + "bigIntU_type BIGINT UNSIGNED DEFAULT 1"+ + ");"); + try { + this.stmt.executeUpdate("INSERT INTO bug25894 VALUES (-1,1,-1,1,-1,1,-1,1,-1,1)"); + this.rs = this.stmt.executeQuery("SELECT * FROM bug25894"); + java.sql.ResultSetMetaData tblMD = this.rs.getMetaData(); + this.rs.first(); + for (int i=1; i