[Git][java-team/libpostgresql-jdbc-java][upstream] New upstream version 42.5.3

Christoph Berg (@myon) gitlab at salsa.debian.org
Thu Feb 9 10:29:16 GMT 2023



Christoph Berg pushed to branch upstream at Debian Java Maintainers / libpostgresql-jdbc-java


Commits:
6a36d0f3 by Christoph Berg at 2023-02-09T11:26:20+01:00
New upstream version 42.5.3
- - - - -


28 changed files:

- README.md
- pom.xml
- src/main/java/org/postgresql/PGProperty.java
- src/main/java/org/postgresql/core/BaseConnection.java
- src/main/java/org/postgresql/core/ConnectionFactory.java
- src/main/java/org/postgresql/core/Oid.java
- src/main/java/org/postgresql/core/PGStream.java
- src/main/java/org/postgresql/core/Parser.java
- src/main/java/org/postgresql/core/QueryExecutor.java
- src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
- src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
- src/main/java/org/postgresql/ds/common/BaseDataSource.java
- src/main/java/org/postgresql/jdbc/PgConnection.java
- src/main/java/org/postgresql/jdbc/PgResultSet.java
- src/main/java/org/postgresql/jdbc/TypeInfoCache.java
- src/main/java/org/postgresql/util/ByteConverter.java
- src/main/java/org/postgresql/util/DriverInfo.java
- src/main/java/org/postgresql/util/StreamWrapper.java
- src/main/resources/META-INF/MANIFEST.MF
- src/test/java/org/postgresql/core/ParserTest.java
- + src/test/java/org/postgresql/test/core/QueryExecutorTest.java
- src/test/java/org/postgresql/test/jdbc2/BaseTest4.java
- src/test/java/org/postgresql/test/jdbc2/ConnectTimeoutTest.java
- + src/test/java/org/postgresql/test/jdbc2/CustomTypeWithBinaryTransferTest.java
- src/test/java/org/postgresql/test/jdbc2/LoginTimeoutTest.java
- src/test/java/org/postgresql/test/jdbc2/ResultSetTest.java
- + src/test/java/org/postgresql/util/BigDecimalByteConverter2Test.java
- src/test/java/org/postgresql/util/BigDecimalByteConverterTest.java


Changes:

=====================================
README.md
=====================================
@@ -1,4 +1,4 @@
-<img height="90" alt="Slonik Duke" align="right" src="docs/media/img/slonik_duke.png" />
+<img height="90" alt="Slonik Duke" align="right" src="docs/_site/media/img/slonik_duke.png" />
 
 # PostgreSQL JDBC Driver
 
@@ -88,62 +88,68 @@ Note that the most detailed log levels, "`FINEST`", may include sensitive inform
 #### Connection Properties
 In addition to the standard connection parameters the driver supports a number of additional properties which can be used to specify additional driver behaviour specific to PostgreSQL™. These properties may be specified in either the connection URL or an additional Properties object parameter to DriverManager.getConnection.
 
