Skip to content

Commit bc7e1a6

Browse files
Merge pull request #155 from oracle/147-row-prefetch-option
Fetch Size and Proxy Client Name Options
2 parents 143cf9f + f8df776 commit bc7e1a6

File tree

6 files changed

+166
-41
lines changed

6 files changed

+166
-41
lines changed

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
@@ -597,8 +597,7 @@ private Publisher<JdbcStatement> createJdbcStatement() {
597597
return adapter.getLock().get(() -> {
598598
PreparedStatement preparedStatement =
599599
jdbcConnection.prepareStatement(sql);
600-
preparedStatement.setFetchSize(currentFetchSize);
601-
preparedStatement.setQueryTimeout(timeout);
600+
configureJdbcStatement(preparedStatement, currentFetchSize, timeout);
602601
return new JdbcStatement(preparedStatement, currentBinds);
603602
});
604603
}
@@ -629,8 +628,7 @@ private Publisher<JdbcStatement> createJdbcBatch() {
629628
return adapter.getLock().get(() -> {
630629
PreparedStatement preparedStatement =
631630
jdbcConnection.prepareStatement(sql);
632-
preparedStatement.setFetchSize(currentFetchSize);
633-
preparedStatement.setQueryTimeout(timeout);
631+
configureJdbcStatement(preparedStatement, currentFetchSize, timeout);
634632
return finalInvalidBinds == null
635633
? new JdbcBatch(preparedStatement, currentBatch)
636634
: new JdbcBatchInvalidBinds(
@@ -649,8 +647,7 @@ private Publisher<JdbcStatement> createJdbcCall() {
649647

650648
return adapter.getLock().get(() -> {
651649
CallableStatement callableStatement = jdbcConnection.prepareCall(sql);
652-
callableStatement.setFetchSize(currentFetchSize);
653-
callableStatement.setQueryTimeout(timeout);
650+
configureJdbcStatement(callableStatement, currentFetchSize, timeout);
654651
return new JdbcCall(callableStatement, currentBinds, parameterNames);
655652
});
656653
}
@@ -671,12 +668,43 @@ private Publisher<JdbcStatement> createJdbcReturningGenerated() {
671668
currentGeneratedColumns.length == 0
672669
? jdbcConnection.prepareStatement(sql, RETURN_GENERATED_KEYS)
673670
: jdbcConnection.prepareStatement(sql, currentGeneratedColumns);
674-
preparedStatement.setFetchSize(currentFetchSize);
675-
preparedStatement.setQueryTimeout(timeout);
671+
configureJdbcStatement(preparedStatement, currentFetchSize, timeout);
676672
return new JdbcReturningGenerated(preparedStatement, currentBinds);
677673
});
678674
}
679675

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

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

+100-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;
@@ -98,6 +99,7 @@
9899
import static org.junit.jupiter.api.Assertions.assertThrows;
99100
import static org.junit.jupiter.api.Assertions.assertTrue;
100101
import static org.junit.jupiter.api.Assertions.fail;
102+
import static org.junit.jupiter.api.Assumptions.assumeTrue;
101103

102104
/**
103105
* Verifies that
@@ -118,23 +120,7 @@ public void testCreateDataSource() throws SQLException {
118120
// properties. The defaultProperties variable contains properties that
119121
// are set to default values by OracleReactiveJdbcAdapter and the Oracle
120122
// JDBC Driver
121-
Properties defaultProperties = new Properties();
122-
defaultProperties.setProperty(
123-
OracleConnection.CONNECTION_PROPERTY_J2EE13_COMPLIANT, "true");
124-
defaultProperties.setProperty(
125-
OracleConnection.CONNECTION_PROPERTY_IMPLICIT_STATEMENT_CACHE_SIZE, "25");
126-
defaultProperties.setProperty(
127-
OracleConnection.CONNECTION_PROPERTY_DEFAULT_LOB_PREFETCH_SIZE,
128-
"1048576");
129-
defaultProperties.setProperty(
130-
OracleConnection.CONNECTION_PROPERTY_THIN_NET_USE_ZERO_COPY_IO,
131-
"false");
132-
133-
if (jdbcVersion() == 21) {
134-
// Oracle JDBC no longer sets this AC property by default in 23.3
135-
defaultProperties.setProperty(
136-
OracleConnection.CONNECTION_PROPERTY_ENABLE_AC_SUPPORT, "false");
137-
}
123+
Properties defaultProperties = getJdbcDefaultProperties();
138124

139125
// Expect only default connection properties when no extended
140126
// options are supplied
@@ -672,7 +658,7 @@ public void testTimezoneAsRegion() {
672658
*/
673659
@Test
674660
public void testEmptyProtocol() {
675-
Assumptions.assumeTrue(
661+
assumeTrue(
676662
DatabaseConfig.protocol() == null,
677663
"Test requires no PROTOCOL in config.properties");
678664

@@ -704,6 +690,100 @@ public void testEmptyProtocol() {
704690
}
705691
}
706692

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

src/test/java/oracle/r2dbc/test/OracleTestKit.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,8 @@ public class OracleTestKit implements TestKit<Integer> {
9393
private final JdbcOperations jdbcOperations;
9494
{
9595
try {
96-
OracleDataSource dataSource = new oracle.jdbc.pool.OracleDataSource();
96+
OracleDataSource dataSource =
97+
new oracle.jdbc.datasource.impl.OracleDataSource();
9798
dataSource.setURL(String.format("jdbc:oracle:thin:@%s%s:%d/%s",
9899
Optional.ofNullable(protocol())
99100
.map(protocol -> protocol + ":")

0 commit comments

Comments
 (0)