Skip to content

Commit

Permalink
[CONJ-526] better error message getting metadata information when SQL…
Browse files Browse the repository at this point in the history
… syntax is wrong
  • Loading branch information
rusher committed Sep 18, 2017
1 parent 621c877 commit 2fe943c
Show file tree
Hide file tree
Showing 5 changed files with 50 additions and 24 deletions.
22 changes: 10 additions & 12 deletions src/main/java/org/mariadb/jdbc/MariaDbPreparedStatementClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -92,10 +92,10 @@ public class MariaDbPreparedStatementClient extends BasePrepareStatement {
public MariaDbPreparedStatementClient(MariaDbConnection connection, String sql, int resultSetScrollType,
int resultSetConcurrency, int autoGeneratedKeys) throws SQLException {
super(connection, resultSetScrollType, resultSetConcurrency, autoGeneratedKeys);
this.sqlQuery = sql;
sqlQuery = sql;

if (options.cachePrepStmts) {
String key = this.protocol.getDatabase() + "-" + sqlQuery;
String key = protocol.getDatabase() + "-" + sqlQuery;
prepareResult = connection.getClientPrepareStatementCache().get(key);
}

Expand All @@ -106,7 +106,7 @@ public MariaDbPreparedStatementClient(MariaDbConnection connection, String sql,
prepareResult = ClientPrepareResult.parameterParts(sqlQuery, protocol.noBackslashEscapes());
}
if (options.cachePrepStmts && sql.length() < 1024) {
String key = this.protocol.getDatabase() + "-" + sqlQuery;
String key = protocol.getDatabase() + "-" + sqlQuery;
connection.getClientPrepareStatementCache().put(key, prepareResult);
}
}
Expand Down Expand Up @@ -381,15 +381,11 @@ private void executeInternalBatch(int size) throws SQLException {
* invoke the method <code>getMetaData</code> on a <code>PreparedStatement</code> object rather than waiting to
* execute it and then invoking the <code>ResultSet.getMetaData</code> method on the <code>ResultSet</code> object
* that is returned.
* <br>
* <B>NOTE:</B> Using this method may be expensive for some drivers due to the lack of underlying DBMS support.
*
* @return the description of a <code>ResultSet</code> object's columns or <code>null</code> if the driver cannot
* return a <code>ResultSetMetaData</code> object
* @throws SQLException if a database access error occurs or this method is called on a closed
* <code>PreparedStatement</code>
* @throws SQLFeatureNotSupportedException if the JDBC driver does not support this method
* @since 1.2
*/
public ResultSetMetaData getMetaData() throws SQLException {
checkClose();
Expand All @@ -398,7 +394,7 @@ public ResultSetMetaData getMetaData() throws SQLException {
return rs.getMetaData();
}
if (resultSetMetaData == null) {
setParametersData();
loadParametersData();
}
return resultSetMetaData;
}
Expand Down Expand Up @@ -450,18 +446,20 @@ public void setParameter(final int parameterIndex, final ParameterHolder holder)
public ParameterMetaData getParameterMetaData() throws SQLException {
checkClose();
if (parameterMetaData == null) {
setParametersData();
loadParametersData();
}
return parameterMetaData;
}

private void setParametersData() {
try (MariaDbPreparedStatementServer ssps = new MariaDbPreparedStatementServer(connection, this.sqlQuery,
private void loadParametersData() throws SQLSyntaxErrorException {
try (MariaDbPreparedStatementServer ssps = new MariaDbPreparedStatementServer(connection, sqlQuery,
ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY, Statement.NO_GENERATED_KEYS)) {
resultSetMetaData = ssps.getMetaData();
parameterMetaData = ssps.getParameterMetaData();
} catch (SQLSyntaxErrorException sqlSyntaxErrorException) {
//if error is due to wrong SQL syntax, better to throw exception immediately
throw sqlSyntaxErrorException;
} catch (SQLException sqle) {
//if statement cannot be prepared
parameterMetaData = new MariaDbParameterMetaData(null);
}
}
Expand Down
11 changes: 11 additions & 0 deletions src/main/java/org/mariadb/jdbc/internal/com/read/Buffer.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ public String readStringLengthEncoded(final Charset charset) {
return string;
}

/**
* Read String with defined length
*
* @param numberOfBytes raw data length.
* @return String value
*/
public String readString(final int numberOfBytes) {
position += numberOfBytes;
return new String(buf, position - numberOfBytes, numberOfBytes);
}

/**
* Read a short (2 bytes) from the buffer.
*
Expand Down
17 changes: 10 additions & 7 deletions src/main/java/org/mariadb/jdbc/internal/com/read/ErrorPacket.java
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,12 @@


public class ErrorPacket {

private static final String GENERAL_ERROR = "HY000";

private final short errorNumber;
private final byte sqlStateMarker;
private final byte[] sqlState;
private final String sqlState;
private final String message;

/**
Expand All @@ -68,16 +71,16 @@ public class ErrorPacket {
*/
public ErrorPacket(Buffer buffer) {
buffer.skipByte();
this.errorNumber = buffer.readShort();
this.sqlStateMarker = buffer.readByte();
errorNumber = buffer.readShort();
sqlStateMarker = buffer.readByte();
if (sqlStateMarker == '#') {
this.sqlState = buffer.readRawBytes(5);
this.message = buffer.readStringNullEnd(StandardCharsets.UTF_8);
sqlState = buffer.readString(5);
message = buffer.readStringNullEnd(StandardCharsets.UTF_8);
} else {
// Pre-4.1 message, still can be output in newer versions (e.g with 'Too many connections')
buffer.position -= 1;
this.message = new String(buffer.buf, buffer.position, buffer.limit - buffer.position, StandardCharsets.UTF_8);
this.sqlState = "HY000".getBytes();
message = new String(buffer.buf, buffer.position, buffer.limit - buffer.position, StandardCharsets.UTF_8);
sqlState = GENERAL_ERROR;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,12 +155,12 @@ private SQLException buildErrorException(Buffer buffer) {
ErrorPacket ep = new ErrorPacket(buffer);
String message = ep.getMessage();
if (1054 == ep.getErrorNumber()) {
return new SQLException("Error preparing query: " + message
return new SQLException(message
+ "\nIf column exists but type cannot be identified (example 'select ? `field1` from dual'). "
+ "Use CAST function to solve this problem (example 'select CAST(? as integer) `field1` from dual')",
ep.getSqlState(), ep.getErrorNumber());
} else {
return new SQLException("Error preparing query: " + message, ep.getSqlState(), ep.getErrorNumber());
return new SQLException(message, ep.getSqlState(), ep.getErrorNumber());
}
}

Expand Down
20 changes: 17 additions & 3 deletions src/test/java/org/mariadb/jdbc/DriverTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,12 @@ public void parameterMetaDataNotPreparable() throws SQLException {
//statement that cannot be prepared
try (PreparedStatement pstmt = sharedConnection.prepareStatement(
"select TMP.field1 from (select ? from dual) TMP")) {
ParameterMetaData parameterMetaData = pstmt.getParameterMetaData();
try {
parameterMetaData.getParameterCount();
ParameterMetaData parameterMetaData = pstmt.getParameterMetaData();
fail();
} catch (SQLException sqle) {
assertEquals("S1C00", sqle.getSQLState());
assertEquals("42S22", sqle.getSQLState());
assertTrue(sqle.getMessage().contains("Unknown column"));
}
}
Map<String, Integer> endingValues = loadVariables(stmt);
Expand All @@ -216,6 +216,20 @@ public void parameterMetaDataNotPreparable() throws SQLException {
assertEquals(initValues.get("Com_stmt_close"), endingValues.get("Com_stmt_close"));
}

@Test
public void parameterMetaDataReturnException() throws SQLException {
//statement that cannot be prepared
try (PreparedStatement preparedStatement = sharedConnection.prepareStatement("selec1t 2 from dual")) {
try {
preparedStatement.getParameterMetaData();
fail();
} catch (SQLException sqle) {
assertEquals("42000", sqle.getSQLState());
assertTrue(sqle.getMessage().contains(" You have an error in your SQL syntax"));
}
}
}

private Map<String, Integer> loadVariables(Statement stmt) throws SQLException {
Map<String, Integer> variables = new HashMap<>();
ResultSet rs = stmt.executeQuery("SHOW SESSION STATUS WHERE Variable_name in ('Prepared_stmt_count','Com_stmt_prepare', 'Com_stmt_close')");
Expand Down

0 comments on commit 2fe943c

Please sign in to comment.