diff --git a/CHANGELOG.md b/CHANGELOG.md index 4dbc59704..16668ca64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,20 @@ # Change Log +## [3.0.9](https://github.com/mariadb-corporation/mariadb-connector-j/tree/3.0.9) (Nov 2022) +[Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.8...3.0.9) +* 2.7.7 merge +* CONJ-1012 stored procedure register output parameter as null if set before registerOutParameter command +* CONJ-1017 Calendar possible race condition, cause wrong timestamp setting + +## [2.7.7](https://github.com/mariadb-corporation/mariadb-connector-j/tree/2.7.7) (Nov 2022) +[Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/compare/2.7.6...2.7.7) + +* CONJ-1021 GSSAPI authentication might result in connection reset +* CONJ-1019 DatabaseMetaData.getImportedKeys should return real value for PK_NAME column +* CONJ-1016 avoid splitting BULK command into multiple commands in case of prepareStatement.setNull() use +* CONJ-1011 correcting possible NPE when using statement.cancel() that coincide with statement.close() in another thread +* CONJ-1007 Socket file descriptors are leaked after connecting with unix socket if DB is not up running + ## [3.0.8](https://github.com/mariadb-corporation/mariadb-connector-j/tree/3.0.8) (Sept 2022) [Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-j/compare/3.0.7...3.0.8) diff --git a/pom.xml b/pom.xml index 83bf6e879..b026aa8dc 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ mariadb-java-client jar mariadb-java-client - 3.0.8 + 3.0.9 JDBC driver for MariaDB and MySQL https://mariadb.com/kb/en/mariadb/about-mariadb-connector-j/ diff --git a/src/main/java/org/mariadb/jdbc/BaseCallableStatement.java b/src/main/java/org/mariadb/jdbc/BaseCallableStatement.java index df5faa1b9..92d0e3d09 100644 --- a/src/main/java/org/mariadb/jdbc/BaseCallableStatement.java +++ b/src/main/java/org/mariadb/jdbc/BaseCallableStatement.java @@ -128,7 +128,8 @@ protected void outputResultFromRes(int i) throws SQLException { public void registerOutParameter(int parameterIndex, int sqlType) throws SQLException { checkIndex(parameterIndex); outputParameters.add(parameterIndex); - parameters.set(parameterIndex - 1, Parameter.NULL_PARAMETER); + if (!parameters.containsKey(parameterIndex - 1)) + parameters.set(parameterIndex - 1, Parameter.NULL_PARAMETER); } private void checkIndex(int index) throws SQLException { diff --git a/src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java b/src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java index 2d4927e48..dc4fb2b21 100644 --- a/src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java +++ b/src/main/java/org/mariadb/jdbc/ClientPreparedStatement.java @@ -509,7 +509,12 @@ public long[] executeLargeBatch() throws SQLException { @Override public void close() throws SQLException { if (prepareResult != null) { - prepareResult.close(this.con.getClient()); + lock.lock(); + try { + prepareResult.close(this.con.getClient()); + } finally { + lock.unlock(); + } } con.fireStatementClosed(this); super.close(); diff --git a/src/main/java/org/mariadb/jdbc/DatabaseMetaData.java b/src/main/java/org/mariadb/jdbc/DatabaseMetaData.java index 1debd8b84..c531bff6f 100644 --- a/src/main/java/org/mariadb/jdbc/DatabaseMetaData.java +++ b/src/main/java/org/mariadb/jdbc/DatabaseMetaData.java @@ -7,10 +7,7 @@ import java.sql.*; import java.sql.Statement; import java.text.ParseException; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Locale; +import java.util.*; import org.mariadb.jdbc.client.DataType; import org.mariadb.jdbc.client.result.CompleteResult; import org.mariadb.jdbc.client.result.Result; @@ -202,7 +199,10 @@ private int parseIdentifierList(char[] part, int startPos, List list */ private ResultSet getImportedKeys( String tableDef, String tableName, String catalog, org.mariadb.jdbc.Connection connection) - throws ParseException { + throws Exception, SQLException { + boolean importedKeysWithConstraintNames = + Boolean.parseBoolean( + conf.nonMappedOptions().getProperty("importedKeysWithConstraintNames", "true")); String[] columnNames = { "PKTABLE_CAT", "PKTABLE_SCHEM", "PKTABLE_NAME", "PKCOLUMN_NAME", "FKTABLE_CAT", "FKTABLE_SCHEM", @@ -255,7 +255,7 @@ private ResultSet getImportedKeys( onDeleteReferenceAction = getImportedKeyAction(referenceAction); } } - + Map> externalInfos = new HashMap<>(); for (int i = 0; i < primaryKeyCols.size(); i++) { String[] row = new String[columnNames.length]; @@ -274,7 +274,37 @@ private ResultSet getImportedKeys( row[9] = Integer.toString(onUpdateReferenceAction); // UPDATE_RULE row[10] = Integer.toString(onDeleteReferenceAction); // DELETE_RULE row[11] = constraintName.name; // FK_NAME - row[12] = null; // PK_NAME - unlike using information_schema, cannot know constraint name + if (importedKeysWithConstraintNames) { + String ext = + (pkTable.schema == null ? "" : quoteIdentifier(pkTable.schema) + ".") + + quoteIdentifier(pkTable.name); + if (!externalInfos.containsKey(ext)) { + externalInfos.put(ext, getExtImportedKeys(ext, connection)); + } + row[12] = null; // PK_NAME + Map externalInfo = externalInfos.get(ext); + if (externalInfo != null) { + for (Map.Entry entry : externalInfo.entrySet()) { + boolean foundAll = true; + + for (String keyPart : entry.getKey()) { + boolean foundKey = false; + for (Identifier keyCol : primaryKeyCols) { + if (keyCol.name.equals(keyPart)) { + foundKey = true; + break; + } + } + if (!foundKey) foundAll = false; + } + if (foundAll) { + row[12] = entry.getValue(); + } + } + } + } else { + row[12] = null; // PK_NAME + } row[13] = Integer.toString(DatabaseMetaData.importedKeyNotDeferrable); // DEFERRABILITY data.add(row); } @@ -301,6 +331,41 @@ private ResultSet getImportedKeys( columnNames, dataTypes, arr, connection.getContext(), ColumnFlags.PRIMARY_KEY); } + private Map getExtImportedKeys( + String tableName, org.mariadb.jdbc.Connection connection) throws SQLException { + ResultSet rs = connection.createStatement().executeQuery("SHOW CREATE TABLE " + tableName); + rs.next(); + String refTableDef = rs.getString(2); + Map res = new HashMap<>(); + String[] parts = refTableDef.split("\n"); + for (int i = 1; i < parts.length - 1; i++) { + String part = parts[i].trim(); + if (part.startsWith("`")) { + // field + continue; + } + if (part.startsWith("PRIMARY KEY") || part.startsWith("UNIQUE KEY")) { + String name = "PRIMARY"; + if (part.indexOf("`") < part.indexOf("(")) { + int offset = part.indexOf("`"); + name = part.substring(offset + 1, part.indexOf("`", offset + 1)); + } + + String subPart = part.substring(part.indexOf("(") + 1, part.lastIndexOf(")")); + List cols = new ArrayList<>(); + int pos = 0; + while (pos < subPart.length()) { + pos = subPart.indexOf("`", pos); + int endpos = subPart.indexOf("`", pos + 1); + cols.add(subPart.substring(pos + 1, endpos)); + pos = endpos + 1; + } + res.put(cols.toArray(new String[0]), name); + } + } + return res; + } + /** * Retrieves a description of the primary key columns that are referenced by the given table's * foreign key columns (the primary keys imported by a table). They are ordered by PKTABLE_CAT, diff --git a/src/main/java/org/mariadb/jdbc/message/client/BulkExecutePacket.java b/src/main/java/org/mariadb/jdbc/message/client/BulkExecutePacket.java index 3770f8fcc..d0a1a69d7 100644 --- a/src/main/java/org/mariadb/jdbc/message/client/BulkExecutePacket.java +++ b/src/main/java/org/mariadb/jdbc/message/client/BulkExecutePacket.java @@ -162,7 +162,8 @@ public int encode(Writer writer, Context context, Prepare newPrepareResult) // ensure type has not changed for (int i = 0; i < parameterCount; i++) { if (parameterHeaderType[i].getBinaryEncodeType() - != parameters.get(i).getBinaryEncodeType()) { + != parameters.get(i).getBinaryEncodeType() + && !parameters.get(i).isNull()) { writer.flush(); // reset header type for (int j = 0; j < parameterCount; j++) { diff --git a/src/main/java/org/mariadb/jdbc/plugin/authentication/addon/gssapi/StandardGssapiAuthentication.java b/src/main/java/org/mariadb/jdbc/plugin/authentication/addon/gssapi/StandardGssapiAuthentication.java index 331cdaec7..14ffcbd9b 100644 --- a/src/main/java/org/mariadb/jdbc/plugin/authentication/addon/gssapi/StandardGssapiAuthentication.java +++ b/src/main/java/org/mariadb/jdbc/plugin/authentication/addon/gssapi/StandardGssapiAuthentication.java @@ -81,8 +81,8 @@ public void authenticate( byte[] inToken = new byte[0]; byte[] outToken; - while (!context.isEstablished()) { + while (true) { outToken = context.initSecContext(inToken, 0, inToken.length); // Send a token to the peer if one was generated by acceptSecContext @@ -90,11 +90,12 @@ public void authenticate( out.writeBytes(outToken); out.flush(); } - if (!context.isEstablished()) { - ReadableByteBuf buf = in.readPacket(true); - inToken = new byte[buf.readableBytes()]; - buf.readBytes(inToken); + if (context.isEstablished()) { + break; } + ReadableByteBuf buf = in.readPacket(true); + inToken = new byte[buf.readableBytes()]; + buf.readBytes(inToken); } } catch (GSSException le) { diff --git a/src/main/java/org/mariadb/jdbc/plugin/authentication/addon/gssapi/WindowsNativeSspiAuthentication.java b/src/main/java/org/mariadb/jdbc/plugin/authentication/addon/gssapi/WindowsNativeSspiAuthentication.java index 63a78b079..e29a37a9c 100644 --- a/src/main/java/org/mariadb/jdbc/plugin/authentication/addon/gssapi/WindowsNativeSspiAuthentication.java +++ b/src/main/java/org/mariadb/jdbc/plugin/authentication/addon/gssapi/WindowsNativeSspiAuthentication.java @@ -37,20 +37,23 @@ public void authenticate( // Step 1: send token to server byte[] tokenForTheServerOnTheClient = clientContext.getToken(); - out.writeBytes(tokenForTheServerOnTheClient); - out.flush(); - - // Step 2: read server response token - if (clientContext.isContinue()) { - ReadableByteBuf buf = in.readPacket(true); - byte[] tokenForTheClientOnTheServer = new byte[buf.readableBytes()]; - buf.readBytes(tokenForTheClientOnTheServer); - Sspi.SecBufferDesc continueToken = - new SspiUtil.ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, tokenForTheClientOnTheServer); - clientContext.initialize(clientContext.getHandle(), continueToken, servicePrincipalName); + if (tokenForTheServerOnTheClient != null && tokenForTheServerOnTheClient.length > 0) { + out.writeBytes(tokenForTheServerOnTheClient); + out.flush(); + } + if (!clientContext.isContinue()) { + break; } - } while (clientContext.isContinue()); + // Step 2: read server response token + ReadableByteBuf buf = in.readPacket(true); + byte[] tokenForTheClientOnTheServer = new byte[buf.readableBytes()]; + buf.readBytes(tokenForTheClientOnTheServer); + Sspi.SecBufferDesc continueToken = + new SspiUtil.ManagedSecBufferDesc(Sspi.SECBUFFER_TOKEN, tokenForTheClientOnTheServer); + clientContext.initialize(clientContext.getHandle(), continueToken, servicePrincipalName); + + } while (true); clientContext.dispose(); } diff --git a/src/main/java/org/mariadb/jdbc/plugin/codec/DateCodec.java b/src/main/java/org/mariadb/jdbc/plugin/codec/DateCodec.java index ba9b98bde..9ae3c6430 100644 --- a/src/main/java/org/mariadb/jdbc/plugin/codec/DateCodec.java +++ b/src/main/java/org/mariadb/jdbc/plugin/codec/DateCodec.java @@ -215,11 +215,14 @@ public void encodeText( public void encodeBinary(Writer encoder, Object value, Calendar providedCal, Long maxLength) throws IOException { Calendar cal = providedCal == null ? Calendar.getInstance() : providedCal; - cal.setTimeInMillis(((java.util.Date) value).getTime()); - encoder.writeByte(4); // length - encoder.writeShort((short) cal.get(Calendar.YEAR)); - encoder.writeByte(((cal.get(Calendar.MONTH) + 1) & 0xff)); - encoder.writeByte((cal.get(Calendar.DAY_OF_MONTH) & 0xff)); + synchronized (cal) { + cal.clear(); + cal.setTimeInMillis(((java.util.Date) value).getTime()); + encoder.writeByte(4); // length + encoder.writeShort((short) cal.get(Calendar.YEAR)); + encoder.writeByte(((cal.get(Calendar.MONTH) + 1) & 0xff)); + encoder.writeByte((cal.get(Calendar.DAY_OF_MONTH) & 0xff)); + } } public int getBinaryEncodeType() { diff --git a/src/main/java/org/mariadb/jdbc/plugin/codec/TimeCodec.java b/src/main/java/org/mariadb/jdbc/plugin/codec/TimeCodec.java index 67728a4e0..5db864c48 100644 --- a/src/main/java/org/mariadb/jdbc/plugin/codec/TimeCodec.java +++ b/src/main/java/org/mariadb/jdbc/plugin/codec/TimeCodec.java @@ -231,23 +231,26 @@ public void encodeText( public void encodeBinary(Writer encoder, Object value, Calendar providedCal, Long maxLength) throws IOException { Calendar cal = providedCal == null ? Calendar.getInstance() : providedCal; - cal.setTime((Time) value); - cal.set(Calendar.DAY_OF_MONTH, 1); - if (cal.get(Calendar.MILLISECOND) > 0) { - encoder.writeByte((byte) 12); - encoder.writeByte((byte) 0); - encoder.writeInt(0); - encoder.writeByte((byte) cal.get(Calendar.HOUR_OF_DAY)); - encoder.writeByte((byte) cal.get(Calendar.MINUTE)); - encoder.writeByte((byte) cal.get(Calendar.SECOND)); - encoder.writeInt(cal.get(Calendar.MILLISECOND) * 1000); - } else { - encoder.writeByte((byte) 8); // length - encoder.writeByte((byte) 0); - encoder.writeInt(0); - encoder.writeByte((byte) cal.get(Calendar.HOUR_OF_DAY)); - encoder.writeByte((byte) cal.get(Calendar.MINUTE)); - encoder.writeByte((byte) cal.get(Calendar.SECOND)); + synchronized (cal) { + cal.clear(); + cal.setTime((Time) value); + cal.set(Calendar.DAY_OF_MONTH, 1); + if (cal.get(Calendar.MILLISECOND) > 0) { + encoder.writeByte((byte) 12); + encoder.writeByte((byte) 0); + encoder.writeInt(0); + encoder.writeByte((byte) cal.get(Calendar.HOUR_OF_DAY)); + encoder.writeByte((byte) cal.get(Calendar.MINUTE)); + encoder.writeByte((byte) cal.get(Calendar.SECOND)); + encoder.writeInt(cal.get(Calendar.MILLISECOND) * 1000); + } else { + encoder.writeByte((byte) 8); // length + encoder.writeByte((byte) 0); + encoder.writeInt(0); + encoder.writeByte((byte) cal.get(Calendar.HOUR_OF_DAY)); + encoder.writeByte((byte) cal.get(Calendar.MINUTE)); + encoder.writeByte((byte) cal.get(Calendar.SECOND)); + } } } diff --git a/src/main/java/org/mariadb/jdbc/plugin/codec/TimestampCodec.java b/src/main/java/org/mariadb/jdbc/plugin/codec/TimestampCodec.java index 440051a72..70db6cd37 100644 --- a/src/main/java/org/mariadb/jdbc/plugin/codec/TimestampCodec.java +++ b/src/main/java/org/mariadb/jdbc/plugin/codec/TimestampCodec.java @@ -352,26 +352,29 @@ public void encodeText( @Override public void encodeBinary(Writer encoder, Object value, Calendar providedCal, Long maxLength) throws IOException { - Calendar cal = providedCal == null ? Calendar.getInstance() : providedCal; Timestamp ts = (Timestamp) value; - cal.setTimeInMillis(ts.getTime()); - if (ts.getNanos() == 0) { - encoder.writeByte(7); // length - encoder.writeShort((short) cal.get(Calendar.YEAR)); - encoder.writeByte((cal.get(Calendar.MONTH) + 1)); - encoder.writeByte(cal.get(Calendar.DAY_OF_MONTH)); - encoder.writeByte(cal.get(Calendar.HOUR_OF_DAY)); - encoder.writeByte(cal.get(Calendar.MINUTE)); - encoder.writeByte(cal.get(Calendar.SECOND)); - } else { - encoder.writeByte(11); // length - encoder.writeShort((short) cal.get(Calendar.YEAR)); - encoder.writeByte((cal.get(Calendar.MONTH) + 1)); - encoder.writeByte(cal.get(Calendar.DAY_OF_MONTH)); - encoder.writeByte(cal.get(Calendar.HOUR_OF_DAY)); - encoder.writeByte(cal.get(Calendar.MINUTE)); - encoder.writeByte(cal.get(Calendar.SECOND)); - encoder.writeInt(ts.getNanos() / 1000); + Calendar cal = providedCal == null ? Calendar.getInstance() : providedCal; + synchronized (cal) { + cal.clear(); + cal.setTimeInMillis(ts.getTime()); + if (ts.getNanos() == 0) { + encoder.writeByte(7); // length + encoder.writeShort((short) cal.get(Calendar.YEAR)); + encoder.writeByte((cal.get(Calendar.MONTH) + 1)); + encoder.writeByte(cal.get(Calendar.DAY_OF_MONTH)); + encoder.writeByte(cal.get(Calendar.HOUR_OF_DAY)); + encoder.writeByte(cal.get(Calendar.MINUTE)); + encoder.writeByte(cal.get(Calendar.SECOND)); + } else { + encoder.writeByte(11); // length + encoder.writeShort((short) cal.get(Calendar.YEAR)); + encoder.writeByte((cal.get(Calendar.MONTH) + 1)); + encoder.writeByte(cal.get(Calendar.DAY_OF_MONTH)); + encoder.writeByte(cal.get(Calendar.HOUR_OF_DAY)); + encoder.writeByte(cal.get(Calendar.MINUTE)); + encoder.writeByte(cal.get(Calendar.SECOND)); + encoder.writeInt(ts.getNanos() / 1000); + } } } diff --git a/src/test/java/org/mariadb/jdbc/integration/BatchTest.java b/src/test/java/org/mariadb/jdbc/integration/BatchTest.java index ee2541674..00178b218 100644 --- a/src/test/java/org/mariadb/jdbc/integration/BatchTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/BatchTest.java @@ -7,6 +7,8 @@ import static org.junit.jupiter.api.Assertions.*; import java.sql.*; +import java.util.Calendar; +import java.util.stream.Stream; import org.junit.jupiter.api.*; import org.mariadb.jdbc.Connection; import org.mariadb.jdbc.Statement; @@ -20,11 +22,14 @@ public static void beforeAll2() throws SQLException { stmt.execute( "CREATE TABLE BatchTest (t1 int not null primary key auto_increment, t2 LONGTEXT)"); createSequenceTables(); + stmt.execute("CREATE TABLE timestampCal(id int, val TIMESTAMP)"); } @AfterAll public static void after2() throws SQLException { - sharedConn.createStatement().execute("DROP TABLE IF EXISTS BatchTest"); + Statement stmt = sharedConn.createStatement(); + stmt.execute("DROP TABLE IF EXISTS timestampCal"); + stmt.execute("DROP TABLE IF EXISTS BatchTest"); } @Test @@ -122,10 +127,20 @@ public void differentParameterType(Connection con, boolean expectSuccessUnknown) prep.setInt(1, 2); prep.setInt(2, 2); prep.addBatch(); + prep.setInt(1, 3); + prep.setNull(2, Types.INTEGER); + prep.addBatch(); int[] res = prep.executeBatch(); - assertEquals(2, res.length); - assertEquals(1, res[0]); - assertEquals(1, res[1]); + assertEquals(3, res.length); + if (expectSuccessUnknown) { + assertEquals(Statement.SUCCESS_NO_INFO, res[0]); + assertEquals(Statement.SUCCESS_NO_INFO, res[1]); + assertEquals(Statement.SUCCESS_NO_INFO, res[2]); + } else { + assertEquals(1, res[0]); + assertEquals(1, res[1]); + assertEquals(1, res[2]); + } } ResultSet rs = stmt.executeQuery("SELECT * FROM BatchTest"); assertTrue(rs.next()); @@ -134,6 +149,9 @@ public void differentParameterType(Connection con, boolean expectSuccessUnknown) assertTrue(rs.next()); assertEquals(2, rs.getInt(1)); assertEquals("2", rs.getString(2)); + assertTrue(rs.next()); + assertEquals(3, rs.getInt(1)); + assertNull(rs.getString(2)); assertFalse(rs.next()); stmt.execute("TRUNCATE BatchTest"); @@ -401,4 +419,74 @@ private void batchWithError(Connection con) throws SQLException { assertThrows(BatchUpdateException.class, prep::executeBatch); } } + + private class TimestampCal { + private Timestamp val; + private int id; + + public TimestampCal(Timestamp val, int id) { + this.val = val; + this.id = id; + } + + public Timestamp getVal() { + return val; + } + + public int getId() { + return id; + } + + @Override + public String toString() { + return "TimestampCal{" + "val=" + val + ", id=" + id + '}'; + } + } + + @Test + public void ensureCalendarSync() throws SQLException { + Assumptions.assumeTrue(isMariaDBServer() && !isXpand()); + // to ensure that calendar is use at the same time, using BULK command + TimestampCal[] t1 = new TimestampCal[50]; + for (int i = 0; i < 50; i++) { + t1[i] = new TimestampCal(Timestamp.valueOf((1970 + i) + "-01-31 12:00:00.0"), i); + } + TimestampCal[] t2 = new TimestampCal[50]; + for (int i = 0; i < 50; i++) { + t2[i] = new TimestampCal(Timestamp.valueOf((1970 + i) + "-12-01 01:12:15.0"), i + 50); + } + + Calendar cal = Calendar.getInstance(); + + int inserts = Stream.of(t1, t2).parallel().mapToInt(l -> insertTimestamp(l, cal)).sum(); + assertEquals(100, inserts); + Statement stmt = sharedConn.createStatement(); + ResultSet rs = stmt.executeQuery("SELECT * FROM timestampCal order by ID"); + for (int i = 0; i < 50; i++) { + rs.next(); + assertEquals(t1[i].getVal().toString(), rs.getTimestamp(2, cal).toString()); + } + for (int i = 0; i < 50; i++) { + rs.next(); + assertEquals(t2[i].getVal().toString(), rs.getTimestamp(2, cal).toString()); + } + } + + private int insertTimestamp(TimestampCal[] vals, Calendar cal) { + try (Connection con = createCon()) { + try (PreparedStatement prep = + con.prepareStatement("INSERT INTO timestampCal(val, id) VALUES (?,?)")) { + for (int i = 0; i < vals.length; i++) { + System.out.println(vals[i]); + prep.setTimestamp(1, vals[i].getVal(), cal); + prep.setInt(2, vals[i].getId()); + prep.addBatch(); + } + return prep.executeBatch().length; + } + } catch (SQLException e) { + e.printStackTrace(); + return -1; + } + } } diff --git a/src/test/java/org/mariadb/jdbc/integration/Common.java b/src/test/java/org/mariadb/jdbc/integration/Common.java index d37e56b86..94fce6541 100644 --- a/src/test/java/org/mariadb/jdbc/integration/Common.java +++ b/src/test/java/org/mariadb/jdbc/integration/Common.java @@ -119,6 +119,12 @@ public static boolean minVersion(int major, int minor, int patch) { return sharedConn.getContext().getVersion().versionGreaterOrEqual(major, minor, patch); } + public static boolean exactVersion(int major, int minor, int patch) { + return sharedConn.getContext().getVersion().getMajorVersion() == major + && sharedConn.getContext().getVersion().getMinorVersion() == minor + && sharedConn.getContext().getVersion().getPatchVersion() == patch; + } + public static Connection createCon() throws SQLException { return (Connection) DriverManager.getConnection(mDefUrl); } diff --git a/src/test/java/org/mariadb/jdbc/integration/ConnectionTest.java b/src/test/java/org/mariadb/jdbc/integration/ConnectionTest.java index cfcf53ec9..145141864 100644 --- a/src/test/java/org/mariadb/jdbc/integration/ConnectionTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/ConnectionTest.java @@ -862,6 +862,9 @@ public void pamAuthPlugin() throws Throwable { } stmt.execute("CREATE USER '" + pamUser + "'@'%' IDENTIFIED VIA pam USING 'mariadb'"); stmt.execute("GRANT SELECT ON *.* TO '" + pamUser + "'@'%' IDENTIFIED VIA pam"); + + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); stmt.execute("FLUSH PRIVILEGES"); int testPort = port; @@ -993,6 +996,8 @@ public void localSocket() throws Exception { stmt.execute("CREATE USER testSocket IDENTIFIED BY 'heyPassw!µ20§rd'"); stmt.execute("GRANT SELECT on *.* to testSocket IDENTIFIED BY 'heyPassw!µ20§rd'"); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); stmt.execute("FLUSH PRIVILEGES"); String url = diff --git a/src/test/java/org/mariadb/jdbc/integration/CredentialPluginTest.java b/src/test/java/org/mariadb/jdbc/integration/CredentialPluginTest.java index bc32aca20..fbf197c59 100644 --- a/src/test/java/org/mariadb/jdbc/integration/CredentialPluginTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/CredentialPluginTest.java @@ -50,6 +50,8 @@ public static void beforeTest() throws SQLException { stmt.execute("CREATE USER 'identityUser'@'%' IDENTIFIED BY '!Passw0rd3Works'"); stmt.execute("GRANT SELECT ON " + sharedConn.getCatalog() + ".* TO 'identityUser'@'%'"); } + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); stmt.execute("FLUSH PRIVILEGES"); } diff --git a/src/test/java/org/mariadb/jdbc/integration/DataSourceTest.java b/src/test/java/org/mariadb/jdbc/integration/DataSourceTest.java index 47f74bb02..0144b0e33 100644 --- a/src/test/java/org/mariadb/jdbc/integration/DataSourceTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/DataSourceTest.java @@ -150,6 +150,8 @@ public void switchUser() throws SQLException { + sharedConn.getCatalog() + ".* TO 'dsUser'@'%' IDENTIFIED BY 'MySup8%rPassw@ord'"); } + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); stmt.execute("FLUSH PRIVILEGES"); DataSource ds = new MariaDbDataSource(mDefUrl + "&allowPublicKeyRetrieval"); diff --git a/src/test/java/org/mariadb/jdbc/integration/DatabaseMetadataTest.java b/src/test/java/org/mariadb/jdbc/integration/DatabaseMetadataTest.java index 9d4f8b3a4..2320d85ab 100644 --- a/src/test/java/org/mariadb/jdbc/integration/DatabaseMetadataTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/DatabaseMetadataTest.java @@ -236,15 +236,21 @@ public void functionColumns() throws SQLException { /** Same as getImportedKeys, with one foreign key in a table in another catalog. */ @Test public void getImportedKeys() throws Exception { - getImportedKeys(sharedConn); + getImportedKeys(sharedConn, true); try (org.mariadb.jdbc.Connection con = createCon()) { java.sql.Statement stmt = con.createStatement(); stmt.execute("SET sql_mode = concat(@@sql_mode,',NO_BACKSLASH_ESCAPES')"); - getImportedKeys(con); + getImportedKeys(con, true); + } + try (org.mariadb.jdbc.Connection con = createCon("importedKeysWithConstraintNames=false")) { + java.sql.Statement stmt = con.createStatement(); + stmt.execute("SET sql_mode = concat(@@sql_mode,',NO_BACKSLASH_ESCAPES')"); + getImportedKeys(con, false); } } - private void getImportedKeys(org.mariadb.jdbc.Connection con) throws Exception { + private void getImportedKeys( + org.mariadb.jdbc.Connection con, boolean importedKeysWithConstraintNames) throws Exception { // cancel for MySQL 8.0, since CASCADE with I_S give importedKeySetDefault, not // importedKeyCascade // Assumptions.assumeFalse(!isMariaDBServer() && minVersion(8, 0, 0)); @@ -349,7 +355,9 @@ Get result sets using either method and compare (ignore minor differences INT vs assertEquals(DatabaseMetaData.importedKeyCascade, rs.getInt("DELETE_RULE")); assertEquals("product order 1_ibfk_1", rs.getString("FK_NAME")); // with show, meta don't know contraint name - assertEquals((i == 0) ? null : "unik_name", rs.getString("PK_NAME")); + assertEquals( + (i == 0 && !importedKeysWithConstraintNames) ? null : "unik_name", + rs.getString("PK_NAME")); assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getInt("DEFERRABILITY")); assertTrue(rs.next()); @@ -366,7 +374,9 @@ Get result sets using either method and compare (ignore minor differences INT vs assertEquals(DatabaseMetaData.importedKeyCascade, rs.getInt("DELETE_RULE")); assertEquals("product order 1_ibfk_1", rs.getString("FK_NAME")); // with show, meta don't know contraint name - assertEquals((i == 0) ? null : "unik_name", rs.getString("PK_NAME")); + assertEquals( + (i == 0 && !importedKeysWithConstraintNames) ? null : "unik_name", + rs.getString("PK_NAME")); assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getInt("DEFERRABILITY")); assertTrue(rs.next()); @@ -388,7 +398,8 @@ Get result sets using either method and compare (ignore minor differences INT vs } assertEquals("product order 1_ibfk_2", rs.getString("FK_NAME")); // with show, meta don't know contraint name - assertEquals((i == 0) ? null : "PRIMARY", rs.getString("PK_NAME")); + assertEquals( + (i == 0 && !importedKeysWithConstraintNames) ? null : "PRIMARY", rs.getString("PK_NAME")); assertEquals(DatabaseMetaData.importedKeyNotDeferrable, rs.getInt("DEFERRABILITY")); } diff --git a/src/test/java/org/mariadb/jdbc/integration/GssapiAuthenticationTest.java b/src/test/java/org/mariadb/jdbc/integration/GssapiAuthenticationTest.java new file mode 100644 index 000000000..d5df69d8d --- /dev/null +++ b/src/test/java/org/mariadb/jdbc/integration/GssapiAuthenticationTest.java @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: LGPL-2.1-or-later +// Copyright (c) 2012-2014 Monty Program Ab +// Copyright (c) 2015-2021 MariaDB Corporation Ab + +package org.mariadb.jdbc.integration; + +import java.sql.*; +import org.junit.jupiter.api.Assumptions; +import org.junit.jupiter.api.Test; + +public class GssapiAuthenticationTest extends Common { + + @Test + public void nativePassword() throws Exception { + Assumptions.assumeTrue(isWindows()); + Statement stmt = sharedConn.createStatement(); + try { + stmt.execute("INSTALL SONAME 'auth_gssapi'"); + } catch (SQLException e) { + // eat + } + System.out.println("user name:" + System.getProperty("user.name")); + stmt.execute("CREATE USER " + System.getProperty("user.name") + " IDENTIFIED VIA gssapi"); + stmt.execute("GRANT ALL PRIVILEGES ON *.* TO " + System.getProperty("user.name")); + + String gssapiUrl = String.format("jdbc:mariadb://%s:%s/%s", hostname, port, database); + try (Connection con = DriverManager.getConnection(gssapiUrl)) { + con.createStatement().execute("SELECT 1"); + } + } +} diff --git a/src/test/java/org/mariadb/jdbc/integration/PoolDataSourceTest.java b/src/test/java/org/mariadb/jdbc/integration/PoolDataSourceTest.java index e9248d5dc..46c54bfc2 100644 --- a/src/test/java/org/mariadb/jdbc/integration/PoolDataSourceTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/PoolDataSourceTest.java @@ -53,6 +53,8 @@ public static void beforeClassDataSourceTest() throws SQLException { stmt.execute( "CREATE TABLE testResetRollback(id int not null primary key auto_increment, test varchar(20))"); stmt.execute("FLUSH TABLES"); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); stmt.execute("FLUSH PRIVILEGES"); } diff --git a/src/test/java/org/mariadb/jdbc/integration/PooledConnectionTest.java b/src/test/java/org/mariadb/jdbc/integration/PooledConnectionTest.java index 99ffd8777..2b4a3e895 100644 --- a/src/test/java/org/mariadb/jdbc/integration/PooledConnectionTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/PooledConnectionTest.java @@ -240,6 +240,8 @@ public void testPooledConnectionStatementError() throws Exception { + sharedConn.getCatalog() + ".* TO 'dsUser'@'%' IDENTIFIED BY 'MySup8%rPassw@ord'"); } + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); stmt.execute("FLUSH PRIVILEGES"); ConnectionPoolDataSource ds = new MariaDbDataSource(mDefUrl); diff --git a/src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java b/src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java index 49aec3a43..5271caa00 100644 --- a/src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/ProcedureTest.java @@ -29,6 +29,7 @@ public class ProcedureTest extends Common { public static void drop() throws SQLException { Statement stmt = sharedConn.createStatement(); stmt.execute("DROP TABLE IF EXISTS procedure_test"); + stmt.execute("DROP PROCEDURE IF EXISTS multiply_by_2"); } @BeforeAll @@ -36,6 +37,17 @@ public static void beforeAll2() throws SQLException { drop(); Statement stmt = sharedConn.createStatement(); stmt.execute("CREATE TABLE procedure_test (t0 int)"); + stmt.execute("CREATE PROCEDURE multiply_by_2 (INOUT t1 INT) BEGIN \nset t1 = t1 * 2;\nEND"); + } + + @Test + public void settingParameterBeforeOutRegistration() throws SQLException { + try (CallableStatement cstmt = sharedConn.prepareCall("{ CALL multiply_by_2(?) }")) { + cstmt.setLong(1, 42L); + cstmt.registerOutParameter(1, Types.NUMERIC); + cstmt.executeQuery(); + assertEquals(84, cstmt.getLong(1)); + } } @Test diff --git a/src/test/java/org/mariadb/jdbc/integration/Sha256AuthenticationTest.java b/src/test/java/org/mariadb/jdbc/integration/Sha256AuthenticationTest.java index 7ac1981ac..50b9c52ea 100644 --- a/src/test/java/org/mariadb/jdbc/integration/Sha256AuthenticationTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/Sha256AuthenticationTest.java @@ -77,6 +77,8 @@ public static void init() throws Exception { stmt.execute("GRANT ALL PRIVILEGES ON *.* TO 'cachingSha256User2'@'%'"); stmt.execute("GRANT ALL PRIVILEGES ON *.* TO 'cachingSha256User3'@'%'"); stmt.execute("GRANT ALL PRIVILEGES ON *.* TO 'cachingSha256User4'@'%'"); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); stmt.execute("FLUSH PRIVILEGES"); } @@ -106,7 +108,13 @@ public void nativePassword() throws Exception { stmt.execute( "CREATE USER tmpUser@'%' IDENTIFIED WITH mysql_native_password BY '!Passw0rd3Works'"); stmt.execute("grant all on `" + sharedConn.getCatalog() + "`.* TO tmpUser@'%'"); - stmt.execute("FLUSH PRIVILEGES"); // reset cache + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); + + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); + stmt.execute("FLUSH PRIVILEGES"); + try (Connection con = createCon("user=tmpUser&password=!Passw0rd3Works")) { con.isValid(1); } @@ -121,7 +129,10 @@ public void nativePassword() throws Exception { public void cachingSha256Empty() throws Exception { Assumptions.assumeTrue( !isWindows() && !isMariaDBServer() && rsaPublicKey != null && minVersion(8, 0, 0)); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); sharedConn.createStatement().execute("FLUSH PRIVILEGES"); // reset cache + try (Connection con = createCon("user=cachingSha256User2&allowPublicKeyRetrieval&password=")) { con.isValid(1); } @@ -131,7 +142,10 @@ public void cachingSha256Empty() throws Exception { public void wrongRsaPath() throws Exception { Assumptions.assumeTrue( !isWindows() && !isMariaDBServer() && rsaPublicKey != null && minVersion(8, 0, 0)); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); sharedConn.createStatement().execute("FLUSH PRIVILEGES"); // reset cache + File tempFile = File.createTempFile("log", ".tmp"); Common.assertThrowsContains( SQLException.class, @@ -146,6 +160,8 @@ public void wrongRsaPath() throws Exception { @Test public void cachingSha256Allow() throws Exception { Assumptions.assumeTrue(!isMariaDBServer() && rsaPublicKey != null && minVersion(8, 0, 0)); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); sharedConn.createStatement().execute("FLUSH PRIVILEGES"); // reset cache try (Connection con = createCon("user=cachingSha256User3&allowPublicKeyRetrieval&password=!Passw0rd3Works")) { @@ -156,6 +172,8 @@ public void cachingSha256Allow() throws Exception { @Test public void cachingSha256PluginTest() throws Exception { Assumptions.assumeTrue(!isMariaDBServer() && rsaPublicKey != null && minVersion(8, 0, 0)); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); sharedConn.createStatement().execute("FLUSH PRIVILEGES"); // reset cache try (Connection con = @@ -192,6 +210,8 @@ public void cachingSha256PluginTest() throws Exception { @Test public void cachingSha256PluginTestWithoutServerRsaKey() throws Exception { Assumptions.assumeTrue(!isWindows() && minVersion(8, 0, 0)); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); sharedConn.createStatement().execute("FLUSH PRIVILEGES"); // reset cache try (Connection con = createCon("user=cachingSha256User&password=!Passw0rd3Works&allowPublicKeyRetrieval")) { @@ -202,6 +222,8 @@ public void cachingSha256PluginTestWithoutServerRsaKey() throws Exception { @Test public void cachingSha256PluginTestException() throws Exception { Assumptions.assumeTrue(!isMariaDBServer() && minVersion(8, 0, 0)); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); sharedConn.createStatement().execute("FLUSH PRIVILEGES"); // reset cache Common.assertThrowsContains( diff --git a/src/test/java/org/mariadb/jdbc/integration/SslTest.java b/src/test/java/org/mariadb/jdbc/integration/SslTest.java index b059ca48a..d5205d9a1 100644 --- a/src/test/java/org/mariadb/jdbc/integration/SslTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/SslTest.java @@ -44,6 +44,8 @@ public static void beforeAll2() throws SQLException { createSslUser("mutualAuthUser", "REQUIRE X509"); Statement stmt = sharedConn.createStatement(); + // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now + Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); stmt.execute("FLUSH PRIVILEGES"); sslPort = System.getenv("TEST_MAXSCALE_TLS_PORT") == null diff --git a/src/test/java/org/mariadb/jdbc/integration/UpdateResultSetTest.java b/src/test/java/org/mariadb/jdbc/integration/UpdateResultSetTest.java index 9738dfb02..5abf9b4b9 100644 --- a/src/test/java/org/mariadb/jdbc/integration/UpdateResultSetTest.java +++ b/src/test/java/org/mariadb/jdbc/integration/UpdateResultSetTest.java @@ -240,6 +240,7 @@ public void testMeta() throws Exception { + "`t1` VARCHAR(50) NOT NULL," + "`t2` VARCHAR(50) NULL default 'default-value'," + "PRIMARY KEY (`id`))"); + stmt.execute("START TRANSACTION"); // if MAXSCALE ensure using WRITER stmt.executeQuery("INSERT INTO UpdateWithoutPrimary(t1,t2) values ('1-1','1-2')"); try (PreparedStatement preparedStatement = @@ -282,6 +283,7 @@ public void testMeta() throws Exception { assertEquals("1-1", rs.getString(2)); assertEquals("1-2", rs.getString(3)); assertFalse(rs.next()); + sharedConn.rollback(); } @Test @@ -607,78 +609,87 @@ public void updateBlobClob() throws SQLException, IOException { Statement stmt = sharedConn.createStatement(); stmt.execute("DROP TABLE IF EXISTS updateBlob"); stmt.execute("CREATE TABLE updateBlob(id int not null primary key, strm blob)"); - PreparedStatement prep = - sharedConn.prepareStatement("insert into updateBlob (id, strm) values (?,?)"); - byte[] theBlob = {1, 2, 3, 4, 5, 6}; - InputStream stream = new ByteArrayInputStream(theBlob); - - prep.setInt(1, 1); - prep.setBlob(2, stream); - prep.execute(); - - byte[] updatedBlob = "abcdef".getBytes(StandardCharsets.UTF_8); - - try (PreparedStatement preparedStatement = - sharedConn.prepareStatement( - "select * from updateBlob", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { - ResultSet rs = preparedStatement.executeQuery(); - assertTrue(rs.next()); - - rs.updateBlob(2, new ByteArrayInputStream(updatedBlob)); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateBlob("strm", new ByteArrayInputStream(updatedBlob)); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateBlob(2, new ByteArrayInputStream(updatedBlob), 20L); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateBlob("strm", new ByteArrayInputStream(updatedBlob), 20L); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateClob(2, new StringReader("abcdef")); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateClob("strm", new StringReader("abcdef")); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateClob(2, new StringReader("abcdef"), 20L); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateClob("strm", new StringReader("abcdef"), 20L); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateNClob(2, new StringReader("abcdef")); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateNClob("strm", new StringReader("abcdef")); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateNClob(2, new StringReader("abcdef"), 20L); - rs.updateRow(); - checkResult(rs, updatedBlob); - - rs.updateNClob("strm", new StringReader("abcdef"), 20L); - rs.updateRow(); - checkResult(rs, updatedBlob); - } + stmt.execute("START TRANSACTION "); + try { + PreparedStatement prep = + sharedConn.prepareStatement("insert into updateBlob (id, strm) values (?,?)"); + byte[] theBlob = {1, 2, 3, 4, 5, 6}; + InputStream stream = new ByteArrayInputStream(theBlob); + + prep.setInt(1, 1); + prep.setBlob(2, stream); + prep.execute(); + + byte[] updatedBlob = "abcdef".getBytes(StandardCharsets.UTF_8); + + try (PreparedStatement preparedStatement = + sharedConn.prepareStatement( + "select * from updateBlob", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_UPDATABLE)) { + ResultSet rs = preparedStatement.executeQuery(); + assertTrue(rs.next()); + + rs.updateBlob(2, new ByteArrayInputStream(updatedBlob)); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateBlob("strm", new ByteArrayInputStream(updatedBlob)); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateBlob(2, new ByteArrayInputStream(updatedBlob), 20L); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateBlob("strm", new ByteArrayInputStream(updatedBlob), 20L); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateClob(2, new StringReader("abcdef")); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateClob("strm", new StringReader("abcdef")); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateClob(2, new StringReader("abcdef"), 20L); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateClob("strm", new StringReader("abcdef"), 20L); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateNClob(2, new StringReader("abcdef")); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateNClob("strm", new StringReader("abcdef")); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateNClob(2, new StringReader("abcdef"), 20L); + rs.updateRow(); + checkResult(rs, updatedBlob); + + rs.updateNClob("strm", new StringReader("abcdef"), 20L); + rs.updateRow(); + checkResult(rs, updatedBlob); + } - try (PreparedStatement preparedStatement = - sharedConn.prepareStatement( - "select * from updateBlob", ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_UPDATABLE)) { - ResultSet rs = preparedStatement.executeQuery(); - assertTrue(rs.next()); - checkResult(rs, updatedBlob); + try (PreparedStatement preparedStatement = + sharedConn.prepareStatement( + "select * from updateBlob", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_UPDATABLE)) { + ResultSet rs = preparedStatement.executeQuery(); + assertTrue(rs.next()); + checkResult(rs, updatedBlob); + } + } finally { + sharedConn.rollback(); } } @@ -819,7 +830,7 @@ private void testMoveToInsertRow(Connection con) throws SQLException { Statement stmt = con.createStatement(); stmt.execute("DROP TABLE IF EXISTS testMoveToInsertRow"); stmt.execute("CREATE TABLE testMoveToInsertRow(t2 text, t1 text, id int primary key)"); - + stmt.execute("START TRANSACTION"); // if MAXSCALE ensure using WRITER try (PreparedStatement preparedStatement = con.prepareStatement( "select id, t1, t2 from testMoveToInsertRow", @@ -861,8 +872,12 @@ private void testMoveToInsertRow(Connection con) throws SQLException { assertEquals(3, rs.getInt("id")); assertEquals("other-t1-value", rs.getString("t1")); assertNull(rs.getString("t2")); + } finally { + con.commit(); } + stmt = sharedConn.createStatement(); + stmt.execute("START TRANSACTION"); // if MAXSCALE ensure using WRITER try (PreparedStatement preparedStatement = sharedConn.prepareStatement( "select id, t1, t2 from testMoveToInsertRow", @@ -902,6 +917,8 @@ private void testMoveToInsertRow(Connection con) throws SQLException { assertTrue(rs.next()); assertEquals(5, rs.getInt("id")); assertEquals("t1-5", rs.getString("t1")); + } finally { + sharedConn.rollback(); } }