-| Property                      | Type    | Default | Description   |
-| ----------------------------- | ------- | :-----: | ------------- |
-| user                          | String  | null    | The database user on whose behalf the connection is being made. |
-| password                      | String  | null    | The database user's password. |
-| options                       | String  | null    | Specify 'options' connection initialization parameter. |
-| service                       | String  | null    | Specify 'service' name described in pg_service.conf file. References: [The Connection Service File](https://www.postgresql.org/docs/current/libpq-pgservice.html) and [The Password File](https://www.postgresql.org/docs/current/libpq-pgpass.html). 'service' file can provide all properties including 'hostname=', 'port=' and 'dbname='. |
-| ssl                           | Boolean | false   | Control use of SSL (true value causes SSL to be required) |
-| sslfactory                    | String  | null    | Provide a SSLSocketFactory class when using SSL. |
-| sslfactoryarg (deprecated)    | String  | null    | Argument forwarded to constructor of SSLSocketFactory class. |
-| sslmode                       | String  | prefer  | Controls the preference for opening using an SSL encrypted connection. |
-| sslcert                       | String  | null    | The location of the client's SSL certificate |
-| sslkey                        | String  | null    | The location of the client's PKCS#8 SSL key |
-| sslrootcert                   | String  | null    | The location of the root certificate for authenticating the server. |
-| sslhostnameverifier           | String  | null    | The name of a class (for use in [Class.forName(String)](https://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName%28java.lang.String%29)) that implements javax.net.ssl.HostnameVerifier and can verify the server hostname. |
-| sslpasswordcallback           | String  | null    | The name of a class (for use in [Class.forName(String)](https://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName%28java.lang.String%29)) that implements javax.security.auth.callback.CallbackHandler and can handle PasswordCallback for the ssl password. |
-| sslpassword                   | String  | null    | The password for the client's ssl key (ignored if sslpasswordcallback is set) |
-| sendBufferSize                | Integer | -1      | Socket write buffer size |
-| receiveBufferSize             | Integer | -1      | Socket read buffer size  |
-| logServerErrorDetail          | Boolean | true    | Allows server error detail (such as sql statements and values) to be logged and passed on in exceptions.  Setting to false will mask these errors so they won't be exposed to users, or logs. |
-| allowEncodingChanges          | Boolean | false   | Allow for changes in client_encoding |
-| logUnclosedConnections        | Boolean | false   | When connections that are not explicitly closed are garbage collected, log the stacktrace from the opening of the connection to trace the leak source |
-| 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. |
-| prepareThreshold              | Integer | 5       | Statement prepare threshold. A value of -1 stands for forceBinary |
-| 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. |
-| defaultRowFetchSize           | Integer | 0       | Positive number of rows that should be fetched from the database when more rows are needed for ResultSet by each fetch iteration |
-| loginTimeout                  | Integer | 0       | Specify how long to wait for establishment of a database connection.|
-| connectTimeout                | Integer | 10      | The timeout value used for socket connect operations. |
-| socketTimeout                 | Integer | 0       | The timeout value used for socket read operations. |
-| sslResponseTimeout            | Integer | 5000    | Socket timeout waiting for a response from a request for SSL upgrade from the server. |                                                                                                                                                                        
-| tcpKeepAlive                  | Boolean | false   | Enable or disable TCP keep-alive. |
-| tcpNoDelay                    | Boolean | true    | Enable or disable TCP no delay. |
-| ApplicationName               | String  | PostgreSQL JDBC Driver    | The application name (require server version >= 9.0). If assumeMinServerVersion is set to >= 9.0 this will be sent in the startup packets, otherwise after the connection is made |
-| readOnly                      | Boolean | false   | Puts this connection in read-only mode |
-| disableColumnSanitiser        | Boolean | false   | Enable optimization that disables column name sanitiser |
-| assumeMinServerVersion        | String  | null    | Assume the server is at least that version |
-| currentSchema                 | String  | null    | Specify the schema (or several schema separated by commas) to be set in the search-path |
-| targetServerType              | String  | any     | Specifies what kind of server to connect, possible values: any, master, slave (deprecated), secondary, preferSlave (deprecated), preferSecondary, preferPrimary |
-| hostRecheckSeconds            | Integer | 10      | Specifies period (seconds) after which the host status is checked again in case it has changed |
-| loadBalanceHosts              | Boolean | false   | If disabled hosts are connected in the given order. If enabled hosts are chosen randomly from the set of suitable candidates |
-| socketFactory                 | String  | null    | Specify a socket factory for socket creation |
-| socketFactoryArg (deprecated) | String  | null    | Argument forwarded to constructor of SocketFactory class. |
-| autosave                      | String  | never   | Specifies what the driver should do if a query fails, possible values: always, never, conservative |
-| cleanupSavepoints             | Boolean | false   | In Autosave mode the driver sets a SAVEPOINT for every query. It is possible to exhaust the server shared buffers. Setting this to true will release each SAVEPOINT at the cost of an additional round trip. |
-| preferQueryMode               | String  | extended | Specifies which mode is used to execute queries to database, possible values: extended, extendedForPrepared, extendedCacheEverything, simple |
-| reWriteBatchedInserts         | Boolean | false   | Enable optimization to rewrite and collapse compatible INSERT statements that are batched. |
-| escapeSyntaxCallMode          | String  | select  | Specifies how JDBC escape call syntax is transformed into underlying SQL (CALL/SELECT), for invoking procedures or functions (requires server version >= 11), possible values: select, callIfNoReturn, call |
-| maxResultBuffer               | String  | null    | Specifies size of result buffer in bytes, which can't be exceeded during reading result set. Can be specified as particular size (i.e. "100", "200M" "2G") or as percent of max heap memory (i.e. "10p", "20pct", "50percent") |
-| gssEncMode                    | String  | allow   | Controls the preference for using GSSAPI encryption for the connection, values are disable, allow, prefer, and require |
-| adaptiveFetch                 | Boolean | false   | Specifies if number of rows fetched in ResultSet by each fetch iteration should be dynamic. Number of rows will be calculated by dividing maxResultBuffer size into max row size observed so far. Requires declaring maxResultBuffer and defaultRowFetchSize for first iteration. 
-| adaptiveFetchMinimum          | Integer | 0       | Specifies minimum number of rows, which can be calculated by adaptiveFetch. Number of rows used by adaptiveFetch cannot go below this value. 
-| adaptiveFetchMaximum          | Integer | -1      | Specifies maximum number of rows, which can be calculated by adaptiveFetch. Number of rows used by adaptiveFetch cannot go above this value. Any negative number set as adaptiveFetchMaximum is used by adaptiveFetch as infinity number of rows.
-| localSocketAddress            | String  | null    | Hostname or IP address given to explicitly configure the interface that the driver will bind the client side of the TCP/IP connection to when connecting.
-| quoteReturningIdentifiers     | Boolean | true    | By default we double quote returning identifiers. Some ORM's already quote them. Switch allows them to turn this off
-| authenticationPluginClassName | String  | null    | Fully qualified class name of the class implementing the AuthenticationPlugin interface. If this is null, the password value in the connection properties will be used.
+| Property                      | Type |         Default         | Description                                                                                                                                                                                                                                                                                                                                     |
+|-------------------------------| -- |:-----------------------:|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------    |
+| user                          | String |          null           | The database user on whose behalf the connection is being made.                                                                                                                                                                                                                                                                               |
+| password                      | String |          null           | The database user's password.                                                                                                                                                                                                                                                                                                                 |
+| options                       | String |          null           | Specify 'options' connection initialization parameter.                                                                                                                                                                                                                                                                                        |
+| service                       | String |          null           | Specify 'service' name described in pg_service.conf file. References: [The Connection Service File](https://www.postgresql.org/docs/current/libpq-pgservice.html) and [The Password File](https://www.postgresql.org/docs/current/libpq-pgpass.html). 'service' file can provide all properties including 'hostname=', 'port=' and 'dbname='. |
+| ssl                           | Boolean |          false          | Control use of SSL (true value causes SSL to be required)                                                                                                                                                                                                                                                                                    |
+| sslfactory                    | String | org.postgresql.ssl.LibPQFactory | Provide a SSLSocketFactory class when using SSL.                                                                                                                                                                                                                                                                                      |
+| sslfactoryarg (deprecated)    | String |          null           | Argument forwarded to constructor of SSLSocketFactory class.                                                                                                                                                                                                                                                                                  |
+| sslmode                       | String |         prefer          | Controls the preference for opening using an SSL encrypted connection.                                                                                                                                                                                                                                                                        |
+| sslcert                       | String |          null           | The location of the client's SSL certificate                                                                                                                                                                                                                                                                                                  |
+| sslkey                        | String |          null           | The location of the client's PKCS#8 SSL key                                                                                                                                                                                                                                                                                                   |
+| sslrootcert                   | String |          null           | The location of the root certificate for authenticating the server.                                                                                                                                                                                                                                                                           |
+| sslhostnameverifier           | String |          null           | The name of a class (for use in [Class.forName(String)](https://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName%28java.lang.String%29)) that implements javax.net.ssl.HostnameVerifier and can verify the server hostname.                                                                                                     |
+| sslpasswordcallback           | String |          null           | The name of a class (for use in [Class.forName(String)](https://docs.oracle.com/javase/6/docs/api/java/lang/Class.html#forName%28java.lang.String%29)) that implements javax.security.auth.callback.CallbackHandler and can handle PasswordCallback for the ssl password.                                                                     |
+| sslpassword                   | String |          null           | The password for the client's ssl key (ignored if sslpasswordcallback is set)                                                                                                                                                                                                                                                                 |
+| sendBufferSize                | Integer |           -1            | Socket write buffer size                                                                                                                                                                                                                                                                                                                      |
+| receiveBufferSize             | Integer |           -1            | Socket read buffer size                                                                                                                                                                                                                                                                                                                       |
+| logServerErrorDetail          | Boolean |          true           | Allows server error detail (such as sql statements and values) to be logged and passed on in exceptions.  Setting to false will mask these errors so they won't be exposed to users, or logs.                                                                                                                                                 |
+| allowEncodingChanges          | Boolean |          false          | Allow for changes in client_encoding                                                                                                                                                                                                                                                                                                          |
+| logUnclosedConnections        | Boolean |          false          | When connections that are not explicitly closed are garbage collected, log the stacktrace from the opening of the connection to trace the leak source                                                                                                                                                                                         |
+| binaryTransfer                | Boolean |          true           | Use binary format for sending and receiving data if possible. Setting this to false disables any binary transfer                                                                                                                                                                                                                              |
+| 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.                                                                                                                                                                   |
+| prepareThreshold              | Integer |            5            | Statement prepare threshold. A value of -1 stands for forceBinary                                                                                                                                                                                                                                                                             |
+| 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.                                                                                                                                                                                                                      |
+| defaultRowFetchSize           | Integer |            0            | Positive number of rows that should be fetched from the database when more rows are needed for ResultSet by each fetch iteration                                                                                                                                                                                                              |
+| loginTimeout                  | Integer |            0            | Specify how long in seconds max(2147484) to wait for establishment of a database connection.                                                                                                                                                                                                                                                  |
+| connectTimeout                | Integer |           10            | The timeout value in seconds max(2147484) used for socket connect operations.                                                                                                                                                                                                                                                                 |
+| socketTimeout                 | Integer |            0            | The timeout value in seconds max(2147484) used for socket read operations.                                                                                                                                                                                                                                                                    |
+| sslResponseTimeout            | Integer |          5000           | Socket timeout in milliseconds waiting for a response from a request for SSL upgrade from the server.                                                                                                                                                                                                                                         |
+| tcpKeepAlive                  | Boolean |          false          | Enable or disable TCP keep-alive.                                                                                                                                                                                                                                                                                                             |
+| tcpNoDelay                    | Boolean |          true           | Enable or disable TCP no delay.                                                                                                                                                                                                                                                                                                               |
+| ApplicationName               | String  | PostgreSQL JDBC Driver   | The application name (require server version >= 9.0). If assumeMinServerVersion is set to >= 9.0 this will be sent in the startup packets, otherwise after the connection is made                                                                                                                                                             |
+| readOnly                      | Boolean |          false          | Puts this connection in read-only mode                                                                                                                                                                                                                                                                                                        |
+| disableColumnSanitiser        | Boolean |          false          | Enable optimization that disables column name sanitiser                                                                                                                                                                                                                                                                                       |
+| assumeMinServerVersion        | String |          null           | Assume the server is at least that version                                                                                                                                                                                                                                                                                                    |
+| currentSchema                 | String |          null           | Specify the schema (or several schema separated by commas) to be set in the search-path                                                                                                                                                                                                                                                       |
+| targetServerType              | String |           any           | Specifies what kind of server to connect, possible values: any, master, slave (deprecated), secondary, preferSlave (deprecated), preferSecondary, preferPrimary                                                                                                                                                                               |
+| hostRecheckSeconds            | Integer |           10            | Specifies period (seconds) after which the host status is checked again in case it has changed                                                                                                                                                                                                                                                |
+| loadBalanceHosts              | Boolean |          false          | If disabled hosts are connected in the given order. If enabled hosts are chosen randomly from the set of suitable candidates                                                                                                                                                                                                                  |
+| socketFactory                 | String |          null           | Specify a socket factory for socket creation                                                                                                                                                                                                                                                                                                  |
+| socketFactoryArg (deprecated) | String |          null           | Argument forwarded to constructor of SocketFactory class.                                                                                                                                                                                                                                                                                     |
+| autosave                      | String |          never          | Specifies what the driver should do if a query fails, possible values: always, never, conservative                                                                                                                                                                                                                                            |
+| cleanupSavepoints             | Boolean |          false          | In Autosave mode the driver sets a SAVEPOINT for every query. It is possible to exhaust the server shared buffers. Setting this to true will release each SAVEPOINT at the cost of an additional round trip.                                                                                                                                  |
+| preferQueryMode               | String |        extended         | Specifies which mode is used to execute queries to database, possible values: extended, extendedForPrepared, extendedCacheEverything, simple                                                                                                                                                                                                  |
+| reWriteBatchedInserts         | Boolean |          false          | Enable optimization to rewrite and collapse compatible INSERT statements that are batched.                                                                                                                                                                                                                                                    |
+| escapeSyntaxCallMode          | String |         select          | Specifies how JDBC escape call syntax is transformed into underlying SQL (CALL/SELECT), for invoking procedures or functions (requires server version >= 11), possible values: select, callIfNoReturn, call                                                                                                                                   |
+| maxResultBuffer               | String |          null           | Specifies size of result buffer in bytes, which can't be exceeded during reading result set. Can be specified as particular size (i.e. "100", "200M" "2G") or as percent of max heap memory (i.e. "10p", "20pct", "50percent")                                                                                                                |
+| gssLib                        | String |          auto           | Permissible values are auto (default, see below), sspi (force SSPI) or gssapi (force GSSAPI-JSSE).                                                                                                                                                                                                                                            |
+| gssResponseTimeout            | Integer |          5000           | Socket timeout in milliseconds waiting for a response from a request for GSS encrypted connection from the server.                                                                                                                                                                                                                            |
+| gssEncMode                    | String |          allow          | Controls the preference for using GSSAPI encryption for the connection, values are disable, allow, prefer, and require                                                                                                                                                                                                                        |
+| useSpnego                     | String |          false           | Use SPNEGO in SSPI authentication requests                                
+| adaptiveFetch                 | Boolean |          false          | Specifies if number of rows fetched in ResultSet by each fetch iteration should be dynamic. Number of rows will be calculated by dividing maxResultBuffer size into max row size observed so far. Requires declaring maxResultBuffer and defaultRowFetchSize for first iteration.                                                             |
+| adaptiveFetchMinimum          | Integer |            0            | Specifies minimum number of rows, which can be calculated by adaptiveFetch. Number of rows used by adaptiveFetch cannot go below this value.                                                                                                                                                                                                  |
+| adaptiveFetchMaximum          | Integer |           -1            | Specifies maximum number of rows, which can be calculated by adaptiveFetch. Number of rows used by adaptiveFetch cannot go above this value. Any negative number set as adaptiveFetchMaximum is used by adaptiveFetch as infinity number of rows.                                                                                             |
+| localSocketAddress            | String |          null           | Hostname or IP address given to explicitly configure the interface that the driver will bind the client side of the TCP/IP connection to when connecting.                                                                                                                                                                                     |
+| quoteReturningIdentifiers     | Boolean |          true           | By default we double quote returning identifiers. Some ORM's already quote them. Switch allows them to turn this off                                                                                                                                                                                                                          |
+| authenticationPluginClassName | String |          null           | Fully qualified class name of the class implementing the AuthenticationPlugin interface. If this is null, the password value in the connection properties will be used.                                                                                                                                                                       |
+| unknownLength                 | Integer |   Integer.MAX_LENGTH    | Specifies the length to return for types of unknown length                                                                                                                                                                                                                                                                                    |
+| stringtype                    | String |          null           | Specify the type to use when binding `PreparedStatement` parameters set via `setString()`                                                                                                                                                                                                                                                     |
 
 ## Contributing
 For information on how to contribute to the project see the [Contributing Guidelines](CONTRIBUTING.md)


=====================================
pom.xml
=====================================
@@ -10,7 +10,7 @@
     <artifactId>postgresql</artifactId>
     <packaging>jar</packaging>
     <name>PostgreSQL JDBC Driver - JDBC 4.2</name>
-    <version>42.5.1</version>
+    <version>42.5.3</version>
     <description>Java JDBC 4.2 (JRE 8+) driver for PostgreSQL database</description>
     <url>https://github.com/pgjdbc/pgjdbc</url>
 


=====================================
src/main/java/org/postgresql/PGProperty.java
=====================================
@@ -163,7 +163,7 @@ public enum PGProperty {
   CONNECT_TIMEOUT(
       "connectTimeout",
       "10",
-      "The timeout value used for socket connect operations."),
+      "The timeout value in seconds used for socket connect operations."),
 
   /**
    * Specify the schema (or several schema separated by commas) to be set in the search-path. This schema will be used to resolve
@@ -260,6 +260,17 @@ public enum PGProperty {
       false,
       new String[] {"auto", "sspi", "gssapi"}),
 
+  /**
+   * <p>After requesting an upgrade to SSL from the server there are reports of the server not responding due to a failover
+   * without a timeout here, the client can wait forever. The pattern for requesting a GSS encrypted connection is the same so we provide the same
+   * timeout mechanism This timeout will be set before the request and reset after </p>
+   */
+  GSS_RESPONSE_TIMEOUT(
+      "gssResponseTimeout",
+      "5000",
+      "Time in milliseconds we wait for a response from the server after requesting a GSS upgrade"),
+
+
   /**
    * Enable mode to filter out the names of database objects for which the current user has no privileges
    * granted from appearing in the DatabaseMetaData returned by the driver.
@@ -344,7 +355,7 @@ public enum PGProperty {
   LOGIN_TIMEOUT(
       "loginTimeout",
       "0",
-      "Specify how long to wait for establishment of a database connection."),
+      "Specify how long in seconds to wait for establishment of a database connection."),
 
   /**
    * Whether to include full server error detail in exception messages.
@@ -589,7 +600,7 @@ public enum PGProperty {
   SOCKET_TIMEOUT(
       "socketTimeout",
       "0",
-      "The timeout value used for socket read operations."),
+      "The timeout value in seconds max(2147484) used for socket read operations."),
 
   /**
    * Control use of SSL: empty or {@code true} values imply {@code sslmode==verify-full}
@@ -613,7 +624,7 @@ public enum PGProperty {
    */
   SSL_FACTORY(
       "sslfactory",
-      null,
+      "org.postgresql.ssl.LibPQFactory",
       "Provide a SSLSocketFactory class when using SSL."),
 
   /**
@@ -793,7 +804,7 @@ public enum PGProperty {
     this.choices = choices;
   }
 
-  private static final Map<String, PGProperty> PROPS_BY_NAME = new HashMap<String, PGProperty>();
+  private static final Map<String, PGProperty> PROPS_BY_NAME = new HashMap<>();
 
   static {
     for (PGProperty prop : PGProperty.values()) {


=====================================
src/main/java/org/postgresql/core/BaseConnection.java
=====================================
@@ -147,6 +147,7 @@ public interface BaseConnection extends PGConnection, Connection {
   boolean getStandardConformingStrings();
 
   // Ew. Quick hack to give access to the connection-specific utils implementation.
+  @Deprecated
   TimestampUtils getTimestampUtils();
 
   // Get the per-connection logger.


=====================================
src/main/java/org/postgresql/core/ConnectionFactory.java
=====================================
@@ -18,6 +18,8 @@ import org.postgresql.util.PSQLState;
 import java.io.IOException;
 import java.sql.SQLException;
 import java.util.Properties;
+import java.util.logging.Level;
+import java.util.logging.Logger;
 
 /**
  * Handles protocol-specific connection setup.
@@ -25,6 +27,9 @@ import java.util.Properties;
  * @author Oliver Jowett (oliver at opencloud.com)
  */
 public abstract class ConnectionFactory {
+
+  private static final Logger LOGGER = Logger.getLogger(ConnectionFactory.class.getName());
+
   /**
    * <p>Establishes and initializes a new connection.</p>
    *
@@ -83,6 +88,7 @@ public abstract class ConnectionFactory {
       try {
         newStream.close();
       } catch (IOException e) {
+        LOGGER.log(Level.WARNING, "Failed to closed stream with error: {0}", e);
       }
     }
   }


=====================================
src/main/java/org/postgresql/core/Oid.java
=====================================
@@ -73,6 +73,7 @@ public class Oid {
   public static final int POINT = 600;
   public static final int POINT_ARRAY = 1017;
   public static final int BOX = 603;
+  public static final int BOX_ARRAY = 1020;
   public static final int JSONB = 3802;
   public static final int JSONB_ARRAY = 3807;
   public static final int JSON = 114;
@@ -91,8 +92,8 @@ public class Oid {
   public static final int TSVECTOR = 3614;
   public static final int TSQUERY = 3615;
 
-  private static final Map<Integer, String> OID_TO_NAME = new HashMap<Integer, String>(100);
-  private static final Map<String, Integer> NAME_TO_OID = new HashMap<String, Integer>(100);
+  private static final Map<Integer, String> OID_TO_NAME = new HashMap<>(100);
+  private static final Map<String, Integer> NAME_TO_OID = new HashMap<>(100);
 
   static {
     for (Field field : Oid.class.getFields()) {


=====================================
src/main/java/org/postgresql/core/PGStream.java
=====================================
@@ -226,21 +226,33 @@ public class PGStream implements Closeable, Flushable {
   }
 
   private Socket createSocket(int timeout) throws IOException {
-    Socket socket = socketFactory.createSocket();
-    String localSocketAddress = hostSpec.getLocalSocketAddress();
-    if (localSocketAddress != null) {
-      socket.bind(new InetSocketAddress(InetAddress.getByName(localSocketAddress), 0));
-    }
-    if (!socket.isConnected()) {
-      // When using a SOCKS proxy, the host might not be resolvable locally,
-      // thus we defer resolution until the traffic reaches the proxy. If there
-      // is no proxy, we must resolve the host to an IP to connect the socket.
-      InetSocketAddress address = hostSpec.shouldResolve()
-          ? new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort())
-          : InetSocketAddress.createUnresolved(hostSpec.getHost(), hostSpec.getPort());
-      socket.connect(address, timeout);
-    }
-    return socket;
+    Socket socket = null;
+    try {
+      socket = socketFactory.createSocket();
+      String localSocketAddress = hostSpec.getLocalSocketAddress();
+      if (localSocketAddress != null) {
+        socket.bind(new InetSocketAddress(InetAddress.getByName(localSocketAddress), 0));
+      }
+      if (!socket.isConnected()) {
+        // When using a SOCKS proxy, the host might not be resolvable locally,
+        // thus we defer resolution until the traffic reaches the proxy. If there
+        // is no proxy, we must resolve the host to an IP to connect the socket.
+        InetSocketAddress address = hostSpec.shouldResolve()
+            ? new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort())
+            : InetSocketAddress.createUnresolved(hostSpec.getHost(), hostSpec.getPort());
+        socket.connect(address, timeout);
+      }
+      return socket;
+    } catch ( Exception ex ) {
+      if (socket != null) {
+        try {
+          socket.close();
+        } catch ( Exception ex1 ) {
+          ex.addSuppressed(ex1);
+        }
+      }
+      throw ex;
+    }
   }
 
   /**


=====================================
src/main/java/org/postgresql/core/Parser.java
=====================================
@@ -66,12 +66,14 @@ public class Parser {
     List<NativeQuery> nativeQueries = null;
     boolean isCurrentReWriteCompatible = false;
     boolean isValuesFound = false;
-    int valuesBraceOpenPosition = -1;
-    int valuesBraceClosePosition = -1;
-    boolean valuesBraceCloseFound = false;
+    int valuesParenthesisOpenPosition = -1;
+    int valuesParenthesisClosePosition = -1;
+    boolean valuesParenthesisCloseFound = false;
     boolean isInsertPresent = false;
     boolean isReturningPresent = false;
     boolean isReturningPresentPrev = false;
+    boolean isBeginPresent = false;
+    boolean isBeginAtomicPresent = false;
     SqlCommandType currentCommandType = SqlCommandType.BLANK;
     SqlCommandType prevCommandType = SqlCommandType.BLANK;
     int numberOfStatements = 0;
@@ -80,10 +82,15 @@ public class Parser {
     int keyWordCount = 0;
     int keywordStart = -1;
     int keywordEnd = -1;
+    /*
+    loop through looking for keywords, single quotes, double quotes, comments, dollar quotes,
+    parenthesis, ? and ;
+    for single/double/dollar quotes, and comments we just want to move the index
+     */
     for (int i = 0; i < aChars.length; ++i) {
       char aChar = aChars[i];
       boolean isKeyWordChar = false;
-      // ';' is ignored as it splits the queries
+      // ';' is ignored as it splits the queries. We do have to deal with ; in BEGIN ATOMIC functions
       whitespaceOnly &= aChar == ';' || Character.isWhitespace(aChar);
       keywordEnd = i; // parseSingleQuotes, parseDoubleQuotes, etc move index so we keep old value
       switch (aChar) {
@@ -111,10 +118,10 @@ public class Parser {
 
         case ')':
           inParen--;
-          if (inParen == 0 && isValuesFound && !valuesBraceCloseFound) {
+          if (inParen == 0 && isValuesFound && !valuesParenthesisCloseFound) {
             // If original statement is multi-values like VALUES (...), (...), ... then
             // search for the latest closing paren
-            valuesBraceClosePosition = nativeSql.length() + i - fragmentStart;
+            valuesParenthesisClosePosition = nativeSql.length() + i - fragmentStart;
           }
           break;
 
@@ -139,7 +146,8 @@ public class Parser {
           break;
 
         case ';':
-          if (inParen == 0) {
+          // we don't split the queries if BEGIN ATOMIC is present
+          if (!isBeginAtomicPresent && inParen == 0) {
             if (!whitespaceOnly) {
               numberOfStatements++;
               nativeSql.append(aChars, fragmentStart, i - fragmentStart);
@@ -156,18 +164,18 @@ public class Parser {
                   nativeQueries = new ArrayList<NativeQuery>();
                 }
 
-                if (!isValuesFound || !isCurrentReWriteCompatible || valuesBraceClosePosition == -1
+                if (!isValuesFound || !isCurrentReWriteCompatible || valuesParenthesisClosePosition == -1
                     || (bindPositions != null
-                    && valuesBraceClosePosition < bindPositions.get(bindPositions.size() - 1))) {
-                  valuesBraceOpenPosition = -1;
-                  valuesBraceClosePosition = -1;
+                    && valuesParenthesisClosePosition < bindPositions.get(bindPositions.size() - 1))) {
+                  valuesParenthesisOpenPosition = -1;
+                  valuesParenthesisClosePosition = -1;
                 }
 
                 nativeQueries.add(new NativeQuery(nativeSql.toString(),
                     toIntArray(bindPositions), false,
                     SqlCommand.createStatementTypeInfo(
-                        currentCommandType, isBatchedReWriteConfigured, valuesBraceOpenPosition,
-                        valuesBraceClosePosition,
+                        currentCommandType, isBatchedReWriteConfigured, valuesParenthesisOpenPosition,
+                        valuesParenthesisClosePosition,
                         isReturningPresent, nativeQueries.size())));
               }
             }
@@ -183,9 +191,9 @@ public class Parser {
               nativeSql.setLength(0);
               isValuesFound = false;
               isCurrentReWriteCompatible = false;
-              valuesBraceOpenPosition = -1;
-              valuesBraceClosePosition = -1;
-              valuesBraceCloseFound = false;
+              valuesParenthesisOpenPosition = -1;
+              valuesParenthesisClosePosition = -1;
+              valuesParenthesisCloseFound = false;
             }
           }
           break;
@@ -202,10 +210,10 @@ public class Parser {
           isKeyWordChar = isIdentifierStartChar(aChar);
           if (isKeyWordChar) {
             keywordStart = i;
-            if (valuesBraceOpenPosition != -1 && inParen == 0) {
+            if (valuesParenthesisOpenPosition != -1 && inParen == 0) {
               // When the statement already has multi-values, stop looking for more of them
               // Since values(?,?),(?,?),... should not contain keywords in the middle
-              valuesBraceCloseFound = true;
+              valuesParenthesisCloseFound = true;
             }
           }
           break;
@@ -238,15 +246,32 @@ public class Parser {
               isCurrentReWriteCompatible = false;
             }
           }
+
         } else if (currentCommandType == SqlCommandType.WITH
             && inParen == 0) {
           SqlCommandType command = parseWithCommandType(aChars, i, keywordStart, wordLength);
           if (command != null) {
             currentCommandType = command;
           }
+        } else if (currentCommandType == SqlCommandType.CREATE) {
+          /*
+          We are looking for BEGIN ATOMIC
+           */
+          if (wordLength == 5 && parseBeginKeyword(aChars, keywordStart)) {
+            isBeginPresent = true;
+          } else {
+            // found begin, now look for atomic
+            if (isBeginPresent == true) {
+              if (wordLength == 6 && parseAtomicKeyword(aChars, keywordStart)) {
+                isBeginAtomicPresent = true;
+              }
+              // either way we reset beginFound
+              isBeginPresent = false;
+            }
+          }
         }
         if (inParen != 0 || aChar == ')') {
-          // RETURNING and VALUES cannot be present in braces
+          // RETURNING and VALUES cannot be present in parentheses
         } else if (wordLength == 9 && parseReturningKeyword(aChars, keywordStart)) {
           isReturningPresent = true;
         } else if (wordLength == 6 && parseValuesKeyword(aChars, keywordStart)) {
@@ -257,17 +282,17 @@ public class Parser {
       }
       if (aChar == '(') {
         inParen++;
-        if (inParen == 1 && isValuesFound && valuesBraceOpenPosition == -1) {
-          valuesBraceOpenPosition = nativeSql.length() + i - fragmentStart;
+        if (inParen == 1 && isValuesFound && valuesParenthesisOpenPosition == -1) {
+          valuesParenthesisOpenPosition = nativeSql.length() + i - fragmentStart;
         }
       }
     }
 
-    if (!isValuesFound || !isCurrentReWriteCompatible || valuesBraceClosePosition == -1
+    if (!isValuesFound || !isCurrentReWriteCompatible || valuesParenthesisClosePosition == -1
         || (bindPositions != null
-        && valuesBraceClosePosition < bindPositions.get(bindPositions.size() - 1))) {
-      valuesBraceOpenPosition = -1;
-      valuesBraceClosePosition = -1;
+        && valuesParenthesisClosePosition < bindPositions.get(bindPositions.size() - 1))) {
+      valuesParenthesisOpenPosition = -1;
+      valuesParenthesisClosePosition = -1;
     }
 
     if (fragmentStart < aChars.length && !whitespaceOnly) {
@@ -293,7 +318,7 @@ public class Parser {
     NativeQuery lastQuery = new NativeQuery(nativeSql.toString(),
         toIntArray(bindPositions), !splitStatements,
         SqlCommand.createStatementTypeInfo(currentCommandType,
-            isBatchedReWriteConfigured, valuesBraceOpenPosition, valuesBraceClosePosition,
+            isBatchedReWriteConfigured, valuesParenthesisOpenPosition, valuesParenthesisClosePosition,
             isReturningPresent, (nativeQueries == null ? 0 : nativeQueries.size())));
 
     if (nativeQueries == null) {
@@ -608,6 +633,44 @@ public class Parser {
         && (query[offset + 5] | 32) == 't';
   }
 
+  /**
+   Parse string to check presence of BEGIN keyword regardless of case.
+   *
+   * @param query char[] of the query statement
+   * @param offset position of query to start checking
+   * @return boolean indicates presence of word
+   */
+
+  public static boolean parseBeginKeyword(final char[] query, int offset) {
+    if (query.length < (offset + 6)) {
+      return false;
+    }
+    return (query[offset] | 32) == 'b'
+        && (query[offset + 1] | 32) == 'e'
+        && (query[offset + 2] | 32) == 'g'
+        && (query[offset + 3] | 32) == 'i'
+        && (query[offset + 4] | 32) == 'n';
+  }
+
+  /**
+   Parse string to check presence of ATOMIC keyword regardless of case.
+   *
+   * @param query char[] of the query statement
+   * @param offset position of query to start checking
+   * @return boolean indicates presence of word
+   */
+  public static boolean parseAtomicKeyword(final char[] query, int offset) {
+    if (query.length < (offset + 7)) {
+      return false;
+    }
+    return (query[offset] | 32) == 'a'
+        && (query[offset + 1] | 32) == 't'
+        && (query[offset + 2] | 32) == 'o'
+        && (query[offset + 3] | 32) == 'm'
+        && (query[offset + 4] | 32) == 'i'
+        && (query[offset + 5] | 32) == 'c';
+  }
+
   /**
    * Parse string to check presence of MOVE keyword regardless of case.
    *
@@ -886,7 +949,7 @@ public class Parser {
      * pgsql/src/backend/parser/scan.l:
      * ident_start    [A-Za-z\200-\377_]
      * ident_cont     [A-Za-z\200-\377_0-9\$]
-     * however is is not clear how that interacts with unicode, so we just use Java's implementation.
+     * however it is not clear how that interacts with unicode, so we just use Java's implementation.
      */
     return Character.isJavaIdentifierStart(c);
   }
@@ -1362,7 +1425,7 @@ public class Parser {
     return i;
   }
 
-  private static int findOpenBrace(char[] sql, int i) {
+  private static int findOpenParenthesis(char[] sql, int i) {
     int posArgs = i;
     while (posArgs < sql.length && sql[posArgs] != '(') {
       posArgs++;
@@ -1383,7 +1446,7 @@ public class Parser {
 
   private static int escapeFunction(char[] sql, int i, StringBuilder newsql, boolean stdStrings) throws SQLException {
     String functionName;
-    int argPos = findOpenBrace(sql, i);
+    int argPos = findOpenParenthesis(sql, i);
     if (argPos < sql.length) {
       functionName = new String(sql, i, argPos - i).trim();
       // extract arguments


=====================================
src/main/java/org/postgresql/core/QueryExecutor.java
=====================================
@@ -335,6 +335,31 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
    */
   int getProtocolVersion();
 
+  /**
+   * Adds a single oid that should be received using binary encoding.
+   *
+   * @param oid The oid to request with binary encoding.
+   */
+  void addBinaryReceiveOid(int oid);
+
+  /**
+   * Remove given oid from the list of oids for binary receive encoding.
+   * <p>Note: the binary receive for the oid can be re-activated later.</p>
+   *
+   * @param oid The oid to request with binary encoding.
+   */
+  void removeBinaryReceiveOid(int oid);
+
+  /**
+   * Gets the oids that should be received using binary encoding.
+   * <p>Note: this returns an unmodifiable set, and its contents might not reflect the current state.</p>
+   *
+   * @return The oids to request with binary encoding.
+   * @deprecated the method returns a copy of the set, so it is not efficient. Use {@link #useBinaryForReceive(int)}
+   */
+  @Deprecated
+  Set<? extends Integer> getBinaryReceiveOids();
+
   /**
    * Sets the oids that should be received using binary encoding.
    *
@@ -342,6 +367,31 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
    */
   void setBinaryReceiveOids(Set<Integer> useBinaryForOids);
 
+  /**
+   * Adds a single oid that should be sent using binary encoding.
+   *
+   * @param oid The oid to send with binary encoding.
+   */
+  void addBinarySendOid(int oid);
+
+  /**
+   * Remove given oid from the list of oids for binary send encoding.
+   * <p>Note: the binary send for the oid can be re-activated later.</p>
+   *
+   * @param oid The oid to send with binary encoding.
+   */
+  void removeBinarySendOid(int oid);
+
+  /**
+   * Gets the oids that should be sent using binary encoding.
+   * <p>Note: this returns an unmodifiable set, and its contents might not reflect the current state.</p>
+   *
+   * @return useBinaryForOids The oids to send with binary encoding.
+   * @deprecated the method returns a copy of the set, so it is not efficient. Use {@link #useBinaryForSend(int)}
+   */
+  @Deprecated
+  Set<? extends Integer> getBinarySendOids();
+
   /**
    * Sets the oids that should be sent using binary encoding.
    *


=====================================
src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
=====================================
@@ -61,6 +61,29 @@ import javax.net.SocketFactory;
  */
 public class ConnectionFactoryImpl extends ConnectionFactory {
 
+  private static class StartupParam {
+    private final String key;
+    private final String value;
+
+    StartupParam(String key, String value) {
+      this.key = key;
+      this.value = value;
+    }
+
+    @Override
+    public String toString() {
+      return this.key + "=" + this.value;
+    }
+
+    public byte[] getEncodedKey() {
+      return this.key.getBytes(StandardCharsets.UTF_8);
+    }
+
+    public byte[] getEncodedValue() {
+      return this.value.getBytes(StandardCharsets.UTF_8);
+    }
+  }
+
   private static final Logger LOGGER = Logger.getLogger(ConnectionFactoryImpl.class.getName());
   private static final int AUTH_REQ_OK = 0;
   private static final int AUTH_REQ_KRB4 = 1;
@@ -173,7 +196,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
         newStream.setNetworkTimeout(socketTimeout * 1000);
       }
 
-      List<String[]> paramList = getParametersForStartup(user, database, info);
+      List<StartupParam> paramList = getParametersForStartup(user, database, info);
       sendStartupPacket(newStream, paramList);
 
       // Do authentication (until AuthenticationOk).
@@ -347,41 +370,41 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
         PSQLState.CONNECTION_UNABLE_TO_CONNECT);
   }
 
-  private List<String[]> getParametersForStartup(String user, String database, Properties info) {
-    List<String[]> paramList = new ArrayList<String[]>();
-    paramList.add(new String[]{"user", user});
-    paramList.add(new String[]{"database", database});
-    paramList.add(new String[]{"client_encoding", "UTF8"});
-    paramList.add(new String[]{"DateStyle", "ISO"});
-    paramList.add(new String[]{"TimeZone", createPostgresTimeZone()});
+  private List<StartupParam> getParametersForStartup(String user, String database, Properties info) {
+    List<StartupParam> paramList = new ArrayList<>();
+    paramList.add(new StartupParam("user", user));
+    paramList.add(new StartupParam("database", database));
+    paramList.add(new StartupParam("client_encoding", "UTF8"));
+    paramList.add(new StartupParam("DateStyle", "ISO"));
+    paramList.add(new StartupParam("TimeZone", createPostgresTimeZone()));
 
     Version assumeVersion = ServerVersion.from(PGProperty.ASSUME_MIN_SERVER_VERSION.getOrDefault(info));
 
     if (assumeVersion.getVersionNum() >= ServerVersion.v9_0.getVersionNum()) {
       // User is explicitly telling us this is a 9.0+ server so set properties here:
-      paramList.add(new String[]{"extra_float_digits", "3"});
+      paramList.add(new StartupParam("extra_float_digits", "3"));
       String appName = PGProperty.APPLICATION_NAME.getOrDefault(info);
       if (appName != null) {
-        paramList.add(new String[]{"application_name", appName});
+        paramList.add(new StartupParam("application_name", appName));
       }
     } else {
       // User has not explicitly told us that this is a 9.0+ server so stick to old default:
-      paramList.add(new String[]{"extra_float_digits", "2"});
+      paramList.add(new StartupParam("extra_float_digits", "2"));
     }
 
     String replication = PGProperty.REPLICATION.getOrDefault(info);
     if (replication != null && assumeVersion.getVersionNum() >= ServerVersion.v9_4.getVersionNum()) {
-      paramList.add(new String[]{"replication", replication});
+      paramList.add(new StartupParam("replication", replication));
     }
 
     String currentSchema = PGProperty.CURRENT_SCHEMA.getOrDefault(info);
     if (currentSchema != null) {
-      paramList.add(new String[]{"search_path", currentSchema});
+      paramList.add(new StartupParam("search_path", currentSchema));
     }
 
     String options = PGProperty.OPTIONS.getOrDefault(info);
     if (options != null) {
-      paramList.add(new String[]{"options", options});
+      paramList.add(new StartupParam("options", options));
     }
 
     return paramList;
@@ -461,6 +484,18 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
     // attempt to acquire a GSS encrypted connection
     LOGGER.log(Level.FINEST, " FE=> GSSENCRequest");
 
+    int gssTimeout = PGProperty.SSL_RESPONSE_TIMEOUT.getInt(info);
+    int currentTimeout = pgStream.getNetworkTimeout();
+
+    // if the current timeout is less than sslTimeout then
+    // use the smaller timeout. We could do something tricky
+    // here to not set it in that case but this is pretty readable
+    if (currentTimeout > 0 && currentTimeout < gssTimeout) {
+      gssTimeout = currentTimeout;
+    }
+
+    pgStream.setNetworkTimeout(gssTimeout);
+
     // Send GSSEncryption request packet
     pgStream.sendInteger4(8);
     pgStream.sendInteger2(1234);
@@ -468,6 +503,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
     pgStream.flush();
     // Now get the response from the backend, one of N, E, S.
     int beresp = pgStream.receiveChar();
+    pgStream.setNetworkTimeout(currentTimeout);
     switch (beresp) {
       case 'E':
         LOGGER.log(Level.FINEST, " <=BE GSSEncrypted Error");
@@ -533,7 +569,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
     LOGGER.log(Level.FINEST, " FE=> SSLRequest");
 
     int sslTimeout = PGProperty.SSL_RESPONSE_TIMEOUT.getInt(info);
-    int currentTimeout = pgStream.getSocket().getSoTimeout();
+    int currentTimeout = pgStream.getNetworkTimeout();
 
     // if the current timeout is less than sslTimeout then
     // use the smaller timeout. We could do something tricky
@@ -542,7 +578,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
       sslTimeout = currentTimeout;
     }
 
-    pgStream.getSocket().setSoTimeout(sslTimeout);
+    pgStream.setNetworkTimeout(sslTimeout);
     // Send SSL request packet
     pgStream.sendInteger4(8);
     pgStream.sendInteger2(1234);
@@ -551,7 +587,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
 
     // Now get the response from the backend, one of N, E, S.
     int beresp = pgStream.receiveChar();
-    pgStream.getSocket().setSoTimeout(currentTimeout);
+    pgStream.setNetworkTimeout(currentTimeout);
 
     switch (beresp) {
       case 'E':
@@ -590,7 +626,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
     }
   }
 
-  private void sendStartupPacket(PGStream pgStream, List<String[]> params)
+  private void sendStartupPacket(PGStream pgStream, List<StartupParam> params)
       throws IOException {
     if (LOGGER.isLoggable(Level.FINEST)) {
       StringBuilder details = new StringBuilder();
@@ -598,9 +634,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
         if (i != 0) {
           details.append(", ");
         }
-        details.append(params.get(i)[0]);
-        details.append("=");
-        details.append(params.get(i)[1]);
+        details.append(params.get(i).toString());
       }
       LOGGER.log(Level.FINEST, " FE=> StartupPacket({0})", details);
     }
@@ -609,8 +643,8 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
     int length = 4 + 4;
     byte[][] encodedParams = new byte[params.size() * 2][];
     for (int i = 0; i < params.size(); ++i) {
-      encodedParams[i * 2] = params.get(i)[0].getBytes(StandardCharsets.UTF_8);
-      encodedParams[i * 2 + 1] = params.get(i)[1].getBytes(StandardCharsets.UTF_8);
+      encodedParams[i * 2] = params.get(i).getEncodedKey();
+      encodedParams[i * 2 + 1] = params.get(i).getEncodedValue();
       length += encodedParams[i * 2].length + 1 + encodedParams[i * 2 + 1].length + 1;
     }
 


=====================================
src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
=====================================
@@ -2912,26 +2912,78 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     return replicationProtocol;
   }
 
+  @Override
+  public void addBinaryReceiveOid(int oid) {
+    synchronized (useBinaryReceiveForOids) {
+      useBinaryReceiveForOids.add(oid);
+    }
+  }
+
+  @Override
+  public void removeBinaryReceiveOid(int oid) {
+    synchronized (useBinaryReceiveForOids) {
+      useBinaryReceiveForOids.remove(oid);
+    }
+  }
+
+  @Override
+  public Set<? extends Integer> getBinaryReceiveOids() {
+    // copy the values to prevent ConcurrentModificationException when reader accesses the elements
+    synchronized (useBinaryReceiveForOids) {
+      return new HashSet<>(useBinaryReceiveForOids);
+    }
+  }
+
   @Override
   public boolean useBinaryForReceive(int oid) {
-    return useBinaryReceiveForOids.contains(oid);
+    synchronized (useBinaryReceiveForOids) {
+      return useBinaryReceiveForOids.contains(oid);
+    }
   }
 
   @Override
   public void setBinaryReceiveOids(Set<Integer> oids) {
-    useBinaryReceiveForOids.clear();
-    useBinaryReceiveForOids.addAll(oids);
+    synchronized (useBinaryReceiveForOids) {
+      useBinaryReceiveForOids.clear();
+      useBinaryReceiveForOids.addAll(oids);
+    }
+  }
+
+  @Override
+  public void addBinarySendOid(int oid) {
+    synchronized (useBinarySendForOids) {
+      useBinarySendForOids.add(oid);
+    }
+  }
+
+  @Override
+  public void removeBinarySendOid(int oid) {
+    synchronized (useBinarySendForOids) {
+      useBinarySendForOids.remove(oid);
+    }
+  }
+
+  @Override
+  public Set<? extends Integer> getBinarySendOids() {
+    // copy the values to prevent ConcurrentModificationException when reader accesses the elements
+    synchronized (useBinarySendForOids) {
+      return new HashSet<>(useBinarySendForOids);
+    }
   }
 
   @Override
   public boolean useBinaryForSend(int oid) {
-    return useBinarySendForOids.contains(oid);
+    synchronized (useBinarySendForOids) {
+      return useBinarySendForOids.contains(oid);
+    }
   }
 
   @Override
   public void setBinarySendOids(Set<Integer> oids) {
-    useBinarySendForOids.clear();
-    useBinarySendForOids.addAll(oids);
+    synchronized (useBinarySendForOids) {
+      useBinarySendForOids.clear();
+      useBinarySendForOids.addAll(oids);
+    }
   }
 
   private void setIntegerDateTimes(boolean state) {


=====================================
src/main/java/org/postgresql/ds/common/BaseDataSource.java
=====================================
@@ -354,6 +354,24 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
     PGProperty.CONNECT_TIMEOUT.set(properties, connectTimeout);
   }
 
+  /**
+   *
+   * @return GSS ResponseTimeout
+   * @see PGProperty#GSS_RESPONSE_TIMEOUT
+   */
+  public int getGssResponseTimeout() {
+    return PGProperty.GSS_RESPONSE_TIMEOUT.getIntNoCheck(properties);
+  }
+
+  /**
+   *
+   * @param gssResponseTimeout gss response timeout
+   * @see PGProperty#GSS_RESPONSE_TIMEOUT
+   */
+  public void setGssResponseTimeout(int gssResponseTimeout) {
+    PGProperty.GSS_RESPONSE_TIMEOUT.set(properties,gssResponseTimeout);
+  }
+
   /**
    *
    * @return SSL ResponseTimeout


=====================================
src/main/java/org/postgresql/jdbc/PgConnection.java
=====================================
@@ -71,6 +71,7 @@ import java.sql.Statement;
 import java.sql.Struct;
 import java.sql.Types;
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -160,6 +161,11 @@ public class PgConnection implements BaseConnection {
   // Default forcebinary option.
   protected boolean forcebinary = false;
 
+  /**
+   * Oids for which binary transfer should be disabled.
+   */
+  private final Set<? extends Integer> binaryDisabledOids;
+
   private int rsHoldability = ResultSet.CLOSE_CURSORS_AT_COMMIT;
   private int savepointId = 0;
   // Connection's autocommit state.
@@ -167,7 +173,7 @@ public class PgConnection implements BaseConnection {
   // Connection's readonly state.
   private boolean readOnly = false;
   // Filter out database objects for which the current user has no privileges granted from the DatabaseMetaData
-  private boolean  hideUnprivilegedObjects ;
+  private final boolean  hideUnprivilegedObjects ;
   // Whether to include error details in logging and exceptions
   private final boolean logServerErrorDetail;
   // Bind String to UNSPECIFIED or VARCHAR?
@@ -261,7 +267,14 @@ public class PgConnection implements BaseConnection {
 
     this.hideUnprivilegedObjects = PGProperty.HIDE_UNPRIVILEGED_OBJECTS.getBoolean(info);
 
-    Set<Integer> binaryOids = getBinaryOids(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<Integer>(binaryOids);
@@ -388,28 +401,51 @@ public class PgConnection implements BaseConnection {
         Oid.UUID));
   }
 
-  private static Set<Integer> getBinaryOids(Properties info) throws PSQLException {
+  /**
+   * Gets all oids for which binary transfer can be enabled.
+   *
+   * @param info properties
+   * @return oids for which binary transfer can be enabled
+   * @throws PSQLException if any oid is not valid
+   */
+  private static Set<Integer> getBinaryEnabledOids(Properties info) throws PSQLException {
+    // check if binary transfer should be enabled for built-in types
     boolean binaryTransfer = PGProperty.BINARY_TRANSFER.getBoolean(info);
-    // Formats that currently have binary protocol support
+    // get formats that currently have binary protocol support
     Set<Integer> binaryOids = new HashSet<Integer>(32);
     if (binaryTransfer) {
       binaryOids.addAll(SUPPORTED_BINARY_OIDS);
     }
-
+    // add all oids which are enabled for binary transfer by the creator of the connection
     String oids = PGProperty.BINARY_TRANSFER_ENABLE.getOrDefault(info);
     if (oids != null) {
       binaryOids.addAll(getOidSet(oids));
     }
-    oids = PGProperty.BINARY_TRANSFER_DISABLE.getOrDefault(info);
-    if (oids != null) {
-      binaryOids.removeAll(getOidSet(oids));
-    }
-
     return binaryOids;
   }
 
-  private static Set<Integer> getOidSet(String oidList) throws PSQLException {
-    Set<Integer> oids = new HashSet<Integer>();
+  /**
+   * Gets all oids for which binary transfer should be disabled.
+   *
+   * @param info properties
+   * @return oids for which binary transfer should be disabled
+   * @throws PSQLException if any oid is not valid
+   */
+  private static Set<? extends Integer> getBinaryDisabledOids(Properties info)
+      throws PSQLException {
+    // check for oids that should explicitly be disabled
+    String oids = PGProperty.BINARY_TRANSFER_DISABLE.getOrDefault(info);
+    if (oids == null) {
+      return Collections.emptySet();
+    }
+    return getOidSet(oids);
+  }
+
+  private static Set<? extends Integer> getOidSet(String oidList) throws PSQLException {
+    if (oidList.isEmpty()) {
+      return Collections.emptySet();
+    }
+    Set<Integer> oids = new HashSet<>();
     StringTokenizer tokenizer = new StringTokenizer(oidList, ",");
     while (tokenizer.hasMoreTokens()) {
       String oid = tokenizer.nextToken();
@@ -434,6 +470,7 @@ public class PgConnection implements BaseConnection {
 
   private final TimestampUtils timestampUtils;
 
+  @Deprecated
   public TimestampUtils getTimestampUtils() {
     return timestampUtils;
   }
@@ -706,14 +743,26 @@ public class PgConnection implements BaseConnection {
     try {
       addDataType(type, Class.forName(name).asSubclass(PGobject.class));
     } catch (Exception e) {
-      throw new RuntimeException("Cannot register new type: " + e);
+      throw new RuntimeException("Cannot register new type " + type, e);
     }
   }
 
   @Override
   public void addDataType(String type, Class<? extends PGobject> klass) throws SQLException {
     checkClosed();
+    // first add the data type to the type cache
     typeCache.addDataType(type, klass);
+    // then check if this type supports binary transfer
+    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);
+      }
+    }
   }
 
   // This initialises the objectTypes hash map


=====================================
src/main/java/org/postgresql/jdbc/PgResultSet.java
=====================================
@@ -48,7 +48,6 @@ import java.io.Reader;
 import java.io.UnsupportedEncodingException;
 import java.math.BigDecimal;
 import java.math.BigInteger;
-import java.math.RoundingMode;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.nio.charset.StandardCharsets;
@@ -597,10 +596,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       if (oid == Oid.TIMESTAMPTZ || oid == Oid.TIMESTAMP) {
         boolean hasTimeZone = oid == Oid.TIMESTAMPTZ;
         TimeZone tz = cal.getTimeZone();
-        return connection.getTimestampUtils().toTimestampBin(tz, castNonNull(row), hasTimeZone);
+        return getTimestampUtils().toTimestampBin(tz, castNonNull(row), hasTimeZone);
       } else if (oid == Oid.TIME) {
         // JDBC spec says getTimestamp of Time and Date must be supported
-        Timestamp tsWithMicros = connection.getTimestampUtils().toTimestampBin(cal.getTimeZone(), castNonNull(row), false);
+        Timestamp tsWithMicros = getTimestampUtils().toTimestampBin(cal.getTimeZone(), castNonNull(row), false);
         // If server sends us a TIME, we ensure java counterpart has date of 1970-01-01
         Timestamp tsUnixEpochDate = new Timestamp(castNonNull(getTime(i, cal)).getTime());
         tsUnixEpochDate.setNanos(tsWithMicros.getNanos());
@@ -608,7 +607,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       } else if (oid == Oid.TIMETZ) {
         TimeZone tz = cal.getTimeZone();
         byte[] timeBytesWithoutTimeZone = Arrays.copyOfRange(castNonNull(row), 0, 8);
-        Timestamp tsWithMicros = connection.getTimestampUtils().toTimestampBin(tz, timeBytesWithoutTimeZone, false);
+        Timestamp tsWithMicros = getTimestampUtils().toTimestampBin(tz, timeBytesWithoutTimeZone, false);
         // If server sends us a TIMETZ, we ensure java counterpart has date of 1970-01-01
         Timestamp tsUnixEpochDate = new Timestamp(castNonNull(getTime(i, cal)).getTime());
         tsUnixEpochDate.setNanos(tsWithMicros.getNanos());
@@ -629,13 +628,13 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     String string = castNonNull(getString(i));
     if (oid == Oid.TIME || oid == Oid.TIMETZ) {
       // If server sends us a TIME, we ensure java counterpart has date of 1970-01-01
-      Timestamp tsWithMicros = connection.getTimestampUtils().toTimestamp(cal, string);
-      Timestamp tsUnixEpochDate = new Timestamp(connection.getTimestampUtils().toTime(cal, string).getTime());
+      Timestamp tsWithMicros = getTimestampUtils().toTimestamp(cal, string);
+      Timestamp tsUnixEpochDate = new Timestamp(getTimestampUtils().toTime(cal, string).getTime());
       tsUnixEpochDate.setNanos(tsWithMicros.getNanos());
       return tsUnixEpochDate;
     }
 
-    return connection.getTimestampUtils().toTimestamp(cal, string);
+    return getTimestampUtils().toTimestamp(cal, string);
 
   }
 
@@ -3376,6 +3375,11 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
         Oid.toString(oid), targetType), PSQLState.DATA_TYPE_MISMATCH);
   }
 
+  private static final float LONG_MAX_FLOAT = StrictMath.nextDown(Long.MAX_VALUE);
+  private static final float LONG_MIN_FLOAT = StrictMath.nextUp(Long.MIN_VALUE);
+  private static final double LONG_MAX_DOUBLE = StrictMath.nextDown((double)Long.MAX_VALUE);
+  private static final double LONG_MIN_DOUBLE = StrictMath.nextUp((double)Long.MIN_VALUE);
+
   /**
    * <p>Converts any numeric binary field to long value.</p>
    *
@@ -3411,15 +3415,34 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
         val = ByteConverter.int8(bytes, 0);
         break;
       case Oid.FLOAT4:
-        val = (long) ByteConverter.float4(bytes, 0);
+        float f = ByteConverter.float4(bytes, 0);
+        // for float values we know to be within values of long, just cast directly to long
+        if (f <= LONG_MAX_FLOAT && f >= LONG_MIN_FLOAT) {
+          val = (long) f;
+        } else {
+          throw new PSQLException(GT.tr("Bad value for type {0} : {1}", targetType, f),
+              PSQLState.NUMERIC_VALUE_OUT_OF_RANGE);
+        }
         break;
       case Oid.FLOAT8:
-        val = (long) ByteConverter.float8(bytes, 0);
+        double d = ByteConverter.float8(bytes, 0);
+        // for double values within the values of a long, just directly cast to long
+        if (d <= LONG_MAX_DOUBLE && d >= LONG_MIN_DOUBLE) {
+          val = (long) d;
+        } else {
+          throw new PSQLException(GT.tr("Bad value for type {0} : {1}", targetType, d),
+              PSQLState.NUMERIC_VALUE_OUT_OF_RANGE);
+        }
         break;
       case Oid.NUMERIC:
         Number num = ByteConverter.numeric(bytes);
-        if (num instanceof  BigDecimal) {
-          val = ((BigDecimal) num).setScale(0 , RoundingMode.DOWN).longValueExact();
+        BigInteger i = ((BigDecimal) num).toBigInteger();
+        int gt = i.compareTo(LONGMAX);
+        int lt = i.compareTo(LONGMIN);
+
+        if (gt > 0 || lt < 0) {
+          throw new PSQLException(GT.tr("Bad value for type {0} : {1}", "long", num),
+              PSQLState.NUMERIC_VALUE_OUT_OF_RANGE);
         } else {
           val = num.longValue();
         }


=====================================
src/main/java/org/postgresql/jdbc/TypeInfoCache.java
=====================================
@@ -105,7 +105,8 @@ public class TypeInfoCache implements TypeInfo {
           Oid.TIMESTAMPTZ_ARRAY},
       {"refcursor", Oid.REF_CURSOR, Types.REF_CURSOR, "java.sql.ResultSet", Oid.REF_CURSOR_ARRAY},
       {"json", Oid.JSON, Types.OTHER, "org.postgresql.util.PGobject", Oid.JSON_ARRAY},
-      {"point", Oid.POINT, Types.OTHER, "org.postgresql.geometric.PGpoint", Oid.POINT_ARRAY}
+      {"point", Oid.POINT, Types.OTHER, "org.postgresql.geometric.PGpoint", Oid.POINT_ARRAY},
+      {"box", Oid.BOX, Types.OTHER, "org.postgresql.geometric.PGBox", Oid.BOX_ARRAY}
   };
 
   /**
@@ -181,6 +182,9 @@ public class TypeInfoCache implements TypeInfo {
     // the box datatype and it's not a JDBC core type.
     //
     Character delim = ',';
+    if (pgTypeName.equals("box")) {
+      delim = ';';
+    }
     arrayOidToDelimiter.put(oid, delim);
     arrayOidToDelimiter.put(arrayOid, delim);
 


=====================================
src/main/java/org/postgresql/util/ByteConverter.java
=====================================
@@ -127,8 +127,8 @@ public class ByteConverter {
       throw new IllegalArgumentException("number of bytes should be at-least 8");
     }
 
-    //number of 2-byte shorts representing 4 decimal digits
-    short len = ByteConverter.int2(bytes, pos);
+    //number of 2-byte shorts representing 4 decimal digits - should be treated as unsigned
+    int len = (ByteConverter.int2(bytes, pos) & 0xFFFF);
     //0 based number of 4 decimal digits (i.e. 2-byte shorts) before the decimal
     //a value <= 0 indicates an absolute value < 1.
     short weight = ByteConverter.int2(bytes, pos + 2);


=====================================
src/main/java/org/postgresql/util/DriverInfo.java
=====================================
@@ -16,13 +16,13 @@ public final class DriverInfo {
   // Driver name
   public static final String DRIVER_NAME = "PostgreSQL JDBC Driver";
   public static final String DRIVER_SHORT_NAME = "PgJDBC";
-  public static final String DRIVER_VERSION = "42.5.1";
+  public static final String DRIVER_VERSION = "42.5.3";
   public static final String DRIVER_FULL_NAME = DRIVER_NAME + " " + DRIVER_VERSION;
 
   // Driver version
   public static final int MAJOR_VERSION = 42;
   public static final int MINOR_VERSION = 5;
-  public static final int PATCH_VERSION = 1;
+  public static final int PATCH_VERSION = 3;
 
   // JDBC specification
   public static final String JDBC_VERSION = "4.2";


=====================================
src/main/java/org/postgresql/util/StreamWrapper.java
=====================================
@@ -52,7 +52,7 @@ public class StreamWrapper {
 
       if (memoryLength == -1) {
         final int diskLength;
-        final File tempFile = Files.createTempFile(TEMP_FILE_PREFIX, null).toFile();
+        final File tempFile = Files.createTempFile(TEMP_FILE_PREFIX, ".tmp").toFile();
         FileOutputStream diskOutputStream = new FileOutputStream(tempFile);
         diskOutputStream.write(rawData);
         try {


=====================================
src/main/resources/META-INF/MANIFEST.MF
=====================================
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Implementation-Title: PostgreSQL JDBC Driver
 Bundle-License: BSD-2-Clause
 Automatic-Module-Name: org.postgresql.jdbc
-Implementation-Version: 42.5.1
+Implementation-Version: 42.5.3
 Specification-Vendor: Oracle Corporation
 Specification-Title: JDBC
 Implementation-Vendor-Id: org.postgresql


=====================================
src/test/java/org/postgresql/core/ParserTest.java
=====================================
@@ -284,4 +284,18 @@ public class ParserTest {
     Assert.assertFalse("No returning keyword should be present", command.isReturningKeywordPresent());
     Assert.assertEquals(SqlCommandType.ALTER, command.getType());
   }
+
+  @Test
+  public void testParseV14functions() throws SQLException {
+    String[] returningColumns = {"*"};
+    String query = "CREATE OR REPLACE FUNCTION asterisks(n int)\n"
+        + "  RETURNS SETOF text\n"
+        + "  LANGUAGE sql IMMUTABLE STRICT PARALLEL SAFE\n"
+        + "BEGIN ATOMIC\n"
+        + "SELECT repeat('*', g) FROM generate_series (1, n) g; \n"
+        + "END;";
+    List<NativeQuery> qry = Parser.parseJdbcSql(query, true, true, true, true, true, returningColumns);
+    Assert.assertNotNull(qry);
+    Assert.assertEquals("There should only be one query returned here", 1, qry.size());
+  }
 }


=====================================
src/test/java/org/postgresql/test/core/QueryExecutorTest.java
=====================================
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.core;
+
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.QueryExecutor;
+import org.postgresql.test.jdbc2.BaseTest4;
+
+import org.junit.Test;
+
+import java.sql.SQLException;
+import java.util.Set;
+
+/**
+ * TestCase to test handling of binary types.
+ */
+public class QueryExecutorTest extends BaseTest4 {
+  /**
+   * Make sure the functions for adding binary transfer OIDs for custom types are correct.
+   *
+   * @throws SQLException if a database error occurs
+   */
+  @Test
+  public void testBinaryTransferOids() throws SQLException {
+    QueryExecutor queryExecutor = con.unwrap(BaseConnection.class).getQueryExecutor();
+    // get current OIDs (make a copy of them)
+    @SuppressWarnings("deprecation")
+    Set<? extends Integer> oidsReceive = queryExecutor.getBinaryReceiveOids();
+    @SuppressWarnings("deprecation")
+    Set<? extends Integer> oidsSend = queryExecutor.getBinarySendOids();
+    // add a new OID to be transferred as binary data
+    int customTypeOid = 91716;
+    assertBinaryForReceive(customTypeOid, false,
+        () -> "Custom type OID should not be binary for receive by default");
+    // first for receiving
+    queryExecutor.addBinaryReceiveOid(customTypeOid);
+    // Verify
+    assertBinaryForReceive(customTypeOid, true,
+        () -> "Just added oid via addBinaryReceiveOid");
+    assertBinaryForSend(customTypeOid, false,
+        () -> "Just added oid via addBinaryReceiveOid");
+    for (int oid : oidsReceive) {
+      assertBinaryForReceive(oid, true,
+          () -> "Previously registered BinaryReceiveOids should be intact after "
+              + "addBinaryReceiveOid(" + customTypeOid + ")");
+    }
+    for (int oid : oidsSend) {
+      assertBinaryForSend(oid, true,
+          () -> "Previously registered BinarySendOids should be intact after "
+              + "addBinaryReceiveOid(" + customTypeOid + ")");
+    }
+    // then for sending
+    queryExecutor.addBinarySendOid(customTypeOid);
+    // check new OID
+    assertBinaryForReceive(customTypeOid, true, () -> "added oid via addBinaryReceiveOid and "
+        + "addBinarySendOid");
+    assertBinaryForSend(customTypeOid, true, () -> "added oid via addBinaryReceiveOid and "
+        + "addBinarySendOid");
+    for (int oid : oidsReceive) {
+      assertBinaryForReceive(oid, true, () -> "Previously registered BinaryReceiveOids should be "
+          + "intact after addBinaryReceiveOid(" + customTypeOid + ") and addBinarySendOid(" + customTypeOid + ")");
+    }
+    for (int oid : oidsSend) {
+      assertBinaryForSend(oid, true, () -> "Previously registered BinarySendOids should be intact"
+          + " after addBinaryReceiveOid(" + customTypeOid + ")");
+    }
+  }
+}


=====================================
src/test/java/org/postgresql/test/jdbc2/BaseTest4.java
=====================================
@@ -5,8 +5,11 @@
 
 package org.postgresql.test.jdbc2;
 
+import static org.junit.Assert.assertEquals;
+
 import org.postgresql.PGConnection;
 import org.postgresql.PGProperty;
+import org.postgresql.core.BaseConnection;
 import org.postgresql.core.Oid;
 import org.postgresql.core.Version;
 import org.postgresql.jdbc.PreferQueryMode;
@@ -20,6 +23,7 @@ import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.Locale;
 import java.util.Properties;
+import java.util.function.Supplier;
 
 public class BaseTest4 {
 
@@ -136,4 +140,13 @@ public class BaseTest4 {
     Assume.assumeTrue(TestUtil.haveMinimumServerVersion(con, version));
   }
 
+  protected void assertBinaryForReceive(int oid, boolean expected, Supplier<String> message) throws SQLException {
+    assertEquals(message.get() + ", useBinaryForReceive(oid=" + oid + ")", expected,
+        con.unwrap(BaseConnection.class).getQueryExecutor().useBinaryForReceive(oid));
+  }
+
+  protected void assertBinaryForSend(int oid, boolean expected, Supplier<String> message) throws SQLException {
+    assertEquals(message.get() + ", useBinaryForSend(oid=" + oid + ")", expected,
+        con.unwrap(BaseConnection.class).getQueryExecutor().useBinaryForSend(oid));
+  }
 }


=====================================
src/test/java/org/postgresql/test/jdbc2/ConnectTimeoutTest.java
=====================================
@@ -8,6 +8,7 @@ package org.postgresql.test.jdbc2;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import org.postgresql.PGProperty;
 import org.postgresql.test.TestUtil;
 
 import org.junit.Assume;
@@ -34,10 +35,10 @@ public class ConnectTimeoutTest {
   @Test
   public void testTimeout() {
     final Properties props = new Properties();
-    props.setProperty("user", "test");
-    props.setProperty("password", "test");
+    PGProperty.USER.set(props,TestUtil.getUser());
+    PGProperty.PASSWORD.set(props, TestUtil.getPassword());
     // with 0 (default value) it hangs for about 60 seconds (platform dependent)
-    props.setProperty("connectTimeout", Integer.toString(CONNECT_TIMEOUT));
+    PGProperty.CONNECT_TIMEOUT.set(props, CONNECT_TIMEOUT);
 
     final long startTime = System.currentTimeMillis();
     try {


=====================================
src/test/java/org/postgresql/test/jdbc2/CustomTypeWithBinaryTransferTest.java
=====================================
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc2;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.postgresql.PGConnection;
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.Oid;
+import org.postgresql.core.QueryExecutor;
+import org.postgresql.jdbc.PreferQueryMode;
+import org.postgresql.test.TestUtil;
+import org.postgresql.util.PGBinaryObject;
+import org.postgresql.util.PGobject;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+
+/**
+ * TestCase to test handling of binary types for custom objects.
+ */
+ at RunWith(Parameterized.class)
+public class CustomTypeWithBinaryTransferTest extends BaseTest4 {
+  // define an oid of a binary type for testing, POINT is used here as it already exists in the
+  // database and requires no complex own type definition
+  private static final int CUSTOM_TYPE_OID = Oid.POINT;
+
+  public CustomTypeWithBinaryTransferTest(BinaryMode binaryMode) {
+    setBinaryMode(binaryMode);
+  }
+
+  @Parameterized.Parameters(name = "binary = {0}")
+  public static Iterable<Object[]> data() {
+    Collection<Object[]> ids = new ArrayList<>();
+    for (BinaryMode binaryMode : BinaryMode.values()) {
+      ids.add(new Object[]{binaryMode});
+    }
+    return ids;
+  }
+
+  /**
+   * Set up the fixture for this testcase: the tables for this test.
+   *
+   * @throws SQLException if a database error occurs
+   */
+  @BeforeClass
+  public static void createTestTable() throws SQLException {
+    try (Connection con = TestUtil.openDB()) {
+      TestUtil.createTable(con, "test_binary_pgobject", "id integer,name text,geom point");
+    }
+  }
+
+  /**
+   * Tear down the fixture for this test case.
+   *
+   * @throws SQLException if a database error occurs
+   */
+  @AfterClass
+  public static void dropTestTable() throws SQLException {
+    try (Connection con = TestUtil.openDB()) {
+      TestUtil.dropTable(con, "test_binary_pgobject");
+    }
+  }
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    QueryExecutor queryExecutor = con.unwrap(BaseConnection.class).getQueryExecutor();
+    queryExecutor.removeBinarySendOid(CUSTOM_TYPE_OID);
+    queryExecutor.removeBinaryReceiveOid(CUSTOM_TYPE_OID);
+    assertBinaryForReceive(CUSTOM_TYPE_OID, false,
+        () -> "Binary transfer for point type should be disabled since we've deactivated it in "
+            + "updateProperties");
+
+    assertBinaryForSend(CUSTOM_TYPE_OID, false,
+        () -> "Binary transfer for point type should be disabled since we've deactivated it in "
+            + "updateProperties");
+    try (Statement st = con.createStatement()) {
+      st.execute("DELETE FROM test_binary_pgobject");
+      st.execute("INSERT INTO test_binary_pgobject(id,name,geom) values(1,'Test',Point(1,2))");
+    }
+  }
+
+  /**
+   * Make sure custom binary types are handled automatically.
+   *
+   * @throws SQLException if a database error occurs
+   */
+  @Test
+  public void testCustomBinaryTypes() throws SQLException {
+    PGConnection pgconn = con.unwrap(PGConnection.class);
+
+    // make sure the test type implements PGBinaryObject
+    assertTrue("test type should implement PGBinaryObject",
+        PGBinaryObject.class.isAssignableFrom(TestCustomType.class));
+
+    // now define a custom type, which will add it to the binary sent/received OIDs (if the type
+    // implements PGBinaryObject)
+    pgconn.addDataType("point", TestCustomType.class);
+    // check if the type was marked for binary transfer
+    if (preferQueryMode != PreferQueryMode.SIMPLE) {
+      assertBinaryForReceive(CUSTOM_TYPE_OID, true,
+          () -> "Binary transfer for point type should be activated by addDataType(..., "
+              + "TestCustomType.class)");
+      assertBinaryForSend(CUSTOM_TYPE_OID, true,
+          () -> "Binary transfer for point type should be activated by addDataType(..., "
+              + "TestCustomType.class)");
+    }
+
+    TestCustomType co;
+    // Try with PreparedStatement
+    try (PreparedStatement pst = con.prepareStatement("SELECT geom FROM test_binary_pgobject WHERE id=?")) {
+      pst.setInt(1, 1);
+      try (ResultSet rs = pst.executeQuery()) {
+        assertTrue("rs.next()", rs.next());
+        Object o = rs.getObject(1);
+        co = (TestCustomType) o;
+        // now binary transfer should be working
+        if (preferQueryMode == PreferQueryMode.SIMPLE) {
+          assertEquals(
+              "reading via prepared statement: TestCustomType.wasReadBinary() should use text encoding since preferQueryMode=SIMPLE",
+              "text",
+              co.wasReadBinary() ? "binary" : "text");
+        } else {
+          assertEquals(
+              "reading via prepared statement: TestCustomType.wasReadBinary() should use match binary mode requested by the test",
+              binaryMode == BinaryMode.FORCE ? "binary" : "text",
+              co.wasReadBinary() ? "binary" : "text");
+        }
+      }
+    }
+
+    // ensure flag is still unset
+    assertFalse("wasWrittenBinary should be false since we have not written the object yet",
+        co.wasWrittenBinary());
+    // now try to write it
+    try (PreparedStatement pst =
+             con.prepareStatement("INSERT INTO test_binary_pgobject(id,geom) VALUES(?,?)")) {
+      pst.setInt(1, 2);
+      pst.setObject(2, co);
+      pst.executeUpdate();
+      // make sure transfer was binary
+      if (preferQueryMode == PreferQueryMode.SIMPLE) {
+        assertEquals(
+            "writing via prepared statement: TestCustomType.wasWrittenBinary() should use text encoding since preferQueryMode=SIMPLE",
+            "text",
+            co.wasWrittenBinary() ? "binary" : "text");
+      } else {
+        assertEquals(
+            "writing via prepared statement: TestCustomType.wasWrittenBinary() should use match binary mode requested by the test",
+            binaryMode == BinaryMode.FORCE ? "binary" : "text",
+            co.wasWrittenBinary() ? "binary" : "text");
+      }
+    }
+  }
+
+  /**
+   * Custom type that supports binary format.
+   */
+  @SuppressWarnings("serial")
+  public static class TestCustomType extends PGobject implements PGBinaryObject {
+    private byte /* @Nullable */ [] byteValue;
+    private boolean wasReadBinary;
+    private boolean wasWrittenBinary;
+
+    @Override
+    public /* @Nullable */ String getValue() {
+      // set flag
+      this.wasWrittenBinary = false;
+      return super.getValue();
+    }
+
+    @Override
+    public int lengthInBytes() {
+      if (byteValue != null) {
+        return byteValue.length;
+      } else {
+        return 0;
+      }
+    }
+
+    @Override
+    public void setByteValue(byte[] value, int offset) throws SQLException {
+      this.wasReadBinary = true;
+      // remember the byte value
+      byteValue = new byte[value.length - offset];
+      System.arraycopy(value, offset, byteValue, 0, byteValue.length);
+    }
+
+    @Override
+    public void setValue(/* @Nullable */ String value) throws SQLException {
+      super.setValue(value);
+      // set flag
+      this.wasReadBinary = false;
+    }
+
+    @Override
+    public void toBytes(byte[] bytes, int offset) {
+      if (byteValue != null) {
+        // make sure array is large enough
+        if ((bytes.length - offset) <= byteValue.length) {
+          // copy data
+          System.arraycopy(byteValue, 0, bytes, offset, byteValue.length);
+        } else {
+          throw new IllegalArgumentException(
+              "byte array is too small, expected: " + byteValue.length + " got: "
+                  + (bytes.length - offset));
+        }
+      } else {
+        throw new IllegalStateException("no geometry has been set");
+      }
+      // set flag
+      this.wasWrittenBinary = true;
+    }
+
+    /**
+     * Checks, if this type was read in binary mode.
+     *
+     * @return true for binary mode, else false
+     */
+    public boolean wasReadBinary() {
+      return this.wasReadBinary;
+    }
+
+    /**
+     * Checks, if this type was written in binary mode.
+     *
+     * @return true for binary mode, else false
+     */
+    public boolean wasWrittenBinary() {
+      return this.wasWrittenBinary;
+    }
+  }
+}


=====================================
src/test/java/org/postgresql/test/jdbc2/LoginTimeoutTest.java
=====================================
@@ -8,6 +8,7 @@ package org.postgresql.test.jdbc2;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
+import org.postgresql.PGProperty;
 import org.postgresql.test.TestUtil;
 
 import org.junit.Before;
@@ -33,9 +34,9 @@ public class LoginTimeoutTest {
   @Test
   public void testIntTimeout() throws Exception {
     Properties props = new Properties();
-    props.setProperty("user", TestUtil.getUser());
-    props.setProperty("password", TestUtil.getPassword());
-    props.setProperty("loginTimeout", "10");
+    PGProperty.USER.set(props,TestUtil.getUser());
+    PGProperty.PASSWORD.set(props, TestUtil.getPassword());
+    PGProperty.LOGIN_TIMEOUT.set(props, 10);
 
     Connection conn = DriverManager.getConnection(TestUtil.getURL(), props);
     conn.close();


=====================================
src/test/java/org/postgresql/test/jdbc2/ResultSetTest.java
=====================================
@@ -23,14 +23,23 @@ import org.junit.runners.Parameterized;
 
 import java.lang.reflect.Field;
 import java.math.BigDecimal;
+import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.sql.Timestamp;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Locale;
 import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /*
  * ResultSet tests.
@@ -95,16 +104,26 @@ public class ResultSetTest extends BaseTest4 {
     stmt.executeUpdate("INSERT INTO testboolstring VALUES('1.0', null)");
     stmt.executeUpdate("INSERT INTO testboolstring VALUES('0.0', null)");
 
-    TestUtil.createTable(con, "testboolfloat", "a float4, b boolean");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES('1.0'::real, true)");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES('0.0'::real, false)");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(1.000::real, true)");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(0.000::real, false)");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES('1.001'::real, null)");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES('-1.001'::real, null)");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(123.4::real, null)");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(1.234e2::real, null)");
-    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(100.00e-2::real, true)");
+    TestUtil.createTable(con, "testboolfloat", "i int, a float4, b boolean");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(1, '1.0'::real, true)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(2, '0.0'::real, false)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(3, 1.000::real, true)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(4, 0.000::real, false)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(5, '1.001'::real, null)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(6, '-1.001'::real, null)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(7, 123.4::real, null)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(8, 1.234e2::real, null)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(9, 100.00e-2::real, true)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(10, '9223371487098961921', null)");
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(11, '10223372036850000000', null)");
+    String floatVal = Float.toString(StrictMath.nextDown(Long.MAX_VALUE - 1));
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(12, " + floatVal + ", null)");
+    floatVal = Float.toString(StrictMath.nextDown(Long.MAX_VALUE + 1));
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(13, " + floatVal + ", null)");
+    floatVal = Float.toString(StrictMath.nextUp(Long.MIN_VALUE - 1));
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(14, " + floatVal + ", null)");
+    floatVal = Float.toString(StrictMath.nextUp(Long.MIN_VALUE + 1));
+    stmt.executeUpdate("INSERT INTO testboolfloat VALUES(15, " + floatVal + ", null)");
 
     TestUtil.createTable(con, "testboolint", "a bigint, b boolean");
     stmt.executeUpdate("INSERT INTO testboolint VALUES(1, true)");
@@ -112,45 +131,50 @@ public class ResultSetTest extends BaseTest4 {
     stmt.executeUpdate("INSERT INTO testboolint VALUES(-1, null)");
     stmt.executeUpdate("INSERT INTO testboolint VALUES(9223372036854775807, null)");
     stmt.executeUpdate("INSERT INTO testboolint VALUES(-9223372036854775808, null)");
+
     // End Boolean Tests
 
     // TestUtil.createTable(con, "testbit", "a bit");
 
-    TestUtil.createTable(con, "testnumeric", "a numeric");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('1.0')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('0.0')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-1.0')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('1.2')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-2.5')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('0.000000000000000000000000000990')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('10.0000000000099')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('.10000000000000')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('.10')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('1.10000000000000')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('99999.2')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('99999')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-99999.2')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-99999')");
+    TestUtil.createTable(con, "testnumeric", "t text, a numeric");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('1.0', '1.0')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('0.0', '0.0')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-1.0', '-1.0')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('1.2', '1.2')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-2.5', '-2.5')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('0.000000000000000000000000000990', '0.000000000000000000000000000990')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('10.0000000000099', '10.0000000000099')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('.10000000000000', '.10000000000000')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('.10', '.10')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('1.10000000000000', '1.10000000000000')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('99999.2', '99999.2')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('99999', '99999')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-99999.2', '-99999.2')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-99999', '-99999')");
 
     // Integer.MaxValue
-    stmt.execute("INSERT INTO testnumeric VALUES('2147483647')");
+    stmt.execute("INSERT INTO testnumeric VALUES('2147483647', '2147483647')");
 
     // Integer.MinValue
-    stmt.execute("INSERT INTO testnumeric VALUES('-2147483648')");
+    stmt.execute("INSERT INTO testnumeric VALUES( '-2147483648', '-2147483648')");
 
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('2147483648')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-2147483649')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('2147483648', '2147483648')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-2147483649', '-2147483649')");
 
     // Long.MaxValue
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('9223372036854775807')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('9223372036854775807','9223372036854775807')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('9223372036854775807.9', '9223372036854775807.9')");
 
     // Long.MinValue
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-9223372036854775808')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-9223372036854775808', '-9223372036854775808')");
+
+    // Long.MaxValue +1
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('9223372036854775808', '9223372036854775808')");
 
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('9223372036854775808')");
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-9223372036854775809')");
+    // Long.Minvalue -1
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('-9223372036854775809', '-9223372036854775809')");
 
-    stmt.executeUpdate("INSERT INTO testnumeric VALUES('10223372036850000000')");
+    stmt.executeUpdate("INSERT INTO testnumeric VALUES('10223372036850000000', '10223372036850000000')");
 
     TestUtil.createTable(con, "testpgobject", "id integer NOT NULL, d date, PRIMARY KEY (id)");
     stmt.execute("INSERT INTO testpgobject VALUES(1, '2010-11-3')");
@@ -427,7 +451,7 @@ public class ResultSetTest extends BaseTest4 {
 
   @Test
   public void testgetByte() throws SQLException {
-    ResultSet rs = con.createStatement().executeQuery("select * from testnumeric");
+    ResultSet rs = con.createStatement().executeQuery("select a from testnumeric");
 
     assertTrue(rs.next());
     assertEquals(1, rs.getByte(1));
@@ -463,7 +487,8 @@ public class ResultSetTest extends BaseTest4 {
       try {
         rs.getByte(1);
         fail("Exception expected.");
-      } catch (Exception e) {
+      } catch (SQLException e) {
+        assertEquals(e.getSQLState(),"22003");
       }
     }
     rs.close();
@@ -471,7 +496,7 @@ public class ResultSetTest extends BaseTest4 {
 
   @Test
   public void testgetShort() throws SQLException {
-    ResultSet rs = con.createStatement().executeQuery("select * from testnumeric");
+    ResultSet rs = con.createStatement().executeQuery("select a from testnumeric");
 
     assertTrue(rs.next());
     assertEquals(1, rs.getShort(1));
@@ -507,7 +532,7 @@ public class ResultSetTest extends BaseTest4 {
       try {
         rs.getShort(1);
         fail("Exception expected.");
-      } catch (Exception e) {
+      } catch (SQLException e) {
       }
     }
     rs.close();
@@ -515,7 +540,7 @@ public class ResultSetTest extends BaseTest4 {
 
   @Test
   public void testgetInt() throws SQLException {
-    ResultSet rs = con.createStatement().executeQuery("select * from testnumeric");
+    ResultSet rs = con.createStatement().executeQuery("select a from testnumeric");
 
     assertTrue(rs.next());
     assertEquals(1, rs.getInt(1));
@@ -569,158 +594,330 @@ public class ResultSetTest extends BaseTest4 {
       try {
         rs.getInt(1);
         fail("Exception expected." + rs.getString(1));
-      } catch (Exception e) {
+      } catch (SQLException e) {
       }
     }
     rs.close();
+    // test for Issue #2748
+    rs = con.createStatement().executeQuery("select 2.0 :: double precision");
+    assertTrue(rs.next());
+    assertEquals(2, rs.getInt(1));
+    rs.close();
+
   }
 
   @Test
   public void testgetLong() throws SQLException {
-    ResultSet rs = con.createStatement().executeQuery("select * from testnumeric");
+    ResultSet rs = null;
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '1.0'");
     assertTrue(rs.next());
     assertEquals(1, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '0.0'");
     assertTrue(rs.next());
     assertEquals(0, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-1.0'");
     assertTrue(rs.next());
     assertEquals(-1, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '1.2'");
     assertTrue(rs.next());
     assertEquals(1, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-2.5'");
     assertTrue(rs.next());
     assertEquals(-2, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '0.000000000000000000000000000990'");
     assertTrue(rs.next());
     assertEquals(0, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '10.0000000000099'");
     assertTrue(rs.next());
     assertEquals(10, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '.10000000000000'");
     assertTrue(rs.next());
     assertEquals(0, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '.10'");
     assertTrue(rs.next());
     assertEquals(0, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '1.10000000000000'");
     assertTrue(rs.next());
     assertEquals(1, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '99999.2'");
     assertTrue(rs.next());
     assertEquals(99999, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '99999'");
     assertTrue(rs.next());
     assertEquals(99999, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-99999.2'");
     assertTrue(rs.next());
     assertEquals(-99999, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-99999'");
     assertTrue(rs.next());
     assertEquals(-99999, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '2147483647'");
     assertTrue(rs.next());
     assertEquals((Integer.MAX_VALUE), rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-2147483648'");
     assertTrue(rs.next());
     assertEquals((Integer.MIN_VALUE), rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '2147483648'");
     assertTrue(rs.next());
     assertEquals(((long) Integer.MAX_VALUE) + 1, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-2147483649'");
     assertTrue(rs.next());
     assertEquals(((long) Integer.MIN_VALUE) - 1, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '9223372036854775807'");
     assertTrue(rs.next());
     assertEquals(Long.MAX_VALUE, rs.getLong(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '9223372036854775807.9'");
+    assertTrue(rs.next());
+    assertEquals(Long.MAX_VALUE, rs.getLong(1));
+    rs.close();
+
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-9223372036854775808'");
     assertTrue(rs.next());
     assertEquals(Long.MIN_VALUE, rs.getLong(1));
+    rs.close();
 
-    while (rs.next()) {
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '9223372036854775808'");
+    assertTrue(rs.next());
+    try {
+      rs.getLong(1);
+      fail("Exception expected. " + rs.getString(1));
+    } catch (SQLException e) {
+    }
+    rs.close();
+
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-9223372036854775809'");
+    assertTrue(rs.next());
+    try {
+      rs.getLong(1);
+      fail("Exception expected. " + rs.getString(1));
+    } catch (SQLException e) {
+    }
+    rs.close();
+
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '10223372036850000000'");
+    assertTrue(rs.next());
+    try {
+      rs.getLong(1);
+      fail("Exception expected. " + rs.getString(1));
+    } catch (SQLException e) {
+    }
+    rs.close();
+
+    rs = con.createStatement().executeQuery("select i, a from testboolfloat order by i");
+
+    assertTrue(rs.next());
+    assertEquals(1, rs.getLong(2));
+
+    assertTrue(rs.next());
+    assertEquals(0, rs.getLong(2));
+
+    assertTrue(rs.next());
+    assertEquals(1, rs.getLong(2));
+
+    assertTrue(rs.next());
+    assertEquals(0, rs.getLong(2));
+
+    assertTrue(rs.next());
+    assertEquals(1, rs.getLong(2));
+
+    assertTrue(rs.next());
+    assertEquals(-1, rs.getLong(2));
+
+    assertTrue(rs.next());
+    assertEquals(123, rs.getLong(2));
+
+    assertTrue(rs.next());
+    assertEquals(123, rs.getLong(2));
+
+    assertTrue(rs.next());
+    assertEquals(1, rs.getLong(2));
+
+    assertTrue(rs.next());
+    // the string value from database trims the significant digits, leading to larger variance than binary
+    // the liberica jdk gets similar variance, even in forced binary mode
+    assertEquals(9223371487098961921.0, rs.getLong(2), 1.0e11);
+
+    assertTrue(rs.next());
+    do {
       try {
-        rs.getLong(1);
-        fail("Exception expected." + rs.getString(1));
-      } catch (Exception e) {
+        int row = rs.getInt(1);
+        long l = rs.getLong(2);
+        if ( row == 12 ) {
+          assertEquals(9223371487098961920.0, l, 1.0e11);
+        } else if ( row == 15 ) {
+          assertEquals(-9223371487098961920.0, l, 1.0e11);
+        } else {
+          fail("Exception expected." + rs.getString(2));
+        }
+      } catch (SQLException e) {
       }
-    }
+    } while (rs.next());
+
     rs.close();
   }
 
   @Test
   public void testgetBigDecimal() throws SQLException {
-    ResultSet rs = con.createStatement().executeQuery("select * from testnumeric");
+    ResultSet rs = null;
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '1.0'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(1.0), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '0.0'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(0.0), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-1.0'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(-1.0), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '1.2'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(1.2), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-2.5'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(-2.5), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '0.000000000000000000000000000990'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("0.000000000000000000000000000990"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '10.0000000000099'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("10.0000000000099"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '.10000000000000'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("0.10000000000000"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '.10'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("0.10"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '1.10000000000000'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("1.10000000000000"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '99999.2'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(99999.2), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '99999'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(99999), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-99999.2'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(-99999.2), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-99999'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(-99999), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '2147483647'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(2147483647), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-2147483648'");
     assertTrue(rs.next());
     assertEquals(BigDecimal.valueOf(-2147483648), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '2147483648'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("2147483648"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-2147483649'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("-2147483649"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '9223372036854775807'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("9223372036854775807"), rs.getBigDecimal(1));
+    rs.close();
+
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '9223372036854775807.9'");
+    assertTrue(rs.next());
+    assertEquals(new BigDecimal("9223372036854775807.9"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-9223372036854775808'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("-9223372036854775808"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '9223372036854775808'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("9223372036854775808"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '-9223372036854775809'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("-9223372036854775809"), rs.getBigDecimal(1));
+    rs.close();
 
+    rs = con.createStatement().executeQuery("select a from testnumeric where t = '10223372036850000000'");
     assertTrue(rs.next());
     assertEquals(new BigDecimal("10223372036850000000"), rs.getBigDecimal(1));
+    rs.close();
   }
 
   @Test
@@ -1152,4 +1349,50 @@ public class ResultSetTest extends BaseTest4 {
     return null;
   }
 
+  private static class SelectTimestampManyTimes implements Callable<Integer> {
+
+    private final Connection connection;
+    private final int expectedYear;
+
+    protected SelectTimestampManyTimes(Connection connection, int expectedYear) {
+      this.connection = connection;
+      this.expectedYear = expectedYear;
+    }
+
+    @Override
+    public Integer call() throws SQLException {
+      int year = expectedYear;
+      try (Statement statement = connection.createStatement()) {
+        for (int i = 0; i < 10; i++) {
+          try (ResultSet resultSet = statement.executeQuery(
+              String.format("SELECT unnest(array_fill('8/10/%d'::timestamp, ARRAY[%d]))",
+                  expectedYear, 500))) {
+            while (resultSet.next()) {
+              Timestamp d = resultSet.getTimestamp(1);
+              year = 1900 + d.getYear();
+              if (year != expectedYear) {
+                return year;
+              }
+            }
+          }
+        }
+      }
+      return year;
+    }
+
+  }
+
+  @Test
+  public void testTimestamp() throws InterruptedException, ExecutionException, TimeoutException {
+    ExecutorService e = Executors.newFixedThreadPool(2);
+    Integer year1 = 7777;
+    Future<Integer> future1 = e.submit(new SelectTimestampManyTimes(con, year1));
+    Integer year2 = 2017;
+    Future<Integer> future2 = e.submit(new SelectTimestampManyTimes(con, year2));
+    assertEquals("Year was changed in another thread", year1, future1.get(1, TimeUnit.MINUTES));
+    assertEquals("Year was changed in another thread", year2, future2.get(1, TimeUnit.MINUTES));
+    e.shutdown();
+    e.awaitTermination(1, TimeUnit.MINUTES);
+  }
+
 }


=====================================
src/test/java/org/postgresql/util/BigDecimalByteConverter2Test.java
=====================================
@@ -0,0 +1,23 @@
+/*
+ * Copyright (c) 2023, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.util;
+
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.math.BigInteger;
+
+/**
+ * This test is moved from {@link BigDecimalByteConverterTest} to prevent test with very long name.
+ */
+public class BigDecimalByteConverter2Test {
+  @Test
+  public void testBigDecimal10_pow_131072_minus_1() {
+    BigDecimalByteConverterTest.testBinary(
+        new BigDecimal(BigInteger.TEN.pow(131072).subtract(BigInteger.ONE))
+    );
+  }
+}


=====================================
src/test/java/org/postgresql/util/BigDecimalByteConverterTest.java
=====================================
@@ -80,6 +80,10 @@ public class BigDecimalByteConverterTest {
 
   @Test
   public void testBinary() {
+    testBinary(number);
+  }
+
+  static void testBinary(BigDecimal number) {
     final byte[] bytes = ByteConverter.numeric(number);
     final BigDecimal actual = (BigDecimal) ByteConverter.numeric(bytes);
     if (number.scale() >= 0) {



View it on GitLab: https://salsa.debian.org/java-team/libpostgresql-jdbc-java/-/commit/6a36d0f3bfc9b6ada7a4fdad75d0aa6b8db2871f

-- 
View it on GitLab: https://salsa.debian.org/java-team/libpostgresql-jdbc-java/-/commit/6a36d0f3bfc9b6ada7a4fdad75d0aa6b8db2871f
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-java-commits/attachments/20230209/0b2dcbdb/attachment.htm>


More information about the pkg-java-commits mailing list