diff --git a/README.md b/README.md index fb26618..b1407a4 100644 --- a/README.md +++ b/README.md @@ -25,12 +25,12 @@ Project Reactor, RxJava, and Akka Streams. [Reactive Streams Specification v1.0.3](https://github.com/reactive-streams/reactive-streams-jvm/blob/v1.0.3/README.md) # About This Version -The 1.2.0 release Oracle R2DBC implements version 1.0.0.RELEASE of the R2DBC SPI. +The 1.3.0 release Oracle R2DBC implements version 1.0.0.RELEASE of the R2DBC SPI. New features in this release: -- [Pipelined Operations](https://github.com/oracle/oracle-r2dbc/pull/145) -- [Vector Data Type](https://github.com/oracle/oracle-r2dbc/pull/146) -- [Options for Fetch Size and Proxy Authentication](https://github.com/oracle/oracle-r2dbc/pull/155) +- [Pipelining](#pipelining) +- [Vector Data Type](#vector) +- [Fetch Size and Proxy Authentication Options](https://github.com/oracle/oracle-r2dbc/pull/155) Updated dependencies: - Updated Oracle JDBC from 21.11.0.0 to 23.6.0.24.10 @@ -369,23 +369,28 @@ If this option is not configured, then the common A subset of Oracle JDBC's connection properties are defined as `Option` constants in the [OracleR2dbcOptions](src/main/java/oracle/r2dbc/OracleR2dbcOptions.java) class. -These connection properties may be configured as options having the same -name as the Oracle JDBC connection property, and may have `CharSequence` value -types. - -For example, the following URL configures the `oracle.net.wallet_location` -connection property: +If an Oracle JDBC property is not defined as an `Option`, in most cases it can +instead be configured by a +[connection properties file](https://docs.oracle.com/en/database/oracle/oracle-database/23/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_CONFIG_FILE) +or a JVM system property instead. +[Pull requests to add missing options](https://github.com/oracle/oracle-r2dbc/pull/124) +are also a welcome addition. + +When a connection property is defined in `OracleR2dbcOptions`, it may be +configured as an R2DBC URL parameter. For example, the following URL configures +the `oracle.net.wallet_location` connection property: ``` r2dbcs:oracle://db.host.example.com:1522/db.service.name?oracle.net.wallet_location=/path/to/wallet/ ``` -The same property can also be configured programmatically: +And, the `OracleR2dbcOptions` constants can be used in programmatic +configuration: ```java ConnectionFactoryOptions.builder() .option(OracleR2dbcOptions.TLS_WALLET_LOCATION, "/path/to/wallet") ``` -The next sections list Oracle JDBC connection properties which are supported by -Oracle R2DBC. +All Oracle JDBC connection properties defined in `OracleR2dbcOptions` are listed +in the next sections. ##### TLS/SSL Connection Properties - [oracle.net.tns_admin](https://docs.oracle.com/en/database/oracle/oracle-database/23/jajdb/oracle/jdbc/OracleConnection.html?is-external=true#CONNECTION_PROPERTY_TNS_ADMIN) diff --git a/src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryProviderImplTest.java b/src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryProviderImplTest.java index 01273fc..406d8f9 100644 --- a/src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryProviderImplTest.java +++ b/src/test/java/oracle/r2dbc/impl/OracleConnectionFactoryProviderImplTest.java @@ -33,7 +33,6 @@ import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import java.util.concurrent.TimeoutException; import java.util.function.Supplier; import static io.r2dbc.spi.ConnectionFactoryOptions.DATABASE; @@ -56,7 +55,7 @@ /** * Verifies that * {@link OracleConnectionFactoryProviderImpl} implements behavior that - * is specified in it's class and method level javadocs. + * is specified in its class and method level javadocs. */ public class OracleConnectionFactoryProviderImplTest { @@ -216,7 +215,7 @@ public void testSupplierOptionNull() { Supplier<Integer> portSupplier = DatabaseConfig::port; Supplier<String> databaseSupplier = DatabaseConfig::serviceName; Supplier<String> userSupplier = DatabaseConfig::user; - TestSupplier<CharSequence> passwordSupplier = new TestSupplier(password()); + TestSupplier<CharSequence> passwordSupplier = new TestSupplier<>(password()); ConnectionFactoryOptions connectionFactoryOptions = connectionFactoryOptions() @@ -326,7 +325,7 @@ public void testPublisherOptionNull() { Publisher<Integer> portPublisher = Mono.fromSupplier(DatabaseConfig::port); Publisher<String> databasePublisher = Mono.fromSupplier(DatabaseConfig::serviceName); Publisher<String> userPublisher = Mono.fromSupplier(DatabaseConfig::user); - TestSupplier<CharSequence> passwordPublisher = new TestSupplier(password()); + TestSupplier<CharSequence> passwordPublisher = new TestSupplier<>(password()); ConnectionFactoryOptions connectionFactoryOptions = connectionFactoryOptions() diff --git a/src/test/java/oracle/r2dbc/impl/OracleResultImplTest.java b/src/test/java/oracle/r2dbc/impl/OracleResultImplTest.java index 66bd37f..735b3b8 100644 --- a/src/test/java/oracle/r2dbc/impl/OracleResultImplTest.java +++ b/src/test/java/oracle/r2dbc/impl/OracleResultImplTest.java @@ -640,10 +640,10 @@ public void testOracleR2dbcWarning() { Connection connection = awaitOne(sharedConnection()); try { - // Expect a warning for forcing a view that references a non-existent + // Expect a warning for invalid PL/SQL // table - String sql = "CREATE OR REPLACE FORCE VIEW testOracleR2dbcWarning AS" + - " SELECT x FROM thisdoesnotexist"; + String sql = "CREATE OR REPLACE PROCEDURE testOracleR2dbcWarning AS" + + " BEGIN this is not valid pl/sql; END;"; Statement warningStatement = connection.createStatement(sql); // Collect the segments @@ -684,7 +684,7 @@ public void testOracleR2dbcWarning() { } finally { tryAwaitExecution( - connection.createStatement("DROP VIEW testOracleR2dbcWarning")); + connection.createStatement("DROP PROCEDURE testOracleR2dbcWarning")); tryAwaitNone(connection.close()); } } @@ -697,11 +697,10 @@ public void testOracleR2dbcWarningIgnored() { Connection connection = awaitOne(sharedConnection()); try { - // Expect a warning for forcing a view that references a non-existent - // table + // Expect a warning for invalid PL/SQL String sql = - "CREATE OR REPLACE FORCE VIEW testOracleR2dbcWarningIgnored AS" + - " SELECT x FROM thisdoesnotexist"; + "CREATE OR REPLACE PROCEDURE testOracleR2dbcWarningIgnored AS" + + " BEGIN this is not valid pl/sql; END;"; Statement warningStatement = connection.createStatement(sql); // Verify that an update count of 0 is returned. @@ -719,7 +718,8 @@ public void testOracleR2dbcWarningIgnored() { } finally { tryAwaitExecution( - connection.createStatement("DROP VIEW testOracleR2dbcWarningIgnored")); + connection.createStatement( + "DROP PROCEDURE testOracleR2dbcWarningIgnored")); tryAwaitNone(connection.close()); } } @@ -732,11 +732,10 @@ public void testOracleR2dbcWarningIgnored() { public void testOracleR2dbcWarningNotIgnored() { Connection connection = awaitOne(sharedConnection()); try { - // Expect a warning for forcing a view that references a non-existent - // table + // Expect a warning for invalid PL/SQL String sql = - "CREATE OR REPLACE FORCE VIEW testOracleR2dbcWarningIgnored AS" + - " SELECT x FROM thisdoesnotexist"; + "CREATE OR REPLACE PROCEDURE testOracleR2dbcWarningIgnored AS" + + " BEGIN this is not valid pl/sql; END;"; Statement warningStatement = connection.createStatement(sql); AtomicInteger segmentIndex = new AtomicInteger(0); awaitError( @@ -754,7 +753,8 @@ public void testOracleR2dbcWarningNotIgnored() { } finally { tryAwaitExecution( - connection.createStatement("DROP VIEW testOracleR2dbcWarningIgnored")); + connection.createStatement( + "DROP PROCEDURE testOracleR2dbcWarningIgnored")); tryAwaitNone(connection.close()); } } diff --git a/src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java b/src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java index d0a2f1b..e88cf04 100644 --- a/src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java +++ b/src/test/java/oracle/r2dbc/impl/OracleStatementImplTest.java @@ -2476,6 +2476,11 @@ public boolean equals(Object other) { public String toString() { return id + ", " + Arrays.toString(value); } + + @Override + public int hashCode() { + return Objects.hash(id, Arrays.hashCode(value)); + } } TestRow row0 = new TestRow(0L, new int[]{1, 2, 3}); @@ -2684,6 +2689,11 @@ public boolean equals(Object other) { public String toString() { return id + ", " + Arrays.toString(value); } + + @Override + public int hashCode() { + return Objects.hash(id, Arrays.hashCode(value)); + } } OracleR2dbcTypes.ArrayType arrayType = @@ -3199,6 +3209,11 @@ public boolean equals(Object other) { && ((IdVector)other).id == id && Objects.equals(((IdVector)other).vector, vector); } + + @Override + public int hashCode() { + return Objects.hash(id, vector); + } } // Round 1: Use PL/SQL to return column values diff --git a/src/test/java/oracle/r2dbc/test/DatabaseConfig.java b/src/test/java/oracle/r2dbc/test/DatabaseConfig.java index aa67679..56ae2cb 100644 --- a/src/test/java/oracle/r2dbc/test/DatabaseConfig.java +++ b/src/test/java/oracle/r2dbc/test/DatabaseConfig.java @@ -35,6 +35,7 @@ import java.sql.DriverManager; import java.sql.SQLException; import java.time.Duration; +import java.util.Optional; import java.util.Properties; /** @@ -178,9 +179,9 @@ public static Publisher<? extends Connection> sharedConnection() { * @return The major version number of the test database. */ public static int databaseVersion() { - try (var jdbcConnection = DriverManager.getConnection(String.format( - "jdbc:oracle:thin:@%s:%s/%s", host(), port(), serviceName()), - user(), password())) { + try ( + var jdbcConnection = + DriverManager.getConnection(jdbcUrl(), user(), password())) { return jdbcConnection.getMetaData().getDatabaseMajorVersion(); } catch (SQLException sqlException) { @@ -188,6 +189,17 @@ public static int databaseVersion() { } } + /** + * Returns an Oracle JDBC URL for opening connections to the test database. + * @return URL for the Oracle JDBC Driver. Not null. + */ + public static String jdbcUrl() { + return String.format( + "jdbc:oracle:thin:@%s%s:%d/%s", + protocol() == null ? "" : protocol() + ":", + host(), port(), serviceName()); + } + /** * Returns the major version number of the Oracle JDBC Driver installed as * a service provider for java.sql.Driver. diff --git a/src/test/java/oracle/r2dbc/test/OracleTestKit.java b/src/test/java/oracle/r2dbc/test/OracleTestKit.java index 90cabca..fe58473 100755 --- a/src/test/java/oracle/r2dbc/test/OracleTestKit.java +++ b/src/test/java/oracle/r2dbc/test/OracleTestKit.java @@ -57,6 +57,7 @@ import static io.r2dbc.spi.ConnectionFactoryOptions.USER; import static oracle.r2dbc.test.DatabaseConfig.connectionFactoryOptions; import static oracle.r2dbc.test.DatabaseConfig.host; +import static oracle.r2dbc.test.DatabaseConfig.jdbcUrl; import static oracle.r2dbc.test.DatabaseConfig.password; import static oracle.r2dbc.test.DatabaseConfig.port; import static oracle.r2dbc.test.DatabaseConfig.protocol; @@ -95,11 +96,7 @@ public class OracleTestKit implements TestKit<Integer> { try { OracleDataSource dataSource = new oracle.jdbc.datasource.impl.OracleDataSource(); - dataSource.setURL(String.format("jdbc:oracle:thin:@%s%s:%d/%s", - Optional.ofNullable(protocol()) - .map(protocol -> protocol + ":") - .orElse(""), - host(), port(), serviceName())); + dataSource.setURL(jdbcUrl()); dataSource.setUser(user()); dataSource.setPassword(password()); this.jdbcOperations = new JdbcTemplate(dataSource); diff --git a/src/test/java/oracle/r2dbc/util/SharedConnectionFactory.java b/src/test/java/oracle/r2dbc/util/SharedConnectionFactory.java index 41fdd96..1fd8ddc 100644 --- a/src/test/java/oracle/r2dbc/util/SharedConnectionFactory.java +++ b/src/test/java/oracle/r2dbc/util/SharedConnectionFactory.java @@ -245,7 +245,7 @@ private static RuntimeException openCursorNotAccessible() { return new RuntimeException( "V$OPEN_CUROSR is not accessible to the test user. " + "Grant access as SYSDBA with: " + - "\"GRANT SELECT ON v_$open_cursor TO "+user()+"\", " + + "\"GRANT SELECT ON v$open_cursor TO "+user()+"\", " + "or disable open cursor checks with: " + " -Doracle.r2bdc.disableCursorCloseVerification=true"); }