From dd2d8df4c7cb4a1a8c8dc7d54f744e4bc52e6fbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Tue, 15 Aug 2023 11:02:57 +0200 Subject: [PATCH 1/6] test: add tests for all execute methods of Statement and PreparedStatement Adds tests for all the different variations of the execute method on Statement and PreparedStatement. This will allow us to make internal changes to the way these methods are handled, while keeping control over any behavior changes that it might introduce. --- .../spanner/jdbc/ExecuteMockServerTest.java | 618 ++++++++++++++++++ 1 file changed, 618 insertions(+) create mode 100644 src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java new file mode 100644 index 000000000..3c36b2686 --- /dev/null +++ b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java @@ -0,0 +1,618 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.cloud.spanner.jdbc; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +import com.google.cloud.spanner.MockSpannerServiceImpl.StatementResult; +import com.google.cloud.spanner.connection.AbstractMockServerTest; +import com.google.longrunning.Operation; +import com.google.protobuf.Any; +import com.google.protobuf.Empty; +import com.google.protobuf.ListValue; +import com.google.protobuf.Value; +import com.google.rpc.Code; +import com.google.spanner.admin.database.v1.UpdateDatabaseDdlMetadata; +import com.google.spanner.v1.ResultSetMetadata; +import com.google.spanner.v1.ResultSetStats; +import com.google.spanner.v1.StructType; +import com.google.spanner.v1.StructType.Field; +import com.google.spanner.v1.Type; +import com.google.spanner.v1.TypeCode; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import org.junit.Before; +import org.junit.Test; +import org.junit.function.ThrowingRunnable; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test class for verifying that the methods execute, executeQuery, and executeUpdate work as + * intended. + */ +@RunWith(JUnit4.class) +public class ExecuteMockServerTest extends AbstractMockServerTest { + private static final String QUERY = "select * from my_table"; + private static final String DML = "insert into my_table (id, value) values (1, 'One')"; + private static final String LARGE_DML = "update my_table set value='new value' where true"; + private static final String DML_RETURNING = + "insert into my_table (id, value) values (1, 'One') THEN RETURN *"; + private static final String DDL = "create table my_table"; + private static final long LARGE_UPDATE_COUNT = 2L * Integer.MAX_VALUE; + + @Before + public void setupResults() { + super.setupResults(); + com.google.spanner.v1.ResultSet resultSet = + com.google.spanner.v1.ResultSet.newBuilder() + .setMetadata( + ResultSetMetadata.newBuilder() + .setRowType( + StructType.newBuilder() + .addFields( + Field.newBuilder() + .setType(Type.newBuilder().setCode(TypeCode.INT64).build()) + .setName("id") + .build()) + .addFields( + Field.newBuilder() + .setType(Type.newBuilder().setCode(TypeCode.STRING).build()) + .setName("value") + .build()) + .build()) + .build()) + .addRows( + ListValue.newBuilder() + .addValues(Value.newBuilder().setStringValue("1").build()) + .addValues(Value.newBuilder().setStringValue("One").build()) + .build()) + .build(); + mockSpanner.putStatementResult( + StatementResult.query(com.google.cloud.spanner.Statement.of(QUERY), resultSet)); + mockSpanner.putStatementResult( + StatementResult.update(com.google.cloud.spanner.Statement.of(DML), 1L)); + mockSpanner.putStatementResult( + StatementResult.update( + com.google.cloud.spanner.Statement.of(LARGE_DML), LARGE_UPDATE_COUNT)); + mockSpanner.putStatementResult( + StatementResult.query( + com.google.cloud.spanner.Statement.of(DML_RETURNING), + resultSet + .toBuilder() + .setStats(ResultSetStats.newBuilder().setRowCountExact(1L).build()) + .build())); + mockDatabaseAdmin.addResponse( + Operation.newBuilder() + .setDone(true) + .setResponse(Any.pack(Empty.getDefaultInstance())) + .setMetadata(Any.pack(UpdateDatabaseDdlMetadata.getDefaultInstance())) + .build()); + } + + private String createUrl() { + return String.format( + "jdbc:cloudspanner://localhost:%d/projects/%s/instances/%s/databases/%s?usePlainText=true", + getPort(), "proj", "inst", "db"); + } + + private Connection createConnection() throws SQLException { + return DriverManager.getConnection(createUrl()); + } + + @Test + public void testStatementExecuteQuery() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + try (ResultSet resultSet = statement.executeQuery(QUERY)) { + verifyResultSet(resultSet); + } + try (ResultSet resultSet = statement.executeQuery(DML_RETURNING)) { + verifyResultSet(resultSet); + } + verifyException(() -> statement.executeQuery(DML)); + verifyException(() -> statement.executeQuery(LARGE_DML)); + verifyException(() -> statement.executeQuery(DDL)); + } + } + + @Test + public void testStatementExecuteUpdate() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + assertEquals(1, statement.executeUpdate(DML)); + assertEquals(0, statement.executeUpdate(DDL)); + verifyOverflow(() -> statement.executeUpdate(LARGE_DML)); + verifyException(() -> statement.executeUpdate(QUERY)); + verifyException(() -> statement.executeUpdate(DML_RETURNING)); + } + } + + @Test + public void testStatementExecuteUpdateReturnGeneratedKeys() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + // TODO: Add tests for RETURN_GENERATED_KEYS when that is supported. + assertEquals(1, statement.executeUpdate(DML, Statement.NO_GENERATED_KEYS)); + assertEquals(0, statement.executeUpdate(DDL, Statement.NO_GENERATED_KEYS)); + verifyOverflow(() -> statement.executeUpdate(LARGE_DML, Statement.NO_GENERATED_KEYS)); + verifyException(() -> statement.executeUpdate(QUERY, Statement.NO_GENERATED_KEYS)); + verifyException(() -> statement.executeUpdate(DML_RETURNING, Statement.NO_GENERATED_KEYS)); + } + } + + @Test + public void testStatementExecuteUpdateReturnColumnNames() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + assertEquals(1, statement.executeUpdate(DML, new String[] {"id"})); + assertEquals(0, statement.executeUpdate(DDL, new String[] {"id"})); + verifyOverflow(() -> statement.executeUpdate(LARGE_DML, new String[] {"id"})); + verifyException(() -> statement.executeUpdate(QUERY, new String[] {"id"})); + verifyException(() -> statement.executeUpdate(DML_RETURNING, new String[] {"id"})); + } + } + + @Test + public void testStatementExecuteUpdateReturnColumnIndexes() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + assertEquals(1, statement.executeUpdate(DML, new int[] {1})); + assertEquals(0, statement.executeUpdate(DDL, new int[] {1})); + verifyOverflow(() -> statement.executeUpdate(LARGE_DML, new int[] {1})); + verifyException(() -> statement.executeUpdate(QUERY, new int[] {1})); + verifyException(() -> statement.executeUpdate(DML_RETURNING, new int[] {1})); + } + } + + @Test + public void testStatementLargeExecuteUpdate() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + assertEquals(1L, statement.executeLargeUpdate(DML)); + assertEquals(0L, statement.executeLargeUpdate(DDL)); + assertEquals(LARGE_UPDATE_COUNT, statement.executeLargeUpdate(LARGE_DML)); + verifyException(() -> statement.executeLargeUpdate(QUERY)); + verifyException(() -> statement.executeLargeUpdate(DML_RETURNING)); + } + } + + @Test + public void testStatementExecuteLargeUpdateReturnGeneratedKeys() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + // TODO: Add tests for RETURN_GENERATED_KEYS when that is supported. + assertEquals(1, statement.executeLargeUpdate(DML, Statement.NO_GENERATED_KEYS)); + assertEquals(0, statement.executeLargeUpdate(DDL, Statement.NO_GENERATED_KEYS)); + assertEquals( + LARGE_UPDATE_COUNT, statement.executeLargeUpdate(LARGE_DML, Statement.NO_GENERATED_KEYS)); + verifyException(() -> statement.executeLargeUpdate(QUERY, Statement.NO_GENERATED_KEYS)); + verifyException( + () -> statement.executeLargeUpdate(DML_RETURNING, Statement.NO_GENERATED_KEYS)); + } + } + + @Test + public void testStatementExecuteLargeUpdateReturnColumnNames() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + assertEquals(1, statement.executeLargeUpdate(DML, new String[] {"id"})); + assertEquals(0, statement.executeLargeUpdate(DDL, new String[] {"id"})); + assertEquals( + LARGE_UPDATE_COUNT, statement.executeLargeUpdate(LARGE_DML, new String[] {"id"})); + verifyException(() -> statement.executeLargeUpdate(QUERY, new String[] {"id"})); + verifyException(() -> statement.executeLargeUpdate(DML_RETURNING, new String[] {"id"})); + } + } + + @Test + public void testStatementExecuteLargeUpdateReturnColumnIndexes() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + assertEquals(1, statement.executeLargeUpdate(DML, new int[] {1})); + assertEquals(0, statement.executeLargeUpdate(DDL, new int[] {1})); + assertEquals(LARGE_UPDATE_COUNT, statement.executeLargeUpdate(LARGE_DML, new int[] {1})); + verifyException(() -> statement.executeLargeUpdate(QUERY, new int[] {1})); + verifyException(() -> statement.executeLargeUpdate(DML_RETURNING, new int[] {1})); + } + } + + @Test + public void testStatementExecute() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + verifyUpdateCount(statement, () -> statement.execute(DML), 1L); + verifyUpdateCount(statement, () -> statement.execute(LARGE_DML), LARGE_UPDATE_COUNT); + verifyUpdateCount(statement, () -> statement.execute(DDL), Statement.SUCCESS_NO_INFO); + verifyResultSet(statement, () -> statement.execute(QUERY)); + verifyResultSet(statement, () -> statement.execute(DML_RETURNING)); + } + } + + @Test + public void testStatementExecuteReturnGeneratedKeys() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + // TODO: Add tests for RETURN_GENERATED_KEYS when that is supported. + verifyUpdateCount(statement, () -> statement.execute(DML, Statement.NO_GENERATED_KEYS), 1L); + verifyUpdateCount( + statement, + () -> statement.execute(LARGE_DML, Statement.NO_GENERATED_KEYS), + LARGE_UPDATE_COUNT); + verifyUpdateCount( + statement, + () -> statement.execute(DDL, Statement.NO_GENERATED_KEYS), + Statement.SUCCESS_NO_INFO); + verifyResultSet(statement, () -> statement.execute(QUERY, Statement.NO_GENERATED_KEYS)); + verifyResultSet( + statement, () -> statement.execute(DML_RETURNING, Statement.NO_GENERATED_KEYS)); + } + } + + @Test + public void testStatementExecuteReturnColumnNames() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + verifyUpdateCount(statement, () -> statement.execute(DML, new String[] {"id"}), 1L); + verifyUpdateCount( + statement, () -> statement.execute(LARGE_DML, new String[] {"id"}), LARGE_UPDATE_COUNT); + verifyUpdateCount( + statement, () -> statement.execute(DDL, new String[] {"id"}), Statement.SUCCESS_NO_INFO); + verifyResultSet(statement, () -> statement.execute(QUERY, new String[] {"id"})); + verifyResultSet(statement, () -> statement.execute(DML_RETURNING, new String[] {"id"})); + } + } + + @Test + public void testStatementExecuteReturnColumnIndexes() throws SQLException { + try (Connection connection = createConnection(); + Statement statement = connection.createStatement()) { + verifyUpdateCount(statement, () -> statement.execute(DML, new int[] {1}), 1L); + verifyUpdateCount( + statement, () -> statement.execute(LARGE_DML, new int[] {1}), LARGE_UPDATE_COUNT); + verifyUpdateCount( + statement, () -> statement.execute(DDL, new int[] {1}), Statement.SUCCESS_NO_INFO); + verifyResultSet(statement, () -> statement.execute(QUERY, new int[] {1})); + verifyResultSet(statement, () -> statement.execute(DML_RETURNING, new int[] {1})); + } + } + + @Test + public void testPreparedStatementExecuteQuery() throws SQLException { + try (Connection connection = createConnection()) { + try (ResultSet resultSet = connection.prepareStatement(QUERY).executeQuery()) { + verifyResultSet(resultSet); + } + try (ResultSet resultSet = connection.prepareStatement(DML_RETURNING).executeQuery()) { + verifyResultSet(resultSet); + } + verifyException(() -> connection.prepareStatement(DML).executeQuery()); + verifyException(() -> connection.prepareStatement(LARGE_DML).executeQuery()); + verifyException(() -> connection.prepareStatement(DDL).executeQuery()); + } + } + + @Test + public void testPreparedStatementExecuteUpdate() throws SQLException { + try (Connection connection = createConnection()) { + assertEquals(1, connection.prepareStatement(DML).executeUpdate()); + // TODO: Enable the next statement once PreparedStatement supports executing DDL through the + // executeUpdate() method. + // assertEquals(0, connection.prepareStatement(DDL).executeUpdate()); + verifyOverflow(() -> connection.prepareStatement(LARGE_DML).executeUpdate()); + verifyException(() -> connection.prepareStatement(QUERY).executeUpdate()); + verifyException( + () -> connection.prepareStatement(DML_RETURNING).executeUpdate(), + Code.FAILED_PRECONDITION); + } + } + + @Test + public void testPreparedStatementExecuteUpdateReturnGeneratedKeys() throws SQLException { + try (Connection connection = createConnection()) { + // TODO: Add tests for RETURN_GENERATED_KEYS when that is supported. + assertEquals( + 1, connection.prepareStatement(DML, Statement.NO_GENERATED_KEYS).executeUpdate()); + // TODO: Enable the next statement once PreparedStatement supports executing DDL through the + // executeUpdate() method. + // assertEquals(0, connection.prepareStatement(DDL, + // Statement.NO_GENERATED_KEYS).executeUpdate()); + verifyOverflow( + () -> + connection.prepareStatement(LARGE_DML, Statement.NO_GENERATED_KEYS).executeUpdate()); + verifyException( + () -> connection.prepareStatement(QUERY, Statement.NO_GENERATED_KEYS).executeUpdate()); + verifyException( + () -> + connection + .prepareStatement(DML_RETURNING, Statement.NO_GENERATED_KEYS) + .executeUpdate(), + Code.FAILED_PRECONDITION); + } + } + + @Test + public void testPreparedStatementExecuteUpdateReturnColumnNames() throws SQLException { + try (Connection connection = createConnection()) { + assertEquals(1, connection.prepareStatement(DML, new String[] {"id"}).executeUpdate()); + // TODO: Enable the next statement once PreparedStatement supports executing DDL through the + // executeUpdate() method. + // assertEquals(0, connection.prepareStatement(DDL, new String[] {"id"}).executeUpdate()); + verifyOverflow( + () -> connection.prepareStatement(LARGE_DML, new String[] {"id"}).executeUpdate()); + verifyException( + () -> connection.prepareStatement(QUERY, new String[] {"id"}).executeUpdate()); + verifyException( + () -> connection.prepareStatement(DML_RETURNING, new String[] {"id"}).executeUpdate(), + Code.FAILED_PRECONDITION); + } + } + + @Test + public void testPreparedStatementExecuteUpdateReturnColumnIndexes() throws SQLException { + try (Connection connection = createConnection()) { + assertEquals(1, connection.prepareStatement(DML, new int[] {1}).executeUpdate()); + // TODO: Enable the next statement once PreparedStatement supports executing DDL through the + // executeUpdate() method. + // assertEquals(0, connection.prepareStatement(DDL, new int[] {1}).executeUpdate()); + verifyOverflow(() -> connection.prepareStatement(LARGE_DML, new int[] {1}).executeUpdate()); + verifyException(() -> connection.prepareStatement(QUERY, new int[] {1}).executeUpdate()); + verifyException( + () -> connection.prepareStatement(DML_RETURNING, new int[] {1}).executeUpdate(), + Code.FAILED_PRECONDITION); + } + } + + @Test + public void testPreparedStatementLargeExecuteUpdate() throws SQLException { + try (Connection connection = createConnection()) { + assertEquals(1L, connection.prepareStatement(DML).executeLargeUpdate()); + // TODO: Enable the next statement once PreparedStatement supports executing DDL through the + // executeUpdate() method. + // assertEquals(0L, connection.prepareStatement(DDL).executeLargeUpdate()); + assertEquals(LARGE_UPDATE_COUNT, connection.prepareStatement(LARGE_DML).executeLargeUpdate()); + verifyException(() -> connection.prepareStatement(QUERY).executeLargeUpdate()); + verifyException( + () -> connection.prepareStatement(DML_RETURNING).executeLargeUpdate(), + Code.FAILED_PRECONDITION); + } + } + + @Test + public void testPreparedStatementExecuteLargeUpdateReturnGeneratedKeys() throws SQLException { + try (Connection connection = createConnection()) { + // TODO: Add tests for RETURN_GENERATED_KEYS when that is supported. + assertEquals( + 1, connection.prepareStatement(DML, Statement.NO_GENERATED_KEYS).executeLargeUpdate()); + // TODO: Enable the next statement once PreparedStatement supports executing DDL through the + // executeUpdate() method. + // assertEquals(0, connection.prepareStatement(DDL, + // Statement.NO_GENERATED_KEYS).executeLargeUpdate()); + assertEquals( + LARGE_UPDATE_COUNT, + connection.prepareStatement(LARGE_DML, Statement.NO_GENERATED_KEYS).executeLargeUpdate()); + verifyException( + () -> + connection.prepareStatement(QUERY, Statement.NO_GENERATED_KEYS).executeLargeUpdate()); + verifyException( + () -> + connection + .prepareStatement(DML_RETURNING, Statement.NO_GENERATED_KEYS) + .executeLargeUpdate(), + Code.FAILED_PRECONDITION); + } + } + + @Test + public void testPreparedStatementExecuteLargeUpdateReturnColumnNames() throws SQLException { + try (Connection connection = createConnection()) { + assertEquals(1, connection.prepareStatement(DML, new String[] {"id"}).executeLargeUpdate()); + // TODO: Enable the next statement once PreparedStatement supports executing DDL through the + // executeUpdate() method. + // assertEquals(0, connection.prepareStatement(DDL, new String[] + // {"id"}).executeLargeUpdate()); + assertEquals( + LARGE_UPDATE_COUNT, + connection.prepareStatement(LARGE_DML, new String[] {"id"}).executeLargeUpdate()); + verifyException( + () -> connection.prepareStatement(QUERY, new String[] {"id"}).executeLargeUpdate()); + verifyException( + () -> + connection.prepareStatement(DML_RETURNING, new String[] {"id"}).executeLargeUpdate(), + Code.FAILED_PRECONDITION); + } + } + + @Test + public void testPreparedStatementExecuteLargeUpdateReturnColumnIndexes() throws SQLException { + try (Connection connection = createConnection()) { + assertEquals(1, connection.prepareStatement(DML, new int[] {1}).executeLargeUpdate()); + // TODO: Enable the next statement once PreparedStatement supports executing DDL through the + // executeUpdate() method. + // assertEquals(0, connection.prepareStatement(DDL, new int[] {1}).executeLargeUpdate()); + assertEquals( + LARGE_UPDATE_COUNT, + connection.prepareStatement(LARGE_DML, new int[] {1}).executeLargeUpdate()); + verifyException(() -> connection.prepareStatement(QUERY, new int[] {1}).executeLargeUpdate()); + verifyException( + () -> connection.prepareStatement(DML_RETURNING, new int[] {1}).executeLargeUpdate(), + Code.FAILED_PRECONDITION); + } + } + + @Test + public void testPreparedStatementExecute() throws SQLException { + try (Connection connection = createConnection()) { + verifyPreparedUpdateCount(connection.prepareStatement(DML), PreparedStatement::execute, 1L); + verifyPreparedUpdateCount( + connection.prepareStatement(LARGE_DML), PreparedStatement::execute, LARGE_UPDATE_COUNT); + verifyPreparedUpdateCount( + connection.prepareStatement(DDL), PreparedStatement::execute, Statement.SUCCESS_NO_INFO); + verifyPreparedResultSet(connection.prepareStatement(QUERY), PreparedStatement::execute); + verifyPreparedResultSet( + connection.prepareStatement(DML_RETURNING), PreparedStatement::execute); + } + } + + @Test + public void testPreparedStatementExecuteReturnGeneratedKeys() throws SQLException { + try (Connection connection = createConnection()) { + // TODO: Add tests for RETURN_GENERATED_KEYS when that is supported. + verifyPreparedUpdateCount( + connection.prepareStatement(DML, Statement.NO_GENERATED_KEYS), + PreparedStatement::execute, + 1L); + verifyPreparedUpdateCount( + connection.prepareStatement(LARGE_DML, Statement.NO_GENERATED_KEYS), + PreparedStatement::execute, + LARGE_UPDATE_COUNT); + verifyPreparedUpdateCount( + connection.prepareStatement(DDL, Statement.NO_GENERATED_KEYS), + PreparedStatement::execute, + Statement.SUCCESS_NO_INFO); + verifyPreparedResultSet( + connection.prepareStatement(QUERY, Statement.NO_GENERATED_KEYS), + PreparedStatement::execute); + verifyPreparedResultSet( + connection.prepareStatement(DML_RETURNING, Statement.NO_GENERATED_KEYS), + PreparedStatement::execute); + } + } + + @Test + public void testPreparedStatementExecuteReturnColumnNames() throws SQLException { + try (Connection connection = createConnection()) { + verifyPreparedUpdateCount( + connection.prepareStatement(DML, new String[] {"id"}), PreparedStatement::execute, 1L); + verifyPreparedUpdateCount( + connection.prepareStatement(LARGE_DML, new String[] {"id"}), + PreparedStatement::execute, + LARGE_UPDATE_COUNT); + verifyPreparedUpdateCount( + connection.prepareStatement(DDL, new String[] {"id"}), + PreparedStatement::execute, + Statement.SUCCESS_NO_INFO); + verifyPreparedResultSet( + connection.prepareStatement(QUERY, new String[] {"id"}), PreparedStatement::execute); + verifyPreparedResultSet( + connection.prepareStatement(DML_RETURNING, new String[] {"id"}), + PreparedStatement::execute); + } + } + + @Test + public void testPreparedStatementExecuteReturnColumnIndexes() throws SQLException { + try (Connection connection = createConnection()) { + verifyPreparedUpdateCount( + connection.prepareStatement(DML, new int[] {1}), PreparedStatement::execute, 1L); + verifyPreparedUpdateCount( + connection.prepareStatement(LARGE_DML, new int[] {1}), + PreparedStatement::execute, + LARGE_UPDATE_COUNT); + verifyPreparedUpdateCount( + connection.prepareStatement(DDL, new int[] {1}), + PreparedStatement::execute, + Statement.SUCCESS_NO_INFO); + verifyPreparedResultSet( + connection.prepareStatement(QUERY, new int[] {1}), PreparedStatement::execute); + verifyPreparedResultSet( + connection.prepareStatement(DML_RETURNING, new int[] {1}), PreparedStatement::execute); + } + } + + private void verifyResultSet(ResultSet resultSet) throws SQLException { + assertNotNull(resultSet.getMetaData()); + assertEquals(2, resultSet.getMetaData().getColumnCount()); + assertTrue(resultSet.next()); + assertFalse(resultSet.next()); + } + + private void verifyException(ThrowingRunnable runnable) { + verifyException(runnable, Code.INVALID_ARGUMENT); + } + + private void verifyOverflow(ThrowingRunnable runnable) { + verifyException(runnable, Code.OUT_OF_RANGE); + } + + private void verifyException(ThrowingRunnable runnable, Code code) { + SQLException exception = assertThrows(SQLException.class, runnable); + assertTrue(exception instanceof JdbcSqlException); + JdbcSqlException sqlException = (JdbcSqlException) exception; + assertEquals(code, sqlException.getCode()); + } + + interface ThrowingFunction { + T apply() throws SQLException; + } + + interface ThrowingPreparedFunction { + T apply(PreparedStatement statement) throws SQLException; + } + + private void verifyPreparedUpdateCount( + PreparedStatement statement, ThrowingPreparedFunction function, long updateCount) + throws SQLException { + verifyUpdateCount(statement, () -> function.apply(statement), updateCount); + } + + private void verifyUpdateCount( + Statement statement, ThrowingFunction function, long updateCount) + throws SQLException { + assertFalse(function.apply()); + if (updateCount > Integer.MAX_VALUE) { + verifyOverflow(statement::getUpdateCount); + } else { + assertEquals((int) updateCount, statement.getUpdateCount()); + } + assertEquals(updateCount, statement.getLargeUpdateCount()); + assertNull(statement.getResultSet()); + assertFalse(statement.getMoreResults()); + assertEquals(-1, statement.getUpdateCount()); + assertEquals(-1L, statement.getLargeUpdateCount()); + } + + private void verifyPreparedResultSet( + PreparedStatement statement, ThrowingPreparedFunction function) throws SQLException { + verifyResultSet(statement, () -> function.apply(statement)); + } + + private void verifyResultSet(Statement statement, ThrowingFunction function) + throws SQLException { + assertTrue(function.apply()); + assertEquals(-1, statement.getUpdateCount()); + assertEquals(-1L, statement.getLargeUpdateCount()); + assertNotNull(statement.getResultSet()); + try (ResultSet resultSet = statement.getResultSet()) { + assertTrue(resultSet.next()); + assertFalse(resultSet.next()); + } + + assertFalse(statement.getMoreResults()); + assertEquals(-1, statement.getUpdateCount()); + assertEquals(-1L, statement.getLargeUpdateCount()); + } +} From b66da5eb146a419edc2b1b10d5e7453498a708b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Fri, 25 Aug 2023 14:04:43 +0200 Subject: [PATCH 2/6] chore: share do-with-timeout code (#1324) Refactor the execute methods a bit so they all share the same do-with-timeout logic. --- .../spanner/jdbc/AbstractJdbcStatement.java | 106 ++++++++++-------- .../cloud/spanner/jdbc/JdbcStatement.java | 6 +- .../spanner/jdbc/ExecuteMockServerTest.java | 16 +-- 3 files changed, 69 insertions(+), 59 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java index 2ee68afb2..99bee40fb 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/AbstractJdbcStatement.java @@ -31,6 +31,8 @@ import java.sql.Statement; import java.util.Arrays; import java.util.concurrent.TimeUnit; +import java.util.function.Function; +import java.util.function.Supplier; /** Base class for Cloud Spanner JDBC {@link Statement}s */ abstract class AbstractJdbcStatement extends AbstractJdbcWrapper implements Statement { @@ -184,20 +186,36 @@ private ResultSet executeQuery( QueryAnalyzeMode analyzeMode, QueryOption... options) throws SQLException { + Options.QueryOption[] queryOptions = getQueryOptions(options); + return doWithStatementTimeout( + () -> { + com.google.cloud.spanner.ResultSet resultSet; + if (analyzeMode == null) { + resultSet = connection.getSpannerConnection().executeQuery(statement, queryOptions); + } else { + resultSet = connection.getSpannerConnection().analyzeQuery(statement, analyzeMode); + } + return JdbcResultSet.of(this, resultSet); + }); + } + + private T doWithStatementTimeout(Supplier runnable) throws SQLException { + return doWithStatementTimeout(runnable, ignore -> Boolean.TRUE); + } + + private T doWithStatementTimeout( + Supplier runnable, Function shouldResetTimeout) throws SQLException { StatementTimeout originalTimeout = setTemporaryStatementTimeout(); + T result = null; try { - com.google.cloud.spanner.ResultSet resultSet; - if (analyzeMode == null) { - resultSet = - connection.getSpannerConnection().executeQuery(statement, getQueryOptions(options)); - } else { - resultSet = connection.getSpannerConnection().analyzeQuery(statement, analyzeMode); - } - return JdbcResultSet.of(this, resultSet); - } catch (SpannerException e) { - throw JdbcSqlExceptionFactory.of(e); + result = runnable.get(); + return result; + } catch (SpannerException spannerException) { + throw JdbcSqlExceptionFactory.of(spannerException); } finally { - resetStatementTimeout(originalTimeout); + if (shouldResetTimeout.apply(result)) { + resetStatementTimeout(originalTimeout); + } } } @@ -211,12 +229,19 @@ private ResultSet executeQuery( * than {@link Integer#MAX_VALUE} */ int executeUpdate(com.google.cloud.spanner.Statement statement) throws SQLException { - long count = executeLargeUpdate(statement); - if (count > Integer.MAX_VALUE) { + return checkedCast(executeLargeUpdate(statement)); + } + + /** + * Do a checked cast from long to int. Throws a {@link SQLException} with code {@link + * Code#OUT_OF_RANGE} if the update count is too big to fit in an int. + */ + int checkedCast(long updateCount) throws SQLException { + if (updateCount > Integer.MAX_VALUE) { throw JdbcSqlExceptionFactory.of( - "update count too large for executeUpdate: " + count, Code.OUT_OF_RANGE); + "update count too large for executeUpdate: " + updateCount, Code.OUT_OF_RANGE); } - return (int) count; + return (int) updateCount; } /** @@ -228,14 +253,7 @@ int executeUpdate(com.google.cloud.spanner.Statement statement) throws SQLExcept * @throws SQLException if a database error occurs */ long executeLargeUpdate(com.google.cloud.spanner.Statement statement) throws SQLException { - StatementTimeout originalTimeout = setTemporaryStatementTimeout(); - try { - return connection.getSpannerConnection().executeUpdate(statement); - } catch (SpannerException e) { - throw JdbcSqlExceptionFactory.of(e); - } finally { - resetStatementTimeout(originalTimeout); - } + return doWithStatementTimeout(() -> connection.getSpannerConnection().executeUpdate(statement)); } /** @@ -248,24 +266,16 @@ long executeLargeUpdate(com.google.cloud.spanner.Statement statement) throws SQL * @throws SQLException if a database error occurs. */ StatementResult execute(com.google.cloud.spanner.Statement statement) throws SQLException { - StatementTimeout originalTimeout = setTemporaryStatementTimeout(); - boolean mustResetTimeout = false; - try { - StatementResult result = connection.getSpannerConnection().execute(statement); - mustResetTimeout = !resultIsSetStatementTimeout(result); - if (mustResetTimeout && resultIsShowStatementTimeout(result)) { - // it was a 'SHOW STATEMENT_TIMEOUT statement, we need to re-run to get the correct value - mustResetTimeout = false; - result = rerunShowStatementTimeout(statement, originalTimeout); - } - return result; - } catch (SpannerException e) { - throw JdbcSqlExceptionFactory.of(e); - } finally { - if (mustResetTimeout) { - resetStatementTimeout(originalTimeout); - } + StatementResult statementResult = + doWithStatementTimeout( + () -> connection.getSpannerConnection().execute(statement), + result -> !resultIsSetStatementTimeout(result)); + if (resultIsShowStatementTimeout(statementResult)) { + // We can safely re-run it without first resetting the timeout to the original value, as that + // has already been done by the 'doWithStatementTimeout' function. + return rerunShowStatementTimeout(statement); } + return statementResult; } /** @@ -288,18 +298,22 @@ StatementResult execute(com.google.cloud.spanner.Statement statement) throws SQL * executed was a SET STATEMENT_TIMEOUT statement. */ private boolean resultIsSetStatementTimeout(StatementResult result) { - return result.getClientSideStatementType() == ClientSideStatementType.SET_STATEMENT_TIMEOUT; + return result != null + && result.getClientSideStatementType() == ClientSideStatementType.SET_STATEMENT_TIMEOUT; } private boolean resultIsShowStatementTimeout(StatementResult result) { - return result.getClientSideStatementType() == ClientSideStatementType.SHOW_STATEMENT_TIMEOUT; + return result != null + && result.getClientSideStatementType() == ClientSideStatementType.SHOW_STATEMENT_TIMEOUT; } - private StatementResult rerunShowStatementTimeout( - com.google.cloud.spanner.Statement statement, StatementTimeout originalTimeout) + private StatementResult rerunShowStatementTimeout(com.google.cloud.spanner.Statement statement) throws SQLException { - resetStatementTimeout(originalTimeout); - return connection.getSpannerConnection().execute(statement); + try { + return connection.getSpannerConnection().execute(statement); + } catch (SpannerException spannerException) { + throw JdbcSqlExceptionFactory.of(spannerException); + } } @Override diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java index 3e7c86f9f..95744275d 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcStatement.java @@ -72,11 +72,7 @@ public ResultSet executeQuery(String sql) throws SQLException { */ @Override public int executeUpdate(String sql) throws SQLException { - long result = executeLargeUpdate(sql); - if (result > Integer.MAX_VALUE) { - throw JdbcSqlExceptionFactory.of("update count too large: " + result, Code.OUT_OF_RANGE); - } - return (int) result; + return checkedCast(executeLargeUpdate(sql)); } /** diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java index 3c36b2686..25175a521 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java @@ -320,7 +320,7 @@ public void testPreparedStatementExecuteUpdate() throws SQLException { try (Connection connection = createConnection()) { assertEquals(1, connection.prepareStatement(DML).executeUpdate()); // TODO: Enable the next statement once PreparedStatement supports executing DDL through the - // executeUpdate() method. + // executeUpdate() method. // assertEquals(0, connection.prepareStatement(DDL).executeUpdate()); verifyOverflow(() -> connection.prepareStatement(LARGE_DML).executeUpdate()); verifyException(() -> connection.prepareStatement(QUERY).executeUpdate()); @@ -337,7 +337,7 @@ public void testPreparedStatementExecuteUpdateReturnGeneratedKeys() throws SQLEx assertEquals( 1, connection.prepareStatement(DML, Statement.NO_GENERATED_KEYS).executeUpdate()); // TODO: Enable the next statement once PreparedStatement supports executing DDL through the - // executeUpdate() method. + // executeUpdate() method. // assertEquals(0, connection.prepareStatement(DDL, // Statement.NO_GENERATED_KEYS).executeUpdate()); verifyOverflow( @@ -359,7 +359,7 @@ public void testPreparedStatementExecuteUpdateReturnColumnNames() throws SQLExce try (Connection connection = createConnection()) { assertEquals(1, connection.prepareStatement(DML, new String[] {"id"}).executeUpdate()); // TODO: Enable the next statement once PreparedStatement supports executing DDL through the - // executeUpdate() method. + // executeUpdate() method. // assertEquals(0, connection.prepareStatement(DDL, new String[] {"id"}).executeUpdate()); verifyOverflow( () -> connection.prepareStatement(LARGE_DML, new String[] {"id"}).executeUpdate()); @@ -376,7 +376,7 @@ public void testPreparedStatementExecuteUpdateReturnColumnIndexes() throws SQLEx try (Connection connection = createConnection()) { assertEquals(1, connection.prepareStatement(DML, new int[] {1}).executeUpdate()); // TODO: Enable the next statement once PreparedStatement supports executing DDL through the - // executeUpdate() method. + // executeUpdate() method. // assertEquals(0, connection.prepareStatement(DDL, new int[] {1}).executeUpdate()); verifyOverflow(() -> connection.prepareStatement(LARGE_DML, new int[] {1}).executeUpdate()); verifyException(() -> connection.prepareStatement(QUERY, new int[] {1}).executeUpdate()); @@ -391,7 +391,7 @@ public void testPreparedStatementLargeExecuteUpdate() throws SQLException { try (Connection connection = createConnection()) { assertEquals(1L, connection.prepareStatement(DML).executeLargeUpdate()); // TODO: Enable the next statement once PreparedStatement supports executing DDL through the - // executeUpdate() method. + // executeUpdate() method. // assertEquals(0L, connection.prepareStatement(DDL).executeLargeUpdate()); assertEquals(LARGE_UPDATE_COUNT, connection.prepareStatement(LARGE_DML).executeLargeUpdate()); verifyException(() -> connection.prepareStatement(QUERY).executeLargeUpdate()); @@ -408,7 +408,7 @@ public void testPreparedStatementExecuteLargeUpdateReturnGeneratedKeys() throws assertEquals( 1, connection.prepareStatement(DML, Statement.NO_GENERATED_KEYS).executeLargeUpdate()); // TODO: Enable the next statement once PreparedStatement supports executing DDL through the - // executeUpdate() method. + // executeUpdate() method. // assertEquals(0, connection.prepareStatement(DDL, // Statement.NO_GENERATED_KEYS).executeLargeUpdate()); assertEquals( @@ -431,7 +431,7 @@ public void testPreparedStatementExecuteLargeUpdateReturnColumnNames() throws SQ try (Connection connection = createConnection()) { assertEquals(1, connection.prepareStatement(DML, new String[] {"id"}).executeLargeUpdate()); // TODO: Enable the next statement once PreparedStatement supports executing DDL through the - // executeUpdate() method. + // executeUpdate() method. // assertEquals(0, connection.prepareStatement(DDL, new String[] // {"id"}).executeLargeUpdate()); assertEquals( @@ -451,7 +451,7 @@ public void testPreparedStatementExecuteLargeUpdateReturnColumnIndexes() throws try (Connection connection = createConnection()) { assertEquals(1, connection.prepareStatement(DML, new int[] {1}).executeLargeUpdate()); // TODO: Enable the next statement once PreparedStatement supports executing DDL through the - // executeUpdate() method. + // executeUpdate() method. // assertEquals(0, connection.prepareStatement(DDL, new int[] {1}).executeLargeUpdate()); assertEquals( LARGE_UPDATE_COUNT, From 6f5d10f0bf25318ec2e3c9f0203ba713266203f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Fri, 25 Aug 2023 15:27:48 +0200 Subject: [PATCH 3/6] test: comments should be sent to Spanner --- .../cloud/spanner/jdbc/ExecuteMockServerTest.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java index a433947e9..00ebb825a 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java @@ -54,15 +54,19 @@ /** * Test class for verifying that the methods execute, executeQuery, and executeUpdate work as - * intended. + * intended, and that they always also include any comments in the statement, as these may contain + * hints. */ @RunWith(JUnit4.class) public class ExecuteMockServerTest extends AbstractMockServerTest { - private static final String QUERY = "select * from my_table"; - private static final String DML = "insert into my_table (id, value) values (1, 'One')"; - private static final String LARGE_DML = "update my_table set value='new value' where true"; + private static final String QUERY = + "/*@ lock_scanned_ranges = exclusive */ select * from my_table"; + private static final String DML = + "/*@ lock_scanned_ranges = exclusive */ insert into my_table (id, value) values (1, 'One')"; + private static final String LARGE_DML = + "/*@ lock_scanned_ranges = exclusive */ update my_table set value='new value' where true"; private static final String DML_RETURNING = - "insert into my_table (id, value) values (1, 'One') THEN RETURN *"; + "/*@ lock_scanned_ranges = exclusive */ insert into my_table (id, value) values (1, 'One') THEN RETURN *"; private static final String DDL = "create table my_table"; private static final long LARGE_UPDATE_COUNT = 2L * Integer.MAX_VALUE; From 5e3592655e1014ee746acb2fdb065a9550a40698 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 14 Sep 2023 16:43:15 +0200 Subject: [PATCH 4/6] chore: format code --- .../spanner/jdbc/JdbcPreparedStatement.java | 11 +++-- .../spanner/jdbc/ExecuteMockServerTest.java | 42 +++++++++---------- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java index ddfa5b87b..88d636975 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java @@ -46,10 +46,15 @@ class JdbcPreparedStatement extends AbstractJdbcPreparedStatement { super(connection); this.sql = sql; try { - // The PostgreSQL parser allows comments to be present in the SQL string that is used to parse the - String sqlForParameterExtraction = getConnection().getDialect() == Dialect.POSTGRESQL ? this.sql : parser.removeCommentsAndTrim(this.sql); + // The PostgreSQL parser allows comments to be present in the SQL string that is used to parse + // the + String sqlForParameterExtraction = + getConnection().getDialect() == Dialect.POSTGRESQL + ? this.sql + : parser.removeCommentsAndTrim(this.sql); this.parameters = - parser.convertPositionalParametersToNamedParameters(POS_PARAM_CHAR, sqlForParameterExtraction); + parser.convertPositionalParametersToNamedParameters( + POS_PARAM_CHAR, sqlForParameterExtraction); } catch (SpannerException e) { throw JdbcSqlExceptionFactory.of(e); } diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java index 9156a547e..3c3827a80 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java @@ -52,7 +52,6 @@ import org.junit.Test; import org.junit.function.ThrowingRunnable; import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; @@ -70,9 +69,8 @@ public static Object[] parameters() { return Dialect.values(); } - @Parameter - public Dialect dialect; - + @Parameter public Dialect dialect; + private static final String DDL = "create table my_table"; private static final long LARGE_UPDATE_COUNT = 2L * Integer.MAX_VALUE; @@ -85,31 +83,33 @@ public static Object[] parameters() { @Before public void setupResults() { - QUERY = dialect == Dialect.POSTGRESQL - ? "/*@ lock_scanned_ranges = exclusive */ select * from my_table" - : "select * from my_table"; - DML = dialect == Dialect.POSTGRESQL - ? "/*@ lock_scanned_ranges = exclusive */ insert into my_table (id, value) values (1, 'One')" - : "insert into my_table (id, value) values (1, 'One')"; - DML_THEN_RETURN_ID = DML + (dialect == Dialect.POSTGRESQL - ? "\nRETURNING \"id\"" - : "\nTHEN RETURN `id`"); - LARGE_DML = dialect == Dialect.POSTGRESQL - ? "/*@ lock_scanned_ranges = exclusive */ update my_table set value='new value' where true" - : "update my_table set value='new value' where true"; - LARGE_DML_THEN_RETURN_ID = LARGE_DML + (dialect == Dialect.POSTGRESQL - ? "\nRETURNING \"id\"" - : "\nTHEN RETURN `id`"); + QUERY = + dialect == Dialect.POSTGRESQL + ? "/*@ lock_scanned_ranges = exclusive */ select * from my_table" + : "select * from my_table"; + DML = + dialect == Dialect.POSTGRESQL + ? "/*@ lock_scanned_ranges = exclusive */ insert into my_table (id, value) values (1, 'One')" + : "insert into my_table (id, value) values (1, 'One')"; + DML_THEN_RETURN_ID = + DML + (dialect == Dialect.POSTGRESQL ? "\nRETURNING \"id\"" : "\nTHEN RETURN `id`"); + LARGE_DML = + dialect == Dialect.POSTGRESQL + ? "/*@ lock_scanned_ranges = exclusive */ update my_table set value='new value' where true" + : "update my_table set value='new value' where true"; + LARGE_DML_THEN_RETURN_ID = + LARGE_DML + (dialect == Dialect.POSTGRESQL ? "\nRETURNING \"id\"" : "\nTHEN RETURN `id`"); DML_RETURNING = dialect == Dialect.POSTGRESQL ? "/*@ lock_scanned_ranges = exclusive */ insert into my_table (id, value) values (1, 'One') RETURNING *" : "insert into my_table (id, value) values (1, 'One') THEN RETURN *"; - // This forces a refresh of the Spanner instance that is used for a connection, which again is needed in order to refresh the dialect of the database. + // This forces a refresh of the Spanner instance that is used for a connection, which again is + // needed in order to refresh the dialect of the database. SpannerPool.closeSpannerPool(); mockSpanner.putStatementResult(StatementResult.detectDialectResult(dialect)); super.setupResults(); - + com.google.spanner.v1.ResultSet resultSet = com.google.spanner.v1.ResultSet.newBuilder() .setMetadata( From deb472fe281f7d70668e70a2424bf8b9be5f63d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Knut=20Olav=20L=C3=B8ite?= Date: Thu, 14 Sep 2023 16:45:13 +0200 Subject: [PATCH 5/6] chore: finish sentence in comment --- .../com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java b/src/main/java/com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java index 88d636975..0b7939880 100644 --- a/src/main/java/com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java +++ b/src/main/java/com/google/cloud/spanner/jdbc/JdbcPreparedStatement.java @@ -47,7 +47,7 @@ class JdbcPreparedStatement extends AbstractJdbcPreparedStatement { this.sql = sql; try { // The PostgreSQL parser allows comments to be present in the SQL string that is used to parse - // the + // the query parameters. String sqlForParameterExtraction = getConnection().getDialect() == Dialect.POSTGRESQL ? this.sql From abbc11e0e5959c4e89454fc5d33087480f007c9e Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 15 Sep 2023 11:42:45 +0000 Subject: [PATCH 6/6] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- .../google/cloud/spanner/jdbc/ExecuteMockServerTest.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java index 4e0e7bcd0..65a54a89a 100644 --- a/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java +++ b/src/test/java/com/google/cloud/spanner/jdbc/ExecuteMockServerTest.java @@ -295,8 +295,7 @@ public void testStatementExecuteLargeUpdateReturnColumnNames() throws SQLExcepti Statement statement = connection.createStatement()) { assertEquals(1, statement.executeLargeUpdate(dml, new String[] {"id"})); assertEquals(0, statement.executeLargeUpdate(DDL, new String[] {"id"})); - assertEquals( - LARGE_UPDATE_COUNT, statement.executeLargeUpdate(largeDml, new String[] {"id"})); + assertEquals(LARGE_UPDATE_COUNT, statement.executeLargeUpdate(largeDml, new String[] {"id"})); verifyException( () -> statement.executeLargeUpdate(query, new String[] {"id"}), Code.FAILED_PRECONDITION); assertEquals(1L, statement.executeLargeUpdate(dmlReturning, new String[] {"id"})); @@ -415,8 +414,7 @@ public void testPreparedStatementExecuteUpdateReturnGeneratedKeys() throws SQLEx // assertEquals(0, connection.prepareStatement(DDL, // Statement.NO_GENERATED_KEYS).executeUpdate()); verifyOverflow( - () -> - connection.prepareStatement(largeDml, Statement.NO_GENERATED_KEYS).executeUpdate()); + () -> connection.prepareStatement(largeDml, Statement.NO_GENERATED_KEYS).executeUpdate()); verifyException( () -> connection.prepareStatement(query, Statement.NO_GENERATED_KEYS).executeUpdate()); verifyException(