Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a timezone as region option #81

Merged
merged 3 commits into from
Sep 3, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Add timezone as region option
  • Loading branch information
Michael-A-McMahon committed Jul 6, 2022
commit ab61bd83a751d005ba0cfd399738edba5507ca90
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -290,13 +290,17 @@ supported by Oracle R2DBC:
- Out of band (OOB) breaks effect statement timeouts. Set this to "true" if statement timeouts are not working correctly.
- [oracle.jdbc.enableQueryResultCache](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_ENABLE_QUERY_RESULT_CACHE)
- Cached query results can cause phantom reads even if the serializable
transaction isolation level is set. Set this to "false" if using the
transaction isolation level is set. Set this to "false" if using the
serializable isolation level.
- [v$session.terminal](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_THIN_VSESSION_TERMINAL)
- [v$session.machine](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_THIN_VSESSION_MACHINE)
- [v$session.osuser](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_THIN_VSESSION_OSUSER)
- [v$session.program](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_THIN_VSESSION_PROGRAM)
- [v$session.process](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_THIN_VSESSION_PROCESS)
- (For inclusion in the next release) [oracle.jdbc.timeZoneAsRegion](https://docs.oracle.com/en/database/oracle/oracle-database/21/jajdb/oracle/jdbc/OracleConnection.html#CONNECTION_PROPERTY_TIMEZONE_AS_REGION)
- Setting this option to "false" may resolve "ORA-01882: timezone region not
found". The ORA-01882 error happens when Oracle Database doesn't recognize
the name returned by `java.util.TimeZone.getDefault().getId()`.

### Thread Safety and Parallel Execution
Oracle R2DBC's `ConnectionFactory` and `ConnectionFactoryProvider` are the only
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/oracle/r2dbc/OracleR2dbcOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -259,4 +259,11 @@ private OracleR2dbcOptions() {}
public static final Option<CharSequence> VSESSION_PROCESS =
Option.valueOf(OracleConnection.CONNECTION_PROPERTY_THIN_VSESSION_PROCESS);

/**
* Configures the Oracle JDBC Connection used by Oracle R2DBC as specified by:
* {@link OracleConnection#CONNECTION_PROPERTY_TIMEZONE_AS_REGION}
*/
public static final Option<CharSequence> TIMEZONE_AS_REGION =
Option.valueOf(OracleConnection.CONNECTION_PROPERTY_TIMEZONE_AS_REGION);

}
19 changes: 10 additions & 9 deletions src/main/java/oracle/r2dbc/impl/OracleReactiveJdbcAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,13 @@ final class OracleReactiveJdbcAdapter implements ReactiveJdbcAdapter {
OracleR2dbcOptions.VSESSION_TERMINAL,
OracleR2dbcOptions.VSESSION_PROCESS,
OracleR2dbcOptions.VSESSION_PROGRAM,
OracleR2dbcOptions.VSESSION_MACHINE
OracleR2dbcOptions.VSESSION_MACHINE,

// Allow JDBC to configure the session timezone as an offset of UTC
// (ie: +09:00), rather than a name (ie: Etc/UTC). This avoids
// "ORA-01882: timezone region not found" when the name of the JVM's
// default timezone is not recognized by Oracle Database.
OracleR2dbcOptions.TIMEZONE_AS_REGION
);

/** Guards access to a JDBC {@code Connection} created by this adapter */
Expand Down Expand Up @@ -401,17 +407,12 @@ public AsyncLock getLock() {
* </li><li>
* {@linkplain OracleConnection#CONNECTION_PROPERTY_THIN_VSESSION_MACHINE
* v$session.machine}
* </li><li>
* {@linkplain OracleConnection#CONNECTION_PROPERTY_TIMEZONE_AS_REGION
* oracle.jdbc.timezoneAsRegion}
* </li>
* </ul>
*
* @implNote The returned {@code DataSource} is configured to create
* connections that encode character bind values using the National
* Character Set of an Oracle Database. In 21c, the National Character Set
* must be either UTF-8 or UTF-16; This ensures that unicode bind data is
* properly encoded by Oracle JDBC. If the data source is not configured
* this way, the Oracle JDBC Driver uses the default character set of the
* database, which may not support Unicode characters.
*
* @throws IllegalArgumentException If the {@code oracleNetDescriptor}
* {@code Option} is provided with any other options that might have
* conflicting values, such as {@link ConnectionFactoryOptions#HOST}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import oracle.jdbc.OracleConnection;
import oracle.jdbc.datasource.OracleDataSource;
import oracle.r2dbc.OracleR2dbcOptions;
import oracle.r2dbc.test.DatabaseConfig;
import org.junit.jupiter.api.Test;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
Expand All @@ -43,8 +42,10 @@
import java.nio.file.StandardOpenOption;
import java.sql.SQLException;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.Properties;
import java.util.Set;
import java.util.TimeZone;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutionException;
Expand Down Expand Up @@ -528,6 +529,53 @@ public void testVSessionOptions() {
}
}

/**
* Verifies the {@link OracleR2dbcOptions#TIMEZONE_AS_REGION} option
*/
@Test
public void testTimezoneAsRegion() {
// Set the timezone to that of Warsaw. When JDBC opens a connection, it will
// read the Warsaw timezone from TimeZone.getDefault().
TimeZone warsawTimeZone = TimeZone.getTimeZone("Europe/Warsaw");
TimeZone timeZoneRestored = TimeZone.getDefault();
TimeZone.setDefault(warsawTimeZone);
try {

// Configure the JDBC connection property with a URL parameter. This has
// JDBC express the session timezone as an offset of UTC (+02:00), rather
// than a name (Europe/Warsaw).
Connection connection = awaitOne(ConnectionFactories.get(
ConnectionFactoryOptions.parse(format(
"r2dbc:oracle://%s:%d/%s?oracle.jdbc.timezoneAsRegion=false",
host(), port(), serviceName()))
.mutate()
.option(USER, user())
.option(PASSWORD, password())
.build())
.create());
try {

// Query the session timezone, and expect it to be expressed as an
// offset, rather than a name.
assertEquals(
ZonedDateTime.now(warsawTimeZone.toZoneId())
.getOffset()
.toString(),
awaitOne(awaitOne(connection.createStatement(
"SELECT sessionTimeZone FROM sys.dual")
.execute())
.map(row ->
row.get(0, String.class))));
}
finally {
tryAwaitNone(connection.close());
}
}
finally {
TimeZone.setDefault(timeZoneRestored);
}
}

/**
* Verifies that an attempt to connect with a {@code listeningChannel}
* results in an {@link R2dbcTimeoutException}.
Expand Down