@@ -2004,6 +2004,82 @@ public void testInformationSchemaQueryInTransactionWithErrorDuringRetry() throws
20042004 assertEquals (0 , mockSpanner .countRequestsOfType (CommitRequest .class ));
20052005 }
20062006
2007+ @ Test
2008+ public void testInformationSchemaQueryInTransactionWithReplacedPgCatalogTables ()
2009+ throws SQLException {
2010+ String sql = "SELECT 1 FROM pg_namespace" ;
2011+ String replacedSql =
2012+ "with pg_namespace as (\n "
2013+ + " select case schema_name when 'pg_catalog' then 11 when 'public' then 2200 else 0 end as oid,\n "
2014+ + " schema_name as nspname, null as nspowner, null as nspacl\n "
2015+ + " from information_schema.schemata\n "
2016+ + ")\n "
2017+ + "SELECT 1 FROM pg_namespace" ;
2018+ // Register a result for the query. Note that we don't really care what the result is, just that
2019+ // there is a result.
2020+ mockSpanner .putStatementResult (
2021+ StatementResult .query (Statement .of (replacedSql ), SELECT1_RESULTSET ));
2022+
2023+ try (Connection connection = DriverManager .getConnection (createUrl ())) {
2024+ // Make sure that we start a transaction.
2025+ connection .setAutoCommit (false );
2026+
2027+ // Execute a query to start the transaction.
2028+ try (ResultSet resultSet = connection .createStatement ().executeQuery (SELECT1 .getSql ())) {
2029+ assertTrue (resultSet .next ());
2030+ assertEquals (1L , resultSet .getLong (1 ));
2031+ assertFalse (resultSet .next ());
2032+ }
2033+
2034+ // This ensures that the following query returns an error the first time it is executed, and
2035+ // then succeeds the second time. This happens because the exception is 'popped' from the
2036+ // response queue when it is returned. The next time the query is executed, it will return the
2037+ // actual result that we set.
2038+ mockSpanner .setExecuteStreamingSqlExecutionTime (
2039+ SimulatedExecutionTime .ofException (
2040+ Status .INVALID_ARGUMENT
2041+ .withDescription (
2042+ "Unsupported concurrency mode in query using INFORMATION_SCHEMA." )
2043+ .asRuntimeException ()));
2044+ try (ResultSet resultSet = connection .createStatement ().executeQuery (sql )) {
2045+ assertTrue (resultSet .next ());
2046+ assertEquals (1L , resultSet .getLong (1 ));
2047+ assertFalse (resultSet .next ());
2048+ }
2049+
2050+ // Make sure that the connection is still usable.
2051+ try (ResultSet resultSet = connection .createStatement ().executeQuery (SELECT2 .getSql ())) {
2052+ assertTrue (resultSet .next ());
2053+ assertEquals (2L , resultSet .getLong (1 ));
2054+ assertFalse (resultSet .next ());
2055+ }
2056+ connection .commit ();
2057+ }
2058+
2059+ // We should receive the INFORMATION_SCHEMA statement twice on Cloud Spanner:
2060+ // 1. The first time it returns an error because it is using the wrong concurrency mode.
2061+ // 2. The specific error will cause the connection to retry the statement using a single-use
2062+ // read-only transaction.
2063+ assertEquals (4 , mockSpanner .countRequestsOfType (ExecuteSqlRequest .class ));
2064+ List <ExecuteSqlRequest > requests = mockSpanner .getRequestsOfType (ExecuteSqlRequest .class );
2065+ // The first statement should start a transaction
2066+ assertTrue (requests .get (0 ).getTransaction ().hasBegin ());
2067+ // The second statement (the initial attempt of the INFORMATION_SCHEMA query) should try to use
2068+ // the transaction.
2069+ assertTrue (requests .get (1 ).getTransaction ().hasId ());
2070+ assertEquals (replacedSql , requests .get (1 ).getSql ());
2071+ // The INFORMATION_SCHEMA query is then retried using a single-use read-only transaction.
2072+ assertFalse (requests .get (2 ).hasTransaction ());
2073+ assertEquals (replacedSql , requests .get (2 ).getSql ());
2074+ // The last statement should use the transaction.
2075+ assertTrue (requests .get (3 ).getTransaction ().hasId ());
2076+
2077+ assertEquals (1 , mockSpanner .countRequestsOfType (CommitRequest .class ));
2078+ CommitRequest commitRequest = mockSpanner .getRequestsOfType (CommitRequest .class ).get (0 );
2079+ assertEquals (commitRequest .getTransactionId (), requests .get (1 ).getTransaction ().getId ());
2080+ assertEquals (commitRequest .getTransactionId (), requests .get (3 ).getTransaction ().getId ());
2081+ }
2082+
20072083 @ Test
20082084 public void testShowGuessTypes () throws SQLException {
20092085 try (Connection connection = DriverManager .getConnection (createUrl ())) {
0 commit comments