Modified:
branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
branches/branch_5_0/connector-j/src/com/mysql/jdbc/MysqlParameterMetadata.java
branches/branch_5_0/connector-j/src/com/mysql/jdbc/PreparedStatement.java
branches/branch_5_0/connector-j/src/testsuite/regression/MetaDataRegressionTest.java
trunk/connector-j/CHANGES
trunk/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
trunk/connector-j/src/com/mysql/jdbc/MysqlParameterMetadata.java
trunk/connector-j/src/com/mysql/jdbc/PreparedStatement.java
trunk/connector-j/src/testsuite/regression/MetaDataRegressionTest.java
Log:
Fixed BUG#21267, ParameterMetaData throws NullPointerException when
prepared SQL actually has a syntax error. Added
"generateSimpleParameterMetadata" configuration property, which when set
to "true" will generate metadata reflecting VARCHAR for every parameter
(the default is "false", which will cause an exception to be thrown if no
parameter metadata for the statement is actually available).
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
===================================================================
---
branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java 2007-02-23
21:43:01 UTC (rev 6320)
+++
branches/branch_5_0/connector-j/src/com/mysql/jdbc/ConnectionProperties.java 2007-02-23
21:52:30 UTC (rev 6321)
@@ -873,7 +873,13 @@
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(
@@ -3801,5 +3807,15 @@
public String getUseConfigs() {
return this.useConfigs.getValueAsString();
}
+
+ public boolean getGenerateSimpleParameterMetadata() {
+ return this.generateSimpleParameterMetadata.getValueAsBoolean();
+ }
+
+
+ public void setGenerateSimpleParameterMetadata(boolean flag) {
+ this.generateSimpleParameterMetadata.setValue(flag);
+ }
+
}
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/MysqlParameterMetadata.java
===================================================================
---
branches/branch_5_0/connector-j/src/com/mysql/jdbc/MysqlParameterMetadata.java 2007-02-23
21:43:01 UTC (rev 6320)
+++
branches/branch_5_0/connector-j/src/com/mysql/jdbc/MysqlParameterMetadata.java 2007-02-23
21:52:30 UTC (rev 6321)
@@ -25,9 +25,10 @@
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;
@@ -38,6 +39,17 @@
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;
}
@@ -49,7 +61,7 @@
}
private void checkAvailable() throws SQLException {
- if (this.metadata == null) {
+ if (this.metadata == null || this.metadata.fields == null) {
throw SQLError.createSQLException(
"Parameter metadata not available for the given statement",
SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
@@ -57,36 +69,72 @@
}
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));
@@ -95,4 +143,20 @@
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);
+
+ }
+ }
}
Modified: branches/branch_5_0/connector-j/src/com/mysql/jdbc/PreparedStatement.java
===================================================================
--- branches/branch_5_0/connector-j/src/com/mysql/jdbc/PreparedStatement.java 2007-02-23
21:43:01 UTC (rev 6320)
+++ branches/branch_5_0/connector-j/src/com/mysql/jdbc/PreparedStatement.java 2007-02-23
21:52:30 UTC (rev 6321)
@@ -910,6 +910,14 @@
}
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();
}
@@ -1985,8 +1993,12 @@
public ParameterMetaData getParameterMetaData()
throws SQLException {
if (this.parameterMetaData == null) {
- this.parameterMetaData = new MysqlParameterMetadata(
+ if (this.connection.getGenerateSimpleParameterMetadata()) {
+ this.parameterMetaData = new MysqlParameterMetadata(this.parameterCount);
+ } else {
+ this.parameterMetaData = new MysqlParameterMetadata(
null, this.parameterCount);
+ }
}
return this.parameterMetaData;
Modified:
branches/branch_5_0/connector-j/src/testsuite/regression/MetaDataRegressionTest.java
===================================================================
---
branches/branch_5_0/connector-j/src/testsuite/regression/MetaDataRegressionTest.java 2007-02-23
21:43:01 UTC (rev 6320)
+++
branches/branch_5_0/connector-j/src/testsuite/regression/MetaDataRegressionTest.java 2007-02-23
21:52:30 UTC (rev 6321)
@@ -1543,6 +1543,50 @@
}
/**
+ * Tests fix for BUG#21267, ParameterMetaData throws NullPointerException
+ * when prepared SQL actually has a syntax error
+ *
+ * @throws Exception
+ */
+ public void testBug21267() throws Exception {
+ if (isRunningOnJdk131()) {
+ return; // no parameter metadata on JDK-1.3.1
+ }
+
+ createTable(
+ "bug21267",
+ "(`Col1` int(11) NOT NULL,`Col2` varchar(45) default NULL,`Col3` varchar(45) default
NULL,PRIMARY KEY (`Col1`))");
+
+ try {
+ this.pstmt = this.conn
+ .prepareStatement("SELECT Col1, Col2,Col4 FROM bug21267 WHERE Col1=?");
+ this.pstmt.setInt(1, 1);
+
+ java.sql.ParameterMetaData psMeta = this.pstmt
+ .getParameterMetaData();
+
+ try {
+ assertEquals(0, psMeta.getParameterType(1));
+ } catch (SQLException sqlEx) {
+ assertEquals(SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, sqlEx.getSQLState());
+ }
+
+ this.pstmt.close();
+
+ Properties props = new Properties();
+ props.setProperty("generateSimpleParameterMetadata", "true");
+
+ this.pstmt = getConnectionWithProps(props).prepareStatement("SELECT Col1, Col2,Col4
FROM bug21267 WHERE Col1=?");
+
+ psMeta = this.pstmt.getParameterMetaData();
+
+ assertEquals(Types.VARCHAR, psMeta.getParameterType(1));
+ } finally {
+ closeMemberJDBCResources();
+ }
+ }
+
+ /**
* Tests fix for BUG#21544 - When using information_schema for metadata,
* COLUMN_SIZE for getColumns() is not clamped to range of
* java.lang.Integer as is the case when not using
Modified: trunk/connector-j/CHANGES
===================================================================
--- trunk/connector-j/CHANGES 2007-02-23 21:43:01 UTC (rev 6320)
+++ trunk/connector-j/CHANGES 2007-02-23 21:52:30 UTC (rev 6321)
@@ -155,7 +155,14 @@
- Fixed BUG#22628 - Driver.getPropertyInfo() throws NullPointerException for
URL that only specifies host and/or port.
-
+
+ - Fixed BUG#21267, ParameterMetaData throws NullPointerException when
+ prepared SQL actually has a syntax error. Added
+ "generateSimpleParameterMetadata" configuration property, which when set
+ to "true" will generate metadata reflecting VARCHAR for every parameter
+ (the default is "false", which will cause an exception to be thrown if no
+ parameter metadata for the statement is actually available).
+
- When extracting foreign key information from "SHOW CREATE TABLE " in
DatabaseMetaData, ignore exceptions relating to tables being missing
(which could happen for cross-reference or imported-key requests, as
Modified: trunk/connector-j/src/com/mysql/jdbc/ConnectionProperties.java
===================================================================
--- trunk/connector-j/src/com/mysql/jdbc/ConnectionProperties.java 2007-02-23 21:43:01 UTC
(rev 6320)
+++ trunk/connector-j/src/com/mysql/jdbc/ConnectionProperties.java 2007-02-23 21:52:30 UTC
(rev 6321)
@@ -877,6 +877,12 @@
"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(
@@ -3865,6 +3871,9 @@
this.useSSPSCompatibleTimezoneShift.setValue(flag);
}
+ public boolean getTreatUtilDateAsTimestamp() {
+ return this.treatUtilDateAsTimestamp.getValueAsBoolean();
+ }
public void setTreatUtilDateAsTimestamp(boolean flag) {
this.treatUtilDateAsTimestamp.setValue(flag);
@@ -3888,10 +3897,18 @@
public void setUseConfigs(String configs) {
this.useConfigs.setValue(configs);
-}
+ }
public String getUseConfigs() {
return this.useConfigs.getValueAsString();
}
+
+ public boolean getGenerateSimpleParameterMetadata() {
+ return this.generateSimpleParameterMetadata.getValueAsBoolean();
+ }
+
+ public void setGenerateSimpleParameterMetadata(boolean flag) {
+ this.generateSimpleParameterMetadata.setValue(flag);
+ }
}
Modified: trunk/connector-j/src/com/mysql/jdbc/MysqlParameterMetadata.java
===================================================================
--- trunk/connector-j/src/com/mysql/jdbc/MysqlParameterMetadata.java 2007-02-23 21:43:01
UTC (rev 6320)
+++ trunk/connector-j/src/com/mysql/jdbc/MysqlParameterMetadata.java 2007-02-23 21:52:30
UTC (rev 6321)
@@ -1,96 +1,145 @@
/*
- Copyright (C) 2005 MySQL AB
+ Copyright (C) 2005 MySQL AB
- This program is free software; you can redistribute it and/or modify
- it under the terms of version 2 of the GNU General Public License as
- published by the Free Software Foundation.
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of version 2 of the GNU General Public License as
+ published by the Free Software Foundation.
- There are special exceptions to the terms and conditions of the GPL
- as it is applied to this software. View the full text of the
- exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
- software distribution.
+ There are special exceptions to the terms and conditions of the GPL
+ as it is applied to this software. View the full text of the
+ exception in file EXCEPTIONS-CONNECTOR-J in the directory of this
+ software distribution.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-*/
+ */
package com.mysql.jdbc;
import java.sql.ParameterMetaData;
import java.sql.SQLException;
+import java.sql.Types;
import com.mysql.jdbc.exceptions.JDBC40NotYetImplementedException;
public class MysqlParameterMetadata implements ParameterMetaData {
-
+ boolean returnSimpleMetadata = false;
+
ResultSetMetaData metadata = null;
+
int parameterCount = 0;
-
-
+
MysqlParameterMetadata(Field[] fieldInfo, int parameterCount) {
this.metadata = new ResultSetMetaData(fieldInfo, false);
-
+
this.parameterCount = parameterCount;
}
-
+
+ /**
+ * Used for "fake" basic metadata for client-side prepared statements when
+ * we don't know the parameter types.
+ *
+ * @param parameterCount
+ */
+ MysqlParameterMetadata(int count) {
+ this.parameterCount = count;
+ this.returnSimpleMetadata = true;
+ }
+
public int getParameterCount() throws SQLException {
return this.parameterCount;
}
public int isNullable(int arg0) throws SQLException {
checkAvailable();
-
+
return this.metadata.isNullable(arg0);
}
private void checkAvailable() throws SQLException {
- if (this.metadata == null) {
+ if (this.metadata == null || this.metadata.fields == null) {
throw SQLError.createSQLException(
- "Parameter metadata not available for the given statement",
- SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
+ "Parameter metadata not available for the given statement",
+ SQLError.SQL_STATE_DRIVER_NOT_CAPABLE);
}
}
public boolean isSigned(int arg0) throws SQLException {
+ if (this.returnSimpleMetadata) {
+ checkBounds(arg0);
+
+ return false;
+ }
+
checkAvailable();
-
+
return (this.metadata.isSigned(arg0));
}
public int getPrecision(int arg0) throws SQLException {
+ if (this.returnSimpleMetadata) {
+ checkBounds(arg0);
+
+ return 0;
+ }
+
checkAvailable();
-
+
return (this.metadata.getPrecision(arg0));
}
public int getScale(int arg0) throws SQLException {
+ if (this.returnSimpleMetadata) {
+ checkBounds(arg0);
+
+ return 0;
+ }
+
checkAvailable();
-
+
return (this.metadata.getScale(arg0));
}
public int getParameterType(int arg0) throws SQLException {
+ if (this.returnSimpleMetadata) {
+ checkBounds(arg0);
+
+ return Types.VARCHAR;
+ }
+
checkAvailable();
-
+
return (this.metadata.getColumnType(arg0));
}
public String getParameterTypeName(int arg0) throws SQLException {
+ if (this.returnSimpleMetadata) {
+ checkBounds(arg0);
+
+ return "VARCHAR";
+ }
+
checkAvailable();
-
+
return (this.metadata.getColumnTypeName(arg0));
}
public String getParameterClassName(int arg0) throws SQLException {
+ if (this.returnSimpleMetadata) {
+ checkBounds(arg0);
+
+ return "java.lang.String";
+ }
+
checkAvailable();
-
+
return (this.metadata.getColumnClassName(arg0));
}
@@ -98,6 +147,23 @@
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);
+
+ }
+ }
+
public boolean isWrapperFor(Class arg0) throws SQLException {
throw new JDBC40NotYetImplementedException();
}
Modified: trunk/connector-j/src/com/mysql/jdbc/PreparedStatement.java
===================================================================
--- trunk/connector-j/src/com/mysql/jdbc/PreparedStatement.java 2007-02-23 21:43:01 UTC
(rev 6320)
+++ trunk/connector-j/src/com/mysql/jdbc/PreparedStatement.java 2007-02-23 21:52:30 UTC
(rev 6321)
@@ -2027,13 +2027,17 @@
*/
public ParameterMetaData getParameterMetaData()
throws SQLException {
- if (this.parameterMetaData == null) {
+ if (this.parameterMetaData == null) {
+ if (this.connection.getGenerateSimpleParameterMetadata()) {
+ this.parameterMetaData = new MysqlParameterMetadata(this.parameterCount);
+ } else {
this.parameterMetaData = new MysqlParameterMetadata(
- null, this.parameterCount);
+ null, this.parameterCount);
}
-
- return this.parameterMetaData;
}
+
+ return this.parameterMetaData;
+}
ParseInfo getParseInfo() {
return this.parseInfo;
Modified: trunk/connector-j/src/testsuite/regression/MetaDataRegressionTest.java
===================================================================
--- trunk/connector-j/src/testsuite/regression/MetaDataRegressionTest.java 2007-02-23
21:43:01 UTC (rev 6320)
+++ trunk/connector-j/src/testsuite/regression/MetaDataRegressionTest.java 2007-02-23
21:52:30 UTC (rev 6321)
@@ -1885,4 +1885,48 @@
}
}
+ /**
+ * Tests fix for BUG#21267, ParameterMetaData throws NullPointerException
+ * when prepared SQL actually has a syntax error
+ *
+ * @throws Exception
+ */
+ public void testBug21267() throws Exception {
+ if (isRunningOnJdk131()) {
+ return; // no parameter metadata on JDK-1.3.1
+ }
+
+ createTable(
+ "bug21267",
+ "(`Col1` int(11) NOT NULL,`Col2` varchar(45) default NULL,`Col3` varchar(45) default
NULL,PRIMARY KEY (`Col1`))");
+
+ try {
+ this.pstmt = this.conn
+ .prepareStatement("SELECT Col1, Col2,Col4 FROM bug21267 WHERE Col1=?");
+ this.pstmt.setInt(1, 1);
+
+ java.sql.ParameterMetaData psMeta = this.pstmt
+ .getParameterMetaData();
+
+ try {
+ assertEquals(0, psMeta.getParameterType(1));
+ } catch (SQLException sqlEx) {
+ assertEquals(SQLError.SQL_STATE_DRIVER_NOT_CAPABLE, sqlEx.getSQLState());
+ }
+
+ this.pstmt.close();
+
+ Properties props = new Properties();
+ props.setProperty("generateSimpleParameterMetadata", "true");
+
+ this.pstmt = getConnectionWithProps(props).prepareStatement("SELECT Col1, Col2,Col4
FROM bug21267 WHERE Col1=?");
+
+ psMeta = this.pstmt.getParameterMetaData();
+
+ assertEquals(Types.VARCHAR, psMeta.getParameterType(1));
+ } finally {
+ closeMemberJDBCResources();
+ }
+ }
+
}
| Thread |
|---|
| • Connector/J commit: r6321 - branches/branch_5_0/connector-j/src/com/mysql/jdbc branches/branch_5_0/connector-j/src/testsuite/regression trunk/connecto... | mmatthews | 23 Feb |