Skip to content

Commit

Permalink
[CONJ-1229] Permit executeQuery commands to not return a result-set
Browse files Browse the repository at this point in the history
  • Loading branch information
rusher committed Jan 24, 2025
1 parent b2d3f59 commit 3331508
Show file tree
Hide file tree
Showing 8 changed files with 79 additions and 48 deletions.
5 changes: 2 additions & 3 deletions src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -251,9 +251,8 @@ public ResultSet executeQuery() throws SQLException {
if (currResult instanceof Result) {
return (Result) currResult;
}
if (Boolean.parseBoolean(
con.getContext().getConf().nonMappedOptions().getProperty("permitNoResults", "false"))) {
// for compatibility with pre 3.4.0 version

if (con.getContext().getConf().permitNoResults()) {
return new CompleteResult(
new ColumnDecoder[0], new byte[0][], con.getContext(), resultSetType);
}
Expand Down
31 changes: 31 additions & 0 deletions src/main/java/org/mariadb/jdbc/Configuration.java
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ public class Configuration {
private String restrictedAuth;
private String initSql;
private boolean pinGlobalTxToPhysicalConnection;
private boolean permitNoResults;

// socket
private String socketFactory;
Expand Down Expand Up @@ -386,6 +387,7 @@ private void initializeDatabaseConfig(Builder builder) {
this.permitRedirect = builder.permitRedirect == null || builder.permitRedirect;
this.pinGlobalTxToPhysicalConnection =
builder.pinGlobalTxToPhysicalConnection != null && builder.pinGlobalTxToPhysicalConnection;
this.permitNoResults = builder.permitNoResults == null || builder.permitNoResults;
this.blankTableNameMeta = builder.blankTableNameMeta != null && builder.blankTableNameMeta;
this.disconnectOnExpiredPasswords =
builder.disconnectOnExpiredPasswords == null || builder.disconnectOnExpiredPasswords;
Expand Down Expand Up @@ -554,6 +556,7 @@ public Builder toBuilder() {
.jdbcCompliantTruncation(this.jdbcCompliantTruncation)
.permitRedirect(this.permitRedirect)
.pinGlobalTxToPhysicalConnection(this.pinGlobalTxToPhysicalConnection)
.permitNoResults(this.permitNoResults)
.transactionIsolation(
transactionIsolation == null ? null : this.transactionIsolation.getValue())
.defaultFetchSize(this.defaultFetchSize)
Expand Down Expand Up @@ -1969,6 +1972,19 @@ public boolean pinGlobalTxToPhysicalConnection() {
return pinGlobalTxToPhysicalConnection;
}

/**
* Indicate if Statement/PreparedStatement.executeQuery for command that produce no result will
* return an exception or just an empty result-set
*
* <p>When enabled, command not returning no data will end returning an empty result-set When
* disabled, command not returning no data will end throwing an exception
*
* @return permitNoResults
*/
public boolean permitNoResults() {
return permitNoResults;
}

/**
* On deadlock exception, must driver execute additional commands to show innodb status in error
* description.
Expand Down Expand Up @@ -2287,6 +2303,7 @@ public static final class Builder implements Cloneable {
private Boolean jdbcCompliantTruncation;
private Boolean permitRedirect;
private Boolean pinGlobalTxToPhysicalConnection;
private Boolean permitNoResults;
private Integer defaultFetchSize;
private Integer maxQuerySizeToLog;
private Integer maxAllowedPacket;
Expand Down Expand Up @@ -3240,6 +3257,20 @@ public Builder pinGlobalTxToPhysicalConnection(Boolean pinGlobalTxToPhysicalConn
return this;
}

/**
* Indicate if Statement/PreparedStatement.executeQuery for command that produce no result will
* return an exception or just an empty result-set When enabled, command not returning no data
* will end returning an empty result-set When disabled, command not returning no data will end
* throwing an exception
*
* @param permitNoResults force reuse of same connection
* @return this {@link Builder}
*/
public Builder permitNoResults(Boolean permitNoResults) {
this.permitNoResults = permitNoResults;
return this;
}

/**
* On dead-lock exception must add innodb status in exception error message. If enabled, an
* additional command will be done to retrieve innodb status when dead-lock occurs.
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/mariadb/jdbc/DatabaseMetaData.java
Original file line number Diff line number Diff line change
Expand Up @@ -3645,7 +3645,7 @@ public ResultSet getTypeInfo() {
String[][] data = baseData;
if (connection.getContext().getVersion().isMariaDBServer()
&& connection.getContext().getVersion().versionGreaterOrEqual(10, 7, 0)) {
List datalist = new ArrayList<>(Arrays.asList(baseData));
List<String[]> datalist = new ArrayList<>(Arrays.asList(baseData));

datalist.add(
new String[] {
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/org/mariadb/jdbc/ServerPreparedStatement.java
Original file line number Diff line number Diff line change
Expand Up @@ -379,9 +379,8 @@ public ResultSet executeQuery() throws SQLException {
if ((currResult instanceof Result)) {
return (Result) currResult;
}
if (Boolean.parseBoolean(
con.getContext().getConf().nonMappedOptions().getProperty("permitNoResults", "false"))) {
// for compatibility with pre 3.4.0 version

if (con.getContext().getConf().permitNoResults()) {
return new CompleteResult(
new ColumnDecoder[0], new byte[0][], con.getContext(), resultSetType);
}
Expand Down
5 changes: 2 additions & 3 deletions src/main/java/org/mariadb/jdbc/Statement.java
Original file line number Diff line number Diff line change
Expand Up @@ -139,9 +139,8 @@ public ResultSet executeQuery(String sql) throws SQLException {
if (currResult instanceof Result) {
return (Result) currResult;
}
if (Boolean.parseBoolean(
con.getContext().getConf().nonMappedOptions().getProperty("permitNoResults", "false"))) {
// for compatibility with pre 3.4.0 version

if (con.getContext().getConf().permitNoResults()) {
return new CompleteResult(
new ColumnDecoder[0], new byte[0][], con.getContext(), resultSetType);
}
Expand Down
1 change: 1 addition & 0 deletions src/main/resources/driver.properties
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,4 @@ connectionCollation=indicate what utf8mb4 collation to use. if not set, server d
trustStore=File path of the trustStore file (similar to java System property \"javax.net.ssl.trustStore\". (legacy alias trustCertificateKeyStoreUrl). Use the specified file for trusted root certificates. When set, overrides serverSslCert.
trustStorePassword=Password for the trusted root certificate file (similar to java System property \"javax.net.ssl.trustStorePassword\").(legacy alias trustCertificateKeyStorePassword).
disconnectOnExpiredPasswords=On connection creation, indicate behavior when password is expired. When true (default) throw an expired password error. When false, connection succeed in "sandbox" mode, only queries related to password change are allowed.
permitNoResults=Indicate if Statement/PreparedStatement.executeQuery for command that produce no result will return an exception or just an empty result-set. When enabled, command not returning no data will end returning an empty result-set, when disabled, command not returning no data will end throwing an exception
20 changes: 10 additions & 10 deletions src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,13 @@ public void settingParameterBeforeOutRegistration() throws SQLException {
cstmt.setLong(1, 43L);
cstmt.executeUpdate();
assertEquals(86, cstmt.getLong(1));
Common.assertThrowsContains(
SQLException.class,
() -> cstmt.executeQuery(),
"PrepareStatement.executeQuery() command does NOT return a result-set as expected");
cstmt.setLong(1, 44L);
cstmt.execute();
assertEquals(88, cstmt.getLong(1));
ResultSet rs = cstmt.executeQuery();
assertFalse(rs.next());
}
try (Connection con = createCon("&permitNoResults=true")) {
try (Connection con = createCon("&permitNoResults=false")) {
try (CallableStatement cstmt = con.prepareCall("{ CALL multiply_by_2(?) }")) {
cstmt.setLong(1, 42L);
cstmt.registerOutParameter(1, Types.NUMERIC);
Expand All @@ -64,11 +65,10 @@ public void settingParameterBeforeOutRegistration() throws SQLException {
cstmt.setLong(1, 43L);
cstmt.executeUpdate();
assertEquals(86, cstmt.getLong(1));
cstmt.setLong(1, 44L);
cstmt.execute();
assertEquals(88, cstmt.getLong(1));
ResultSet rs = cstmt.executeQuery();
assertFalse(rs.next());
Common.assertThrowsContains(
SQLException.class,
() -> cstmt.executeQuery(),
"PrepareStatement.executeQuery() command does NOT return a result-set as expected");
}
}
}
Expand Down
58 changes: 30 additions & 28 deletions src/test/java/org/mariadb/jdbc/integration/StatementTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,25 +75,26 @@ public void ensureGetGeneratedKeysReturnsEmptyResult() throws SQLException {

@Test
public void ensureJdbcErrorWhenNoResultset() throws SQLException {
Statement stmt = sharedConn.createStatement();
stmt.execute("DO 1");
assertThrowsContains(
SQLException.class,
() -> stmt.executeQuery("DO 1"),
"Statement.executeQuery() command does NOT return a result-set as expected. Either use"
+ " Statement.execute(), Statement.executeUpdate(), or correct command");
stmt.execute("DO 1");
try (PreparedStatement ps =
sharedConn.prepareStatement("DO ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, 1);
ps.execute();
try (Connection con = createCon("&permitNoResults=false")) {
Statement stmt = con.createStatement();
stmt.execute("DO 1");
assertThrowsContains(
SQLException.class,
() -> ps.executeQuery(),
"PrepareStatement.executeQuery() command does NOT return a result-set as expected. Either"
+ " use PrepareStatement.execute(), PrepareStatement.executeUpdate(), or correct"
+ " command");
ps.execute();
() -> stmt.executeQuery("DO 1"),
"Statement.executeQuery() command does NOT return a result-set as expected. Either use"
+ " Statement.execute(), Statement.executeUpdate(), or correct command");
stmt.execute("DO 1");
try (PreparedStatement ps = con.prepareStatement("DO ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, 1);
ps.execute();
assertThrowsContains(
SQLException.class,
() -> ps.executeQuery(),
"PrepareStatement.executeQuery() command does NOT return a result-set as expected."
+ " Either use PrepareStatement.execute(), PrepareStatement.executeUpdate(), or"
+ " correct command");
ps.execute();
}
}
try (Connection con = createCon("&permitNoResults=true")) {
try (PreparedStatement ps = con.prepareStatement("DO ?", Statement.RETURN_GENERATED_KEYS)) {
Expand All @@ -104,17 +105,18 @@ public void ensureJdbcErrorWhenNoResultset() throws SQLException {
ps.execute();
}
}
try (PreparedStatement ps =
sharedConnBinary.prepareStatement("DO ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, 1);
ps.execute();
assertThrowsContains(
SQLException.class,
() -> ps.executeQuery(),
"PrepareStatement.executeQuery() command does NOT return a result-set as expected. Either"
+ " use PrepareStatement.execute(), PrepareStatement.executeUpdate(), or correct"
+ " command");
ps.execute();
try (Connection con = createCon("permitNoResults=false&useServerPrepStmts=true")) {
try (PreparedStatement ps = con.prepareStatement("DO ?", Statement.RETURN_GENERATED_KEYS)) {
ps.setInt(1, 1);
ps.execute();
assertThrowsContains(
SQLException.class,
() -> ps.executeQuery(),
"PrepareStatement.executeQuery() command does NOT return a result-set as expected."
+ " Either use PrepareStatement.execute(), PrepareStatement.executeUpdate(), or"
+ " correct command");
ps.execute();
}
}
try (Connection con = createCon("permitNoResults=true&useServerPrepStmts=true")) {
try (PreparedStatement ps = con.prepareStatement("DO ?", Statement.RETURN_GENERATED_KEYS)) {
Expand Down

0 comments on commit 3331508

Please sign in to comment.