Skip to content

Commit 3dec13d

Browse files
Merge branch 'main' of github.com:oracle/oracle-r2dbc into jdbc-23.5
2 parents ba7a5f2 + bc7e1a6 commit 3dec13d

File tree

9 files changed

+231
-49
lines changed

9 files changed

+231
-49
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -989,7 +989,7 @@ Use the `oracle.r2dbc.OracleR2dbcTypes.REF_CURSOR` type to bind `SYS_REFCURSOR`
989989
out parameters:
990990
```java
991991
Publisher<Result> executeProcedure(Connection connection) {
992-
connection.createStatement(
992+
return connection.createStatement(
993993
"BEGIN example_procedure(:cursor_parameter); END;")
994994
.bind("cursor_parameter", Parameters.out(OracleR2dbcTypes.REF_CURSOR))
995995
.execute()

sample/src/main/java/oracle/r2dbc/samples/JdbcToR2dbc.java

+3-2
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
import io.r2dbc.spi.ConnectionFactoryOptions;
2727
import io.r2dbc.spi.R2dbcException;
2828
import io.r2dbc.spi.Result;
29-
import oracle.jdbc.pool.OracleDataSource;
29+
import oracle.jdbc.datasource.impl.OracleDataSource;
3030
import org.reactivestreams.Publisher;
3131
import reactor.core.publisher.Flux;
3232

@@ -141,7 +141,8 @@ public final class JdbcToR2dbc {
141141
*/
142142
static DataSource configureJdbc() throws SQLException {
143143

144-
OracleDataSource dataSource = new oracle.jdbc.pool.OracleDataSource();
144+
OracleDataSource dataSource =
145+
new oracle.jdbc.datasource.impl.OracleDataSource();
145146
dataSource.setDriverType("thin");
146147
dataSource.setServerName(DatabaseConfig.HOST);
147148
dataSource.setPortNumber(DatabaseConfig.PORT);

src/main/java/oracle/r2dbc/OracleR2dbcOptions.java

+17-1
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,18 @@ private OracleR2dbcOptions() {}
396396
*/
397397
public static final Option<CharSequence> KERBEROS_JAAS_LOGIN_MODULE;
398398

399+
/**
400+
* Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by:
401+
* {@link OracleConnection#CONNECTION_PROPERTY_DEFAULT_ROW_PREFETCH}
402+
*/
403+
public static final Option<Integer> DEFAULT_FETCH_SIZE;
404+
405+
/**
406+
* Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by:
407+
* {@link OracleConnection#CONNECTION_PROPERTY_PROXY_CLIENT_NAME}
408+
*/
409+
public static final Option<CharSequence> PROXY_CLIENT_NAME;
410+
399411
/** The unmodifiable set of all extended options */
400412
private static final Set<Option<?>> OPTIONS = Set.of(
401413
DESCRIPTOR = Option.valueOf("oracle.r2dbc.descriptor"),
@@ -509,7 +521,11 @@ private OracleR2dbcOptions() {}
509521
KERBEROS_REALM = Option.valueOf(
510522
OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_KRB_REALM),
511523
KERBEROS_JAAS_LOGIN_MODULE = Option.valueOf(
512-
OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_KRB_JAAS_LOGIN_MODULE)
524+
OracleConnection.CONNECTION_PROPERTY_THIN_NET_AUTHENTICATION_KRB_JAAS_LOGIN_MODULE),
525+
DEFAULT_FETCH_SIZE = Option.valueOf(
526+
OracleConnection.CONNECTION_PROPERTY_DEFAULT_ROW_PREFETCH),
527+
PROXY_CLIENT_NAME = Option.valueOf(
528+
OracleConnection.CONNECTION_PROPERTY_PROXY_CLIENT_NAME)
513529
);
514530

515531
/**

src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java

+8-9
Original file line numberDiff line numberDiff line change
@@ -341,7 +341,7 @@ public AsyncLock getLock() {
341341
public DataSource createDataSource(ConnectionFactoryOptions options) {
342342

343343
OracleDataSource oracleDataSource =
344-
fromJdbc(oracle.jdbc.pool.OracleDataSource::new);
344+
fromJdbc(oracle.jdbc.datasource.impl.OracleDataSource::new);
345345

346346
runJdbc(() -> oracleDataSource.setURL(composeJdbcUrl(options)));
347347
configureStandardOptions(oracleDataSource, options);
@@ -632,15 +632,14 @@ private static void configureJdbcDefaults(OracleDataSource oracleDataSource) {
632632
// its effects. One effect is to have ResultSetMetaData describe
633633
// FLOAT columns as the FLOAT type, rather than the NUMBER type. This
634634
// effect allows the Oracle R2DBC Driver obtain correct metadata for
635-
// FLOAT type columns. The property is deprecated, but the deprecation note
636-
// explains that setting this to "false" is deprecated, and that it
637-
// should be set to true; If not set, the 21c driver uses a default value
638-
// of false.
639-
@SuppressWarnings("deprecation")
640-
String enableJdbcSpecCompliance =
641-
OracleConnection.CONNECTION_PROPERTY_J2EE13_COMPLIANT;
635+
// FLOAT type columns.
636+
// The OracleConnection.CONNECTION_PROPERTY_J2EE13_COMPLIANT field is
637+
// deprecated, so the String literal value of this field is used instead,
638+
// just in case the field were to be removed in a future release of Oracle
639+
// JDBC.
642640
runJdbc(() ->
643-
oracleDataSource.setConnectionProperty(enableJdbcSpecCompliance, "true"));
641+
oracleDataSource.setConnectionProperty(
642+
"oracle.jdbc.J2EE13Compliant", "true"));
644643

645644
// Cache PreparedStatements by default. The default value of the
646645
// OPEN_CURSORS parameter in the 21c and 19c databases is 50:

src/main/java/oracle/r2dbc/impl/OracleStatementImpl.java

+36-8
Original file line numberDiff line numberDiff line change
@@ -602,8 +602,7 @@ private Publisher<JdbcStatement> createJdbcStatement() {
602602
return adapter.getLock().get(() -> {
603603
PreparedStatement preparedStatement =
604604
jdbcConnection.prepareStatement(sql);
605-
preparedStatement.setFetchSize(currentFetchSize);
606-
preparedStatement.setQueryTimeout(timeout);
605+
configureJdbcStatement(preparedStatement, currentFetchSize, timeout);
607606
return new JdbcStatement(preparedStatement, currentBinds);
608607
});
609608
}
@@ -634,8 +633,7 @@ private Publisher<JdbcStatement> createJdbcBatch() {
634633
return adapter.getLock().get(() -> {
635634
PreparedStatement preparedStatement =
636635
jdbcConnection.prepareStatement(sql);
637-
preparedStatement.setFetchSize(currentFetchSize);
638-
preparedStatement.setQueryTimeout(timeout);
636+
configureJdbcStatement(preparedStatement, currentFetchSize, timeout);
639637
return finalInvalidBinds == null
640638
? new JdbcBatch(preparedStatement, currentBatch)
641639
: new JdbcBatchInvalidBinds(
@@ -654,8 +652,7 @@ private Publisher<JdbcStatement> createJdbcCall() {
654652

655653
return adapter.getLock().get(() -> {
656654
CallableStatement callableStatement = jdbcConnection.prepareCall(sql);
657-
callableStatement.setFetchSize(currentFetchSize);
658-
callableStatement.setQueryTimeout(timeout);
655+
configureJdbcStatement(callableStatement, currentFetchSize, timeout);
659656
return new JdbcCall(callableStatement, currentBinds, parameterNames);
660657
});
661658
}
@@ -676,12 +673,43 @@ private Publisher<JdbcStatement> createJdbcReturningGenerated() {
676673
currentGeneratedColumns.length == 0
677674
? jdbcConnection.prepareStatement(sql, RETURN_GENERATED_KEYS)
678675
: jdbcConnection.prepareStatement(sql, currentGeneratedColumns);
679-
preparedStatement.setFetchSize(currentFetchSize);
680-
preparedStatement.setQueryTimeout(timeout);
676+
configureJdbcStatement(preparedStatement, currentFetchSize, timeout);
681677
return new JdbcReturningGenerated(preparedStatement, currentBinds);
682678
});
683679
}
684680

681+
/**
682+
* Configures a JDBC Statement with values that have been configured on an
683+
* R2DBC Statement.
684+
*
685+
* @param statement The statement to configure. Not null.
686+
*
687+
* @param fetchSize Configuration of {@link #fetchSize(int)}, possibly 0 if
688+
* a default size should be used.
689+
*
690+
* @param queryTimeout Configuration of {@link #timeout}, possibly 0 if no
691+
* timeout should be used.
692+
*
693+
* @throws SQLException If the JDBC statement is closed.
694+
*/
695+
private static void configureJdbcStatement(
696+
java.sql.Statement statement, int fetchSize, int queryTimeout)
697+
throws SQLException {
698+
699+
// It is noted that Oracle JDBC's feature of auto-tuning fetch sizes will
700+
// be disabled if 0 is passed to setFetchSize. Perhaps similar behavior
701+
// occurs with methods like setQueryTimeout as well? To be sure, don't call
702+
// any methods unless non-default values are set.
703+
704+
if (fetchSize != 0) {
705+
statement.setFetchSize(fetchSize);
706+
}
707+
708+
if (queryTimeout != 0) {
709+
statement.setQueryTimeout(queryTimeout);
710+
}
711+
}
712+
685713
/**
686714
* Binds a {@code value} to all named parameters matching the specified
687715
* {@code name}. The match is case-sensitive.

src/test/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapterTest.java

+99-20
Original file line numberDiff line numberDiff line change
@@ -34,11 +34,11 @@
3434
import oracle.r2dbc.OracleR2dbcOptions;
3535
import oracle.r2dbc.test.DatabaseConfig;
3636
import oracle.r2dbc.util.TestContextFactory;
37-
import org.junit.jupiter.api.Assumptions;
3837
import org.junit.jupiter.api.Test;
3938
import reactor.core.publisher.Flux;
4039
import reactor.core.publisher.Mono;
4140

41+
import javax.sql.DataSource;
4242
import java.io.IOException;
4343
import java.nio.channels.ServerSocketChannel;
4444
import java.nio.channels.SocketChannel;
@@ -49,6 +49,7 @@
4949
import java.time.Duration;
5050
import java.time.ZonedDateTime;
5151
import java.util.List;
52+
import java.util.Map;
5253
import java.util.Objects;
5354
import java.util.Optional;
5455
import java.util.Properties;
@@ -63,6 +64,7 @@
6364
import java.util.concurrent.TimeUnit;
6465
import java.util.concurrent.TimeoutException;
6566
import java.util.concurrent.atomic.AtomicInteger;
67+
import java.util.function.Function;
6668
import java.util.stream.Collectors;
6769

6870
import static io.r2dbc.spi.ConnectionFactoryOptions.CONNECT_TIMEOUT;
@@ -78,7 +80,6 @@
7880
import static oracle.r2dbc.test.DatabaseConfig.connectTimeout;
7981
import static oracle.r2dbc.test.DatabaseConfig.connectionFactoryOptions;
8082
import static oracle.r2dbc.test.DatabaseConfig.host;
81-
import static oracle.r2dbc.test.DatabaseConfig.jdbcVersion;
8283
import static oracle.r2dbc.test.DatabaseConfig.password;
8384
import static oracle.r2dbc.test.DatabaseConfig.port;
8485
import static oracle.r2dbc.test.DatabaseConfig.protocol;
@@ -119,23 +120,7 @@ public void testCreateDataSource() throws SQLException {
119120
// properties. The defaultProperties variable contains properties that
120121
// are set to default values by OracleReactiveJdbcAdapter and the Oracle
121122
// JDBC Driver
122-
Properties defaultProperties = new Properties();
123-
defaultProperties.setProperty(
124-
OracleConnection.CONNECTION_PROPERTY_J2EE13_COMPLIANT, "true");
125-
defaultProperties.setProperty(
126-
OracleConnection.CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE, "25");
127-
defaultProperties.setProperty(
128-
OracleConnection.CONNECTION_PROPERTY_DEFAULT_LOB_PREFETCH_SIZE,
129-
"1048576");
130-
defaultProperties.setProperty(
131-
OracleConnection.CONNECTION_PROPERTY_THIN_NET_USE_ZERO_COPY_IO,
132-
"false");
133-
134-
if (jdbcVersion() == 21) {
135-
// Oracle JDBC no longer sets this AC property by default in 23.3
136-
defaultProperties.setProperty(
137-
OracleConnection.CONNECTION_PROPERTY_ENABLE_AC_SUPPORT, "false");
138-
}
123+
Properties defaultProperties = getJdbcDefaultProperties();
139124

140125
// Expect only default connection properties when no extended
141126
// options are supplied
@@ -681,7 +666,7 @@ public void testTimezoneAsRegion() {
681666
*/
682667
@Test
683668
public void testEmptyProtocol() {
684-
Assumptions.assumeTrue(
669+
assumeTrue(
685670
DatabaseConfig.protocol() == null,
686671
"Test requires no PROTOCOL in config.properties");
687672

@@ -713,6 +698,100 @@ public void testEmptyProtocol() {
713698
}
714699
}
715700

701+
@Test
702+
public void testJdbcPropertyOptions() throws SQLException {
703+
704+
// Create a map where every Option of OracleR2dbcOptions is assigned to a
705+
// value. The values are not necessarily valid, or even of the right class
706+
// (every option is cast to Option<String>). That's OK because this test
707+
// just wants to make sure the values are transferred to OracleDataSource,
708+
// and it won't actually attempt to create a connection with these values.
709+
Map<Option<String>, String> optionValues =
710+
OracleR2dbcOptions.options()
711+
.stream()
712+
.map(option -> {
713+
@SuppressWarnings("unchecked")
714+
Option<String> stringOption = (Option<String>)option;
715+
return stringOption;
716+
})
717+
.collect(Collectors.toMap(
718+
Function.identity(),
719+
option -> "VALUE OF " + option.name()
720+
));
721+
722+
ConnectionFactoryOptions.Builder optionsBuilder =
723+
ConnectionFactoryOptions.builder();
724+
optionValues.forEach(optionsBuilder::option);
725+
726+
DataSource dataSource =
727+
OracleReactiveJdbcAdapter.getInstance()
728+
.createDataSource(optionsBuilder.build());
729+
assumeTrue(dataSource.isWrapperFor(OracleDataSource.class));
730+
731+
Properties actualProperties =
732+
dataSource.unwrap(OracleDataSource.class)
733+
.getConnectionProperties();
734+
735+
Properties expectedProperties = getJdbcDefaultProperties();
736+
optionValues.forEach((option, value) ->
737+
expectedProperties.setProperty(option.name(), value));
738+
739+
expectedProperties.entrySet()
740+
.removeAll(actualProperties.entrySet());
741+
742+
// Don't expect OracleDataSource.getConnectionProperties() to have entries
743+
// for options that Oracle R2DBC doesn't set as connection properties.
744+
expectedProperties.entrySet()
745+
.removeIf(entry ->
746+
entry.getKey().toString().startsWith("oracle.r2dbc."));
747+
748+
// Don't expect OracleDataSource.getConnectionProperties() to have entries
749+
// for options of security sensitive values.
750+
expectedProperties.entrySet()
751+
.removeIf(entry ->
752+
entry.getKey().toString().toLowerCase().contains("password"));
753+
754+
assertTrue(
755+
expectedProperties.isEmpty(),
756+
"One or more properties were not set: " + expectedProperties);
757+
}
758+
759+
/**
760+
* Returns the connection properties that will be set by default when an
761+
* {@link OracleDataSource} is created. Tests which verify the setting of
762+
* properties can assume these default properties will be set as well.
763+
*
764+
* @return Properties that OracleDataSource sets by default.
765+
*/
766+
private static Properties getJdbcDefaultProperties() throws SQLException {
767+
768+
// Start with any properties that JDBC will set by default. For example, the
769+
// 21 driver would set CONNECTION_PROPERTY_ENABLE_AC_SUPPORT="false" by
770+
// default.
771+
Properties defaultProperties =
772+
new oracle.jdbc.datasource.impl.OracleDataSource()
773+
.getConnectionProperties();
774+
775+
if (defaultProperties == null)
776+
defaultProperties = new Properties();
777+
778+
// Set the properties that Oracle R2DBC will set by default
779+
// Not referencing the deprecated
780+
// OracleConnection.CONNECTION_PROPERTY_J2EE13_COMPLIANT field, just in case
781+
// it gets removed in a future release of Oracle JDBC.
782+
defaultProperties.setProperty("oracle.jdbc.J2EE13Compliant", "true");
783+
defaultProperties.setProperty(
784+
OracleConnection.CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE, "25");
785+
defaultProperties.setProperty(
786+
OracleConnection.CONNECTION_PROPERTY_DEFAULT_LOB_PREFETCH_SIZE,
787+
"1048576");
788+
defaultProperties.setProperty(
789+
OracleConnection.CONNECTION_PROPERTY_THIN_NET_USE_ZERO_COPY_IO,
790+
"false");
791+
792+
return defaultProperties;
793+
}
794+
716795
/**
717796
* Returns an Oracle Net Descriptor having the values configured by
718797
* {@link DatabaseConfig}

src/test/java/oracle/r2dbc/impl/OracleReadableMetadataImplTest.java

+25-1
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,8 @@ public void testObjectTypes() {
532532
*/
533533
@Test
534534
public void testVectorType() throws SQLException {
535-
Assumptions.assumeTrue(databaseVersion() >= 23);
535+
Assumptions.assumeTrue(databaseVersion() >= 23,
536+
"VECTOR requires Oracle Database 23ai or newer");
536537

537538
Connection connection =
538539
Mono.from(sharedConnection()).block(connectTimeout());
@@ -552,7 +553,30 @@ public void testVectorType() throws SQLException {
552553
finally {
553554
tryAwaitNone(connection.close());
554555
}
556+
}
557+
558+
/**
559+
* Verifies the implementation of {@link OracleReadableMetadataImpl} for
560+
* BOOLEAN type columns. When the test database is older than version 23ai,
561+
* this test is ignored; The BOOLEAN type was added in 23ai.
562+
*/
563+
@Test
564+
public void testBooleanType() throws SQLException {
565+
Assumptions.assumeTrue(databaseVersion() >= 23,
566+
"BOOLEAN requires Oracle Database 23ai or newer");
555567

568+
Connection connection =
569+
Mono.from(sharedConnection()).block(connectTimeout());
570+
try {
571+
// Expect BOOLEAN and Boolean to map.
572+
verifyColumnMetadata(
573+
connection, "BOOLEAN", JDBCType.BOOLEAN, R2dbcType.BOOLEAN,
574+
null, null,
575+
Boolean.class, true);
576+
}
577+
finally {
578+
tryAwaitNone(connection.close());
579+
}
556580
}
557581

558582
/**

0 commit comments

Comments
 (0)