Skip to content

Commit

Permalink
JDBC-603 Implement Firebird 4 set_bind support
Browse files Browse the repository at this point in the history
+ cleanup removed/remapped Firebird 4 DPB items
  • Loading branch information
mrotteveel committed Dec 14, 2019
1 parent e99d286 commit a82288e
Show file tree
Hide file tree
Showing 6 changed files with 100 additions and 56 deletions.
128 changes: 88 additions & 40 deletions src/documentation/release_notes.md
Expand Up @@ -252,7 +252,12 @@ The following has been changed or fixed since Jaybird 3.0.9:
properties in the JDBC url. ([JDBC-604](http://tracker.firebirdsql.org/browse/JDBC-604)) \
This introduce a minor incompatibility, see also
[URL encoding in query part of JDBC URL]. \
This feature was backported to Jaybird 3.0.9.
- New feature: Firebird 4 data type bind configuration support ([JDBC-603](http://tracker.firebirdsql.org/browse/JDBC-603)) \
This change also removes the `timeZoneBind` and `decfloatBind` connection
properties introduced in Jaybird 3.0.6 as the corresponding DPB items were
removed from Firebird 4. This feature requires
Firebird 4 beta 2 or snapshot Firebird 4.0.0.1683 or higher. \
See also [Limited support for new Firebird 4 data types]. \

### Known issues in Jaybird 3.0.9

Expand Down Expand Up @@ -626,50 +631,93 @@ plugins

Jaybird 3 does not support the new Firebird 4 data types `TIME WITH TIME ZONE`,
`TIMESTAMP WITH TIME ZONE`, `DECFLOAT` and `NUMERIC`/`DECIMAL` with precision
greater than 18. As an accommodation, Jaybird 3.0.6 has limited support for the
following Firebird connection properties:

- `timeZoneBind` (alias: `time_zone_bind`) with valid values:
- `native` (default) - produces the normal `WITH TIME ZONE` values (not
supported by Jaybird 3)
- `legacy` - converts the `WITH TIME ZONE` values to the equivalent `WITHOUT
TIME ZONE` values using the session time zone
greater than 18. As an accommodation, Jaybird 3.0.9 and higher has limited
support for the following Firebird connection properties:

- `dataTypeBind` (alias: `set_bind`) which accepts a list of semicolon-separated
bind definitions. See details below in [Notes on dataTypeBind].
- `sessionTimeZone` (alias: `session_time_zone`) configures the **server-side**
session time zone used for conversion of `WITH TIME ZONE` to `WITHOUT TIME
ZONE` and values generated by `CURRENT_TIME`, `LOCALTIME`, etc.
Valid values are Firebird time zone names or offsets. See also the Firebird 4
documentation. For important caveats, see [Notes on sessionTimeZone].
- `decfloatBind` (alias: `decfloat_bind`) with valid values:
- `native` (default) - produces the normal `DECFLOAT` values (not supported by
Jaybird 3)
- `char`/`character` - converts the `DECFLOAT` values to an equivalent string
using scientific notation.
- `double precision` - converts the `DECFLOAT` values to an equivalent `double
precision`. This may lose precision, and this option cannot support the full
range of values of `DECFLOAT(34)` and can result in errors on overflow.
- `bigint, n` where `n` is the target scale converts the value to a
`NUMERIC(18,n)`. This option cannot support the full range of values of
`DECFLOAT` and can result in errors on overflow.

**Important**: These features requires Firebird 4 beta 2 or higher (or a snapshot
build version 4.0.0.1481 or later). They will be ignored in earlier builds as the
necessary database parameter buffer items do not exist in earlier versions.

These properties can be used as connection properties with `DriverManager`. For
Jaybird data sources, the properties must be set using `setNonStandardProperty`
as corresponding setters have not been defined.

These properties can be used as connection properties with `DriverManager`. When
specified in a JDBC URL, make sure to encode the `;` in a `dataTypeBind` value
with `%3B`. For Jaybird data sources, the properties must be set using
`setNonStandardProperty` as corresponding setters have not been defined.

To be able to use `WITH TIME ZONE` types with Jaybird 3, you must use
`timeZoneBind=legacy`. Setting `sessionTimeZone` is optional, see also
[Notes on sessionTimeZone].
`dataTypeBind=time with time zone to legacy;timestamp with time zone to legacy`.
Setting `sessionTimeZone` is optional, see also [Notes on sessionTimeZone].

We recommend using `varchar` for `decfloat` and `numeric/decimal(38)` (as shown
in the next section) because that will allow you to use to full range of values
without overflow or loss of precision through `get/setBigDecimal`. Alternatively,
you can use `double precision`, although this may result in overflow for very
large `decfloat(34)` values.

**Important**: These features requires Firebird 4 beta 2 or higher (or a snapshot
build version 4.0.0.1683 or later). It will be ignored in builds before 1481 as
the necessary database parameter buffer item does not exist, and it will raise
an error in versions between 1481 and 1682 as there the DPB item points to the
removed DPB item `isc_time_zone_bind`.

#### Notes on dataTypeBind ####

Firebird 4 (build 4.0.0.1683 or later) introduced the `SET BIND` statement and
`isc_dpb_set_bind` DPB item. This allows you to define data type conversion
rules for compatibility or ease of processing data.

In Jaybird this feature is exposed as connection property `dataTypeBind` (alias
`set_bind`). The value of this connection property is a semicolon-separated list
of data type bind definitions.

A data type bind definition is of the form `<from-type> TO <to-type>`. A
definition is the same as the second half of a `SET BIND` statement after the
`OF`. See the Firebird documentation of `SET BIND` for more information. Invalid
values or impossible mappings will result in an error on connect.

When using the `dataTypeBind` connection property in a JDBC URL, the semicolons
of the list need to be encoded as `%3B`, as semicolons in the JDBC URL are
an alternative to `&` as the separator between properties.

For example:

```
String jdbcUrl = "jdbc:firebirdsql://localhost/database?charSet=utf-8"
+ "&dataTypeBind=decfloat to varchar%3Btimestamp with time zone to legacy
```

When the property is set through a `Properties` object or a `DataSource`
configuration, encoding the semicolon is not necessary or possible, and will
result in errors.

For example:

```
Properties props = new Properties();
props.setProperty("dataTypeBind",
"decfloat to varchar;timestamp with time zone to legacy"
```

For `DECFLOAT`, we recommend either `decfloatBind=char`
or `decfloatBind=double precision`. Option `char` has our preference as it is
able to support the full range of values of the `DECFLOAT` types.
Values set through this connection property will be the session default
configuration, which means that they are retained (or reverted to) when
executing `ALTER SESSION RESET`.

This feature replaces the connection properties `timeZoneBind` and
`decfloatBind` from earlier Jaybird 4 versions. The `timeZoneBind` and
`decfloatBind` properties are no longer supported.

To remap the Firebird 4 types, you will need to specify:

```
"decfloat to varchar;numeric(38) to varchar;decimal(38) to varchar" +
";time with time zone to legacy;timestamp with time zone to legacy"
```

Unfortunately there is no option for the extended precision numeric types, other
than explicitly casting to `DECFLOAT(34)` and relying on the conversion provided
by property `decfloatBind`.
Instead of `legacy`, you can also explicitly specify `time` and `timestamp`
respectively.

#### Notes on sessionTimeZone ####

Expand All @@ -681,7 +729,7 @@ types.

When Jaybird 3 and Firebird 4 are hosted on machines with different time zone
settings, setting `sessionTimeZone` can result in changes in current time values
(eg for `CURRENT_TIME`) as Firebird will base these values on the session time
(eg for `CURRENT_TIME`) as Firebird 4 will base these values on the session time
zone.

Setting `sessionTimeZone` to the JVM default time zone will yield the best
Expand All @@ -692,9 +740,9 @@ versions of Jaybird.
When setting `sessionTimeZone`, we recommend to use the long-form time zone
names (eg `Europe/Amsterdam`) and not the short-form ids (eg `CET`).

We recommend not setting this property, or setting it to the default JVM time
zone. If you set it to a different time zone, then we recommend that you do not
use the legacy `java.sql.Time/Timestamp/Date` types, but instead use
In general, we recommend not setting this property, or setting it to the default
JVM time zone. If you set it to a different time zone, then we recommend that
you do not use the legacy `java.sql.Time/Timestamp/Date` types, but instead use
`java.time.LocalTime/LocalDateTime/LocalDate`.

New low-level implementation
Expand Down
7 changes: 3 additions & 4 deletions src/main/org/firebirdsql/gds/ISCConstants.java
Expand Up @@ -166,10 +166,9 @@ public interface ISCConstants {
// Firebird 4 constants
int isc_dpb_session_time_zone = 91;
int isc_dpb_set_db_replica = 92;
int isc_dpb_time_zone_bind = 93;
int isc_dpb_decfloat_bind = 94;
int isc_dpb_decfloat_round = 95;
int isc_dpb_decfloat_traps = 96;
int isc_dpb_set_bind = 93;
int isc_dpb_decfloat_round = 94;
int isc_dpb_decfloat_traps = 95;

/*
* Driver-specific DPB params that will be removed before sending them
Expand Down
3 changes: 1 addition & 2 deletions src/main/org/firebirdsql/gds/ng/FbConnectionProperties.java
Expand Up @@ -234,8 +234,7 @@ public void fromDpb(DatabaseParameterBuffer dpb) throws SQLException {
case isc_dpb_process_id:
case isc_dpb_process_name:
case isc_dpb_session_time_zone:
case isc_dpb_time_zone_bind:
case isc_dpb_decfloat_bind:
case isc_dpb_set_bind:
case isc_dpb_decfloat_round:
case isc_dpb_decfloat_traps:
parameter.copyTo(getExtraDatabaseParameters(), null);
Expand Down
3 changes: 1 addition & 2 deletions src/resources/driver_property_info.properties
Expand Up @@ -47,6 +47,5 @@ dbCryptConfig isc_dpb_db_crypt_config # FB3+ database encr
ignoreProcedureType isc_dpb_ignore_procedure_type # Ignore procedure type from metadata (defaults to executable stored procedure)

sessionTimeZone isc_dpb_session_time_zone # FB4+ session time zone (sets server session time zone only!)
timeZoneBind isc_dpb_time_zone_bind # FB4+ time zone bind setting: native (default) or legacy (Firebird converts with time zone types to without time zone types)

decfloatBind isc_dpb_decfloat_bind # FB4+ decloat bind setting: native (default), char or character, double precision, bigint (with optional comma-separated scale)
dataTypeBind isc_dpb_set_bind # FB4+ data type bind settings: semicolon separated list of <from-type> TO <to-type> (see SET BIND documentation in Firebird 4)
3 changes: 1 addition & 2 deletions src/resources/isc_dpb_types.properties
Expand Up @@ -20,8 +20,7 @@ isc_dpb_sql_dialect byte
isc_dpb_process_id int
isc_dpb_process_name string
isc_dpb_session_time_zone string
isc_dpb_time_zone_bind string
isc_dpb_decfloat_bind string
isc_dpb_set_bind string
isc_dpb_decfloat_round string
isc_dpb_decfloat_traps string

Expand Down
12 changes: 6 additions & 6 deletions src/test/org/firebirdsql/jdbc/Firebird4DataTypeTest.java
Expand Up @@ -52,7 +52,7 @@ public static void requireFirebird4() {
@Test
public void testTimeZoneBind() throws Exception {
Properties props = getDefaultPropertiesForConnection();
props.setProperty("timeZoneBind", "legacy");
props.setProperty("dataTypeBind", "timestamp with time zone to legacy");
// Ensure consistent value
props.setProperty("sessionTimeZone", TimeZone.getDefault().getID());

Expand All @@ -75,9 +75,9 @@ public void testTimeZoneBind() throws Exception {
@Test
public void testDecfloatBindCharOnDecfloat() throws Exception {
Properties props = getDefaultPropertiesForConnection();
props.setProperty("decfloatBind", "char");
props.setProperty("dataTypeBind", "decfloat to char");

final String testValue = "9.999999999999999999999999999999999E+6144";
final String testValue = "-9.999999999999999999999999999999999E+6144";
final BigDecimal expectedBigDecimal = new BigDecimal(testValue);

try (Connection connection = DriverManager.getConnection(getUrl(), props);
Expand All @@ -94,7 +94,7 @@ public void testDecfloatBindCharOnDecfloat() throws Exception {
@Test
public void testDecfloatBindDoublePrecisionOnDecfloat() throws Exception {
Properties props = getDefaultPropertiesForConnection();
props.setProperty("decfloatBind", "double precision");
props.setProperty("dataTypeBind", "decfloat to double precision");

// Max value representable; larger DECFLOAT values will yield an error with default trap settings
final String testValue = String.valueOf(Double.MAX_VALUE);
Expand All @@ -110,9 +110,9 @@ public void testDecfloatBindDoublePrecisionOnDecfloat() throws Exception {
}

@Test
public void testDecfloatBindBigint3OnDecfloat() throws Exception {
public void testDecfloatBindNumeric18_3OnDecfloat() throws Exception {
Properties props = getDefaultPropertiesForConnection();
props.setProperty("decfloatBind", "bigint,3");
props.setProperty("dataTypeBind", "decfloat to numeric(18,3)");

// Max value representable; larger DECFLOAT values will yield an error with default trap settings
BigDecimal testValue = BigDecimal.valueOf(Long.MAX_VALUE, 3);
Expand Down

0 comments on commit a82288e

Please sign in to comment.