pgjdbc/pgjdbc GitHub issues and pull requests (mirror)
help / color / mirror / Atom feedFrom: vlsi (@vlsi) <[email protected]>
To: pgjdbc/pgjdbc <[email protected]>
Subject: [pgjdbc/pgjdbc] PR #4195: feat(jdbc): add direction-aware binarySend/binaryReceive properties
Date: Wed, 17 Jun 2026 20:24:44 +0000
Message-ID: <[email protected]> (raw)
## Why
The existing `binaryTransfer`, `binaryTransferEnable`, and `binaryTransferDisable` properties cannot tell the **send** direction (parameters sent to the server) apart from the **receive** direction (results read back), even though the driver already keeps the two OID sets separate internally. The two directions have a different nature — for example, the driver excludes `date` from binary on send to preserve sub-day precision for timestamp targets, but that has no bearing on receive — so a single knob cannot express what users actually need.
Splitting the configuration by direction also makes [#3062](https://github.com/pgjdbc/pgjdbc/pull/3062) easier to configure, since that work needs per-direction control over which types travel in binary.
This change can land either before #3062 or as part of #3062.
## What
Two new connection properties with an `oid:mode` format, where `oid` is a type name or OID number:
- `binarySend` — modes `auto`, `force`, `disable`.
- `binaryReceive` — modes `auto`, `disable`.
Semantics per type and direction:
- `force` (send only) adds the type to the binary set; best-effort, same risk as `binaryTransferEnable` (the server may reject binary for a type it cannot send that way, and for temporal types it can change the stored value).
- `disable` keeps the type in text and stops the legacy properties from re-enabling it.
- `auto` resets the type to the driver's built-in default for that direction. It overrides the legacy properties too — including `binaryTransferDisable` — so the type also leaves the disabled set that `addDataType` and other later opt-ins consult.
Precedence: a per-type mode here wins over the legacy `binaryTransfer` / `binaryTransferEnable` / `binaryTransferDisable` for that type and direction. The legacy properties are unchanged and keep working.
Scope for this version: modes apply to top-level types only; `auto` may change between driver versions; recursion into composite and array element types can follow later. No codec-layer or `typsend` work is involved — this is a thin wrapper over the per-direction OID sets the driver already maintains.
Also adds matching `BaseDataSource` getters/setters and documents the properties in `README.md` and `docs/content/documentation/use.md`.
## How to verify
```
./gradlew --quiet :postgresql:classes :postgresql:style
./gradlew --quiet :postgresql:test --tests org.postgresql.test.jdbc2.BinaryDirectionPropertiesTest
```
`BinaryDirectionPropertiesTest` covers `force`/`disable`/`auto`, precedence over the legacy properties in both directions, OID-by-name and OID-by-number, and rejection of invalid modes/syntax. It needs a live PostgreSQL (`localhost:5432`, database/user `test`).
🤖 Generated with [Claude Code](https://claude.com/claude-code)
diff --git a/README.md b/README.md
index b7ebf9954c..04a2001f49 100644
--- a/README.md
+++ b/README.md
@@ -117,6 +117,8 @@ In addition to the standard connection parameters the driver supports a number o
| binaryTransfer | Boolean | true | Enable binary transfer for supported built-in types if possible. Setting this to false disables any binary transfer unless it's individually activated for each type with `binaryTransferEnable`. Whether it is possible to use binary transfer at all depends on server side prepared statements (see `prepareThreshold` ). |
| binaryTransferEnable | String | "" | Comma separated list of types to enable binary transfer. Either OID numbers or names. |
| binaryTransferDisable | String | "" | Comma separated list of types to disable binary transfer. Either OID numbers or names. Overrides values in the driver default set and values set with binaryTransferEnable. |
+| binarySend | String | "" | Per-type binary format for parameters sent to the server. Comma separated `oid:mode` entries, where `oid` is a type name or OID number and `mode` is `auto`, `force`, or `disable`. Every mode overrides `binaryTransfer`, `binaryTransferEnable`, and `binaryTransferDisable` for that type when sending. `auto` resets the type to the driver's built-in default, which may change between versions; `disable` keeps the type in text; `force` is best-effort and may fail at the server or change the stored value for temporal types. |
+| binaryReceive | String | "" | Per-type binary format for results received from the server. Comma separated `oid:mode` entries, where `oid` is a type name or OID number and `mode` is `auto` or `disable`. Every mode overrides `binaryTransfer`, `binaryTransferEnable`, and `binaryTransferDisable` for that type when receiving. `auto` resets the type to the driver's built-in default, which may change between versions; `disable` keeps the type in text. |
| prepareThreshold | Integer | 5 | Determine the number of `PreparedStatement` executions required before switching over to use server side prepared statements. The default is five, meaning start using server side prepared statements on the fifth execution of the same `PreparedStatement` object. A value of -1 activates server side prepared statements and forces binary transfer for enabled types (see `binaryTransfer` ). |
| preparedStatementCacheQueries | Integer | 256 | Specifies the maximum number of entries in per-connection cache of prepared statements. A value of 0 disables the cache. |
| preparedStatementCacheSizeMiB | Integer | 5 | Specifies the maximum size (in megabytes) of a per-connection prepared statement cache. A value of 0 disables the cache. |
diff --git a/docs/content/documentation/use.md b/docs/content/documentation/use.md
index 315dff6672..43fd76aefe 100644
--- a/docs/content/documentation/use.md
+++ b/docs/content/documentation/use.md
@@ -212,6 +212,23 @@ Comma separated list of types to enable binary transfer. Either OID numbers or n
Comma separated list of types to disable binary transfer. Either OID numbers or names.
Overrides values in the driver default set and values set with binaryTransferEnable.
+* **`binarySend (`*String*`)`** *Default `empty string`*\
+Per-type binary format for parameters sent to the server.
+Comma separated list of `oid:mode` entries, where `oid` is a type name or OID number and `mode` is `auto`, `force`, or `disable`.
+Every mode overrides `binaryTransfer`, `binaryTransferEnable`, and `binaryTransferDisable` for that type when sending.
+`auto` resets the type to the driver's built-in default, which may change between driver versions.
+`disable` keeps the type in text.
+`force` is best-effort: like `binaryTransferEnable`, it may request the binary format for a type the server cannot send in binary, which then fails at the server; for temporal types it can also change the value the server stores (for example, binary `date` loses the sub-day precision the text path keeps for timestamp targets).
+In this version, modes apply to top-level types only; recursion into composite and array element types may follow later, and only widens what `force`/`disable` reach.
+
+* **`binaryReceive (`*String*`)`** *Default `empty string`*\
+Per-type binary format for results received from the server.
+Comma separated list of `oid:mode` entries, where `oid` is a type name or OID number and `mode` is `auto` or `disable`.
+Every mode overrides `binaryTransfer`, `binaryTransferEnable`, and `binaryTransferDisable` for that type when receiving.
+`auto` resets the type to the driver's built-in default, which may change between driver versions.
+`disable` keeps the type in text.
+In this version, modes apply to top-level types only; recursion into composite and array element types may follow later, and only widens what `disable` reaches.
+
* **`databaseMetadataCacheFields (`*int*`)`** *Default `65536`*\
Specifies the maximum number of fields to be cached per connection.
A value of `0` disables the cache.
diff --git a/pgjdbc/src/main/java/org/postgresql/PGProperty.java b/pgjdbc/src/main/java/org/postgresql/PGProperty.java
index bb5a9e51b6..98029fa96c 100644
--- a/pgjdbc/src/main/java/org/postgresql/PGProperty.java
+++ b/pgjdbc/src/main/java/org/postgresql/PGProperty.java
@@ -108,6 +108,38 @@ public enum PGProperty {
false,
new String[]{"always", "never", "conservative"}),
+ /**
+ * Per-type binary format for results received from the server.
+ */
+ BINARY_RECEIVE(
+ "binaryReceive",
+ "",
+ "Per-type binary format for results received from the server. "
+ + "Comma separated list of `oid:mode` entries, where `oid` is a type name or OID number "
+ + "and `mode` is `auto` or `disable`. "
+ + "Every mode overrides `binaryTransfer`, `binaryTransferEnable`, and "
+ + "`binaryTransferDisable` for that type when receiving. `auto` resets the type to the "
+ + "driver's built-in default, which may change between driver versions. "
+ + "Modes apply to top-level types only."),
+
+ /**
+ * Per-type binary format for parameters sent to the server.
+ */
+ BINARY_SEND(
+ "binarySend",
+ "",
+ "Per-type binary format for parameters sent to the server. "
+ + "Comma separated list of `oid:mode` entries, where `oid` is a type name or OID number "
+ + "and `mode` is `auto`, `force`, or `disable`. "
+ + "Every mode overrides `binaryTransfer`, `binaryTransferEnable`, and "
+ + "`binaryTransferDisable` for that type when sending. `auto` resets the type to the "
+ + "driver's built-in default, which may change between driver versions. "
+ + "`force` is best-effort: like `binaryTransferEnable`, it may request the binary format "
+ + "for a type the server cannot send in binary, which then fails at the server, and for "
+ + "temporal types it can change the value the server stores (for example, binary `date` "
+ + "loses the sub-day precision that the text path keeps for timestamp targets). "
+ + "Modes apply to top-level types only."),
+
/**
* Use binary format for sending and receiving data if possible.
*/
diff --git a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
index 4a5dd4ce14..c17a1734a0 100644
--- a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
+++ b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
@@ -966,6 +966,40 @@ public String getBinaryTransferDisable() {
return castNonNull(PGProperty.BINARY_TRANSFER_DISABLE.getOrDefault(properties));
}
+ /**
+ * @param spec per-type binary format for parameters sent to the server, as {@code oid:mode}
+ * entries
+ * @see PGProperty#BINARY_SEND
+ */
+ public void setBinarySend(@Nullable String spec) {
+ PGProperty.BINARY_SEND.set(properties, spec);
+ }
+
+ /**
+ * @return per-type binary format for parameters sent to the server
+ * @see PGProperty#BINARY_SEND
+ */
+ public String getBinarySend() {
+ return castNonNull(PGProperty.BINARY_SEND.getOrDefault(properties));
+ }
+
+ /**
+ * @param spec per-type binary format for results received from the server, as {@code oid:mode}
+ * entries
+ * @see PGProperty#BINARY_RECEIVE
+ */
+ public void setBinaryReceive(@Nullable String spec) {
+ PGProperty.BINARY_RECEIVE.set(properties, spec);
+ }
+
+ /**
+ * @return per-type binary format for results received from the server
+ * @see PGProperty#BINARY_RECEIVE
+ */
+ public String getBinaryReceive() {
+ return castNonNull(PGProperty.BINARY_RECEIVE.getOrDefault(properties));
+ }
+
/**
* @return string type
* @see PGProperty#STRING_TYPE
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
index c6351adacc..66adb313b2 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
@@ -197,9 +197,12 @@ private enum ReadOnlyBehavior {
protected boolean forcebinary;
/**
- * Oids for which binary transfer should be disabled.
+ * Oids kept in text on each direction: the union of the legacy {@code binaryTransferDisable} set
+ * and the {@code disable} entries of {@code binarySend}/{@code binaryReceive}. Later opt-ins such
+ * as {@code addDataType} honour these so a disabled type is not silently re-enabled.
*/
- private final Set<? extends Integer> binaryDisabledOids;
+ private final Set<Integer> binarySendDisabledOids;
+ private final Set<Integer> binaryReceiveDisabledOids;
private int rsHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT;
private int savepointId;
@@ -313,26 +316,37 @@ public PgConnection(HostSpec[] hostSpecs,
// "cached plan must not change result type" to callers.
queryExecutor.setFlushCacheOnDdl(PGProperty.FLUSH_CACHE_ON_DDL.getBoolean(info));
- // get oids that support binary transfer
- Set<Integer> binaryOids = getBinaryEnabledOids(info);
- // get oids that should be disabled from transfer
- binaryDisabledOids = getBinaryDisabledOids(info);
- // if there are any, remove them from the enabled ones
- if (!binaryDisabledOids.isEmpty()) {
- binaryOids.removeAll(binaryDisabledOids);
- }
-
- // split for receive and send for better control
- Set<Integer> useBinarySendForOids = new HashSet<>(binaryOids);
+ // Step 1: parse the legacy binaryTransfer* properties and lay them out into the
+ // per-direction send/receive sets (both identical at this point).
+ Set<Integer> legacyBinaryOids = getBinaryEnabledOids(info);
+ Set<? extends Integer> legacyDisabledOids = getBinaryDisabledOids(info);
+ legacyBinaryOids.removeAll(legacyDisabledOids);
+ Set<Integer> useBinarySendForOids = new HashSet<>(legacyBinaryOids);
+ Set<Integer> useBinaryReceiveForOids = new HashSet<>(legacyBinaryOids);
- Set<Integer> useBinaryReceiveForOids = new HashSet<>(binaryOids);
-
- /*
- * Does not pass unit tests because unit tests expect setDate to have millisecond accuracy
- * whereas the binary transfer only supports date accuracy.
- */
+ // Step 2: the driver never sends DATE in binary by default, because the text path keeps the
+ // sub-day precision that setDate needs for timestamp targets. Model it as date=disable on send.
useBinarySendForOids.remove(Oid.DATE);
+ // The built-in defaults that binarySend/binaryReceive `auto` resets a type to. The send
+ // default follows the same DATE rule. Computed here, so the parser stays free of type hardcodes.
+ Set<Integer> sendDefaultOids = new HashSet<>(SUPPORTED_BINARY_OIDS);
+ sendDefaultOids.remove(Oid.DATE);
+ Set<Integer> receiveDefaultOids = SUPPORTED_BINARY_OIDS;
+
+ // Step 3: parse the new binarySend/binaryReceive properties and update the per-direction sets.
+ // These override the legacy properties for the types they mention; `auto` resets to the
+ // supplied default set. The disabled sets start from the legacy binaryTransferDisable set so
+ // that later opt-ins such as addDataType keep honouring every disabled type.
+ Set<Integer> sendDisabledOids = new HashSet<>(legacyDisabledOids);
+ Set<Integer> receiveDisabledOids = new HashSet<>(legacyDisabledOids);
+ applyBinaryDirectionOverrides(info, PGProperty.BINARY_SEND, useBinarySendForOids,
+ sendDefaultOids, true, sendDisabledOids);
+ applyBinaryDirectionOverrides(info, PGProperty.BINARY_RECEIVE, useBinaryReceiveForOids,
+ receiveDefaultOids, false, receiveDisabledOids);
+ binarySendDisabledOids = sendDisabledOids;
+ binaryReceiveDisabledOids = receiveDisabledOids;
+
queryExecutor.setBinaryReceiveOids(useBinaryReceiveForOids);
queryExecutor.setBinarySendOids(useBinarySendForOids);
@@ -518,6 +532,76 @@ private static Set<? extends Integer> getOidSet(String oidList) throws PSQLExcep
return oids;
}
+ /**
+ * Applies a per-direction, per-type binary override property ({@code binarySend} or
+ * {@code binaryReceive}) on top of the set already computed from the legacy
+ * {@code binaryTransfer*} properties. Every mode overrides the legacy properties for that type
+ * and direction: {@code force} adds the OID, {@code disable} removes it, and {@code auto} resets
+ * the OID to {@code defaultOids} (the driver's built-in default for the direction, which may
+ * change between driver versions). The parser itself never special-cases a type; any default
+ * such as the send-side {@code DATE} exclusion is baked into {@code defaultOids} by the caller.
+ *
+ * @param info connection properties
+ * @param property {@link PGProperty#BINARY_SEND} or {@link PGProperty#BINARY_RECEIVE}
+ * @param oids set for the given direction, mutated in place
+ * @param defaultOids the driver's built-in default set for this direction, used by {@code auto}
+ * @param isSend whether this is the send direction ({@code force} is accepted for send only)
+ * @param disabledOut collects the OIDs set to {@code disable}, so later opt-ins such as
+ * {@code addDataType} keep honouring them, like the legacy {@code binaryTransferDisable}
+ * @throws PSQLException if an OID, a mode, or the {@code oid:mode} syntax is invalid,
+ * or if the same OID appears more than once
+ */
+ private static void applyBinaryDirectionOverrides(Properties info, PGProperty property,
+ Set<Integer> oids, Set<Integer> defaultOids, boolean isSend, Set<Integer> disabledOut)
+ throws PSQLException {
+ String spec = property.getOrDefault(info);
+ if (spec == null || spec.isEmpty()) {
+ return;
+ }
+ Set<Integer> seen = new HashSet<>();
+ StringTokenizer tokenizer = new StringTokenizer(spec, ",");
+ while (tokenizer.hasMoreTokens()) {
+ String entry = tokenizer.nextToken().trim();
+ int colon = entry.indexOf(':');
+ if (colon < 0) {
+ throw new PSQLException(
+ GT.tr("Invalid value \"{0}\" for property {1}: expected oid:mode entries.",
+ entry, property.getName()),
+ PSQLState.INVALID_PARAMETER_VALUE);
+ }
+ int oid = Oid.valueOf(entry.substring(0, colon).trim());
+ String mode = entry.substring(colon + 1).trim();
+ if (!seen.add(oid)) {
+ throw new PSQLException(
+ GT.tr("Duplicate type \"{0}\" in property {1}.",
+ Oid.toString(oid), property.getName()),
+ PSQLState.INVALID_PARAMETER_VALUE);
+ }
+ if ("auto".equalsIgnoreCase(mode)) {
+ // Reset to the driver's built-in default for this direction, overriding any legacy
+ // binaryTransfer* setting — including binaryTransferDisable, so the type must also
+ // leave the disabled set (consulted by addDataType and other later opt-ins).
+ disabledOut.remove(oid);
+ if (defaultOids.contains(oid)) {
+ oids.add(oid);
+ } else {
+ oids.remove(oid);
+ }
+ } else if ("disable".equalsIgnoreCase(mode)) {
+ oids.remove(oid);
+ disabledOut.add(oid);
+ } else if (isSend && "force".equalsIgnoreCase(mode)) {
+ oids.add(oid);
+ } else {
+ throw new PSQLException(
+ GT.tr("Invalid mode \"{0}\" for type \"{1}\" in property {2}. Allowed modes are {3}.",
+ mode, Oid.toString(oid), property.getName(),
+ isSend ? "auto, force, disable" : "auto, disable"),
+ PSQLState.INVALID_PARAMETER_VALUE);
+ }
+ }
+ }
+
private static String oidsToString(Set<Integer> oids) {
StringBuilder sb = new StringBuilder();
for (Integer oid : oids) {
@@ -840,11 +924,15 @@ public void addDataType(String type, Class<? extends PGobject> klass) throws SQL
if (PGBinaryObject.class.isAssignableFrom(klass) && getPreferQueryMode() != PreferQueryMode.SIMPLE) {
// try to get an oid for this type (will return 0 if the type does not exist in the database)
int oid = typeCache.getPGType(type);
- // check if oid is there and if it is not disabled for binary transfer
- if (oid > 0 && !binaryDisabledOids.contains(oid)) {
- // allow using binary transfer for receiving and sending of this type
- queryExecutor.addBinaryReceiveOid(oid);
- queryExecutor.addBinarySendOid(oid);
+ // check if oid is there and honour the per-direction disabled sets (which already fold in the
+ // legacy binaryTransferDisable set) so a disabled type is not silently re-enabled here
+ if (oid > 0) {
+ if (!binaryReceiveDisabledOids.contains(oid)) {
+ queryExecutor.addBinaryReceiveOid(oid);
+ }
+ if (!binarySendDisabledOids.contains(oid)) {
+ queryExecutor.addBinarySendOid(oid);
+ }
}
}
}
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/BinaryDirectionPropertiesTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/BinaryDirectionPropertiesTest.java
new file mode 100644
index 0000000000..3a6c0a337b
--- /dev/null
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/BinaryDirectionPropertiesTest.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (c) 2024, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc2;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.postgresql.PGProperty;
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.Oid;
+import org.postgresql.core.QueryExecutor;
+import org.postgresql.test.TestUtil;
+import org.postgresql.util.PSQLState;
+
+import org.junit.jupiter.api.Test;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.Properties;
+
+/**
+ * Tests for the per-direction, per-type {@code binarySend} and {@code binaryReceive} properties,
+ * including their precedence over the legacy {@code binaryTransfer*} properties.
+ */
+class BinaryDirectionPropertiesTest {
+
+ private static QueryExecutor executorOf(Connection con) throws SQLException {
+ return con.unwrap(BaseConnection.class).getQueryExecutor();
+ }
+
+ @Test
+ void forceAddsSendBinaryForType() throws SQLException {
+ Properties props = new Properties();
+ // Start from nothing so the only send-binary type is the one we force.
+ PGProperty.BINARY_TRANSFER.set(props, false);
+ PGProperty.BINARY_SEND.set(props, "int4:force");
+ try (Connection con = TestUtil.openDB(props)) {
+ QueryExecutor executor = executorOf(con);
+ assertTrue(executor.useBinaryForSend(Oid.INT4), "int4:force should enable binary send");
+ assertFalse(executor.useBinaryForReceive(Oid.INT4),
+ "binaryReceive untouched and binaryTransfer=false, so receive stays text");
+ }
+ }
+
+ @Test
+ void forceAcceptsNumericOid() throws SQLException {
+ Properties props = new Properties();
+ PGProperty.BINARY_TRANSFER.set(props, false);
+ // 23 is the OID of int4.
+ PGProperty.BINARY_SEND.set(props, "23:force");
+ try (Connection con = TestUtil.openDB(props)) {
+ assertTrue(executorOf(con).useBinaryForSend(Oid.INT4),
+ "numeric OID 23:force should enable binary send for int4");
+ }
+ }
+
+ @Test
+ void disableRemovesReceiveBinaryFromDefault() throws SQLException {
+ Properties props = new Properties();
+ // int4 is a default binary type; disable it on receive only.
+ PGProperty.BINARY_RECEIVE.set(props, "int4:disable");
+ try (Connection con = TestUtil.openDB(props)) {
+ QueryExecutor executor = executorOf(con);
+ assertFalse(executor.useBinaryForReceive(Oid.INT4),
+ "int4:disable should turn off binary receive");
+ assertTrue(executor.useBinaryForSend(Oid.INT4),
+ "send direction is untouched and stays at the default");
+ }
+ }
+
+ @Test
+ void autoKeepsDriverDefault() throws SQLException {
+ Properties props = new Properties();
+ PGProperty.BINARY_SEND.set(props, "int4:auto");
+ PGProperty.BINARY_RECEIVE.set(props, "int4:auto");
+ try (Connection con = TestUtil.openDB(props)) {
+ QueryExecutor executor = executorOf(con);
+ assertTrue(executor.useBinaryForSend(Oid.INT4), "auto keeps the default (binary) for send");
+ assertTrue(executor.useBinaryForReceive(Oid.INT4),
+ "auto keeps the default (binary) for receive");
+ }
+ }
+
+ @Test
+ void autoResetsLegacyEnableToDefault() throws SQLException {
+ Properties props = new Properties();
+ // bool is not a default binary type, so the legacy enable is what turns send on.
+ PGProperty.BINARY_TRANSFER_ENABLE.set(props, "bool");
+ PGProperty.BINARY_SEND.set(props, "bool:auto");
+ try (Connection con = TestUtil.openDB(props)) {
+ QueryExecutor executor = executorOf(con);
+ assertFalse(executor.useBinaryForSend(Oid.BOOL),
+ "auto should override binaryTransferEnable and reset bool to its default (text) for send");
+ assertTrue(executor.useBinaryForReceive(Oid.BOOL),
+ "receive has no override, so binaryTransferEnable=bool still applies");
+ }
+ }
+
+ @Test
+ void autoResetsLegacyDisableToDefault() throws SQLException {
+ Properties props = new Properties();
+ // int4 is a default binary type; the legacy disable turns it off.
+ PGProperty.BINARY_TRANSFER_DISABLE.set(props, "int4");
+ PGProperty.BINARY_RECEIVE.set(props, "int4:auto");
+ try (Connection con = TestUtil.openDB(props)) {
+ QueryExecutor executor = executorOf(con);
+ assertTrue(executor.useBinaryForReceive(Oid.INT4),
+ "auto should override binaryTransferDisable and reset int4 to its default (binary)");
+ assertFalse(executor.useBinaryForSend(Oid.INT4),
+ "send has no override, so binaryTransferDisable=int4 still applies");
+ }
+ }
+
+ @Test
+ void perDirectionForceWinsOverLegacyDisable() throws SQLException {
+ Properties props = new Properties();
+ PGProperty.BINARY_TRANSFER_DISABLE.set(props, "int4");
+ PGProperty.BINARY_SEND.set(props, "int4:force");
+ try (Connection con = TestUtil.openDB(props)) {
+ QueryExecutor executor = executorOf(con);
+ assertTrue(executor.useBinaryForSend(Oid.INT4),
+ "binarySend=int4:force should override binaryTransferDisable=int4 for send");
+ assertFalse(executor.useBinaryForReceive(Oid.INT4),
+ "binaryTransferDisable still applies to receive, which has no override");
+ }
+ }
+
+ @Test
+ void perDirectionDisableWinsOverLegacyEnable() throws SQLException {
+ Properties props = new Properties();
+ // bool is not a default binary type, so binaryTransferEnable is what turns it on.
+ PGProperty.BINARY_TRANSFER_ENABLE.set(props, "bool");
+ PGProperty.BINARY_RECEIVE.set(props, "bool:disable");
+ try (Connection con = TestUtil.openDB(props)) {
+ QueryExecutor executor = executorOf(con);
+ assertFalse(executor.useBinaryForReceive(Oid.BOOL),
+ "binaryReceive=bool:disable should override binaryTransferEnable=bool for receive");
+ assertTrue(executor.useBinaryForSend(Oid.BOOL),
+ "send direction keeps binaryTransferEnable=bool");
+ }
+ }
+
+ @Test
+ void forceIsRejectedForReceive() {
+ assertInvalidParameterValue("binaryReceive", "int4:force",
+ "force is not a valid mode for binaryReceive");
+ }
+
+ @Test
+ void unknownModeIsRejected() {
+ assertInvalidParameterValue("binarySend", "int4:bogus", "an unknown mode should be rejected");
+ }
+
+ @Test
+ void missingColonIsRejected() {
+ assertInvalidParameterValue("binarySend", "int4",
+ "an entry without oid:mode syntax should be rejected");
+ }
+
+ @Test
+ void duplicateTypeIsRejected() {
+ assertInvalidParameterValue("binarySend", "int4:force,int4:disable",
+ "the same type listed twice in one direction should be rejected");
+ }
+
+ /**
+ * Asserts that connecting with the given binary direction property fails with
+ * {@link PSQLState#INVALID_PARAMETER_VALUE}, so the test cannot pass on an unrelated
+ * connection error.
+ */
+ private static void assertInvalidParameterValue(String property, String value, String message) {
+ Properties props = new Properties();
+ props.setProperty(property, value);
+ SQLException e = assertThrows(SQLException.class, () -> TestUtil.openDB(props).close(), message);
+ assertEquals(PSQLState.INVALID_PARAMETER_VALUE.getState(), e.getSQLState(), message);
+ }
+}
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: github://pgjdbc/pgjdbc
Cc: [email protected], [email protected]
Subject: Re: [pgjdbc/pgjdbc] PR #4195: feat(jdbc): add direction-aware binarySend/binaryReceive properties
In-Reply-To: <<[email protected]>>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox