Repository: pgjdbc/pgjdbc
Pull request #1506 (2019-06-17 20:44:23)

fix: issue 716 getTypeInfo() may not return data in the order specified in Oracle documentation
this replaces PR #910 as it was stale and easier to replace than fix. Original work by @zemian



AppVeyorBot: :white_check_mark: [Build pgjdbc 1.0.289 completed](https://ci.appveyor.com/project/davecramer/pgjdbc/builds/25343778) (commit https://github.com/pgjdbc/pgjdbc/commit/9f0b01c2ee by @davecramer)
codecov-io: # [Codecov](https://codecov.io/gh/pgjdbc/pgjdbc/pull/1506?src=pr&el=h1) Report
> Merging [#1506](https://codecov.io/gh/pgjdbc/pgjdbc/pull/1506?src=pr&el=desc) into [master](https://codecov.io/gh/pgjdbc/pgjdbc/commit/45ce14fd170d3b50aed5bbe5e3d25bbd0a3e80a5?src=pr&el=desc) will **increase** coverage by `0.06%`.
> The diff coverage is `69.23%`.



```diff
@@             Coverage Diff              @@
##             master    #1506      +/-   ##
============================================
+ Coverage     68.77%   68.83%   +0.06%     
- Complexity     3929     3939      +10     
============================================
  Files           179      179              
  Lines         16465    16477      +12     
  Branches       2674     2677       +3     
============================================
+ Hits          11323    11342      +19     
+ Misses         3892     3890       -2     
+ Partials       1250     1245       -5
```


Patch:
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
index 3a9228c3..fcff6cd3 100644
--- a/.github/pull_request_template.md
+++ b/.github/pull_request_template.md
@@ -9,6 +9,7 @@
 
 1. [ ] Does your submission pass tests?
 2. [ ] Does mvn checkstyle:check pass ?
+3. [ ] Have you added your new test classes to an existing test suite in alphabetical order?
 
 ### Changes to Existing Features:
 
diff --git a/.travis.yml b/.travis.yml
index b2a842e8..26404e1b 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -39,6 +39,7 @@ before_script:
   - if [[ $TRAVIS_BRANCH == release/* ]]; then echo "MAVEN_OPTS='-Xmx1g'" > ~/.mavenrc; else echo "MAVEN_OPTS='-Xmx1g -Dgpg.skip=true'" > ~/.mavenrc; fi
   - test "x$PG_VERSION" == 'x' || test "x$NO_HSTORE" == 'xY' || psql test -c 'CREATE EXTENSION hstore;' -U postgres
   - test "x$PG_VERSION" == 'x' || test "x$CREATE_PLPGSQL" == 'x' || createlang -U postgres plpgsql test
+  - test "x$PG_VERSION" == 'x' || test "x$PLPGSQL_EXTENSION" == 'x' || psql test -c 'CREATE EXTENSION IF NOT EXISTS plpgsql;' -U postgres
 
 env:
   global:
@@ -102,6 +103,36 @@ matrix:
       - PG_VERSION=10
       - JDK=12
       - TZ=America/New_York # flips between −05:00 and −04:00
+    - jdk: openjdk-ea
+      sudo: required
+      addons:
+        postgresql: "11"
+      env:
+        - PG_VERSION=11
+        - XA=true
+        - COVERAGE=Y
+        - NO_HSTORE=Y
+        - PLPGSQL_EXTENSION=Y
+    - jdk: oraclejdk9
+      sudo: required
+      addons:
+        postgresql: "11"
+      env:
+        - PG_VERSION=11
+        - XA=true
+        - COVERAGE=Y
+        - NO_HSTORE=Y
+        - PLPGSQL_EXTENSION=Y
+    - jdk: oraclejdk8
+      sudo: required
+      addons:
+        postgresql: "11"
+      env:
+        - PG_VERSION=11
+        - XA=true
+        - COVERAGE=Y
+        - NO_HSTORE=Y
+        - PLPGSQL_EXTENSION=Y
     - jdk: oraclejdk11
       sudo: required
       addons:
@@ -188,24 +219,6 @@ matrix:
         - COVERAGE=Y
         - NO_HSTORE=Y
         - CREATE_PLPGSQL=Y
-    - jdk: oraclejdk8
-      sudo: required
-      dist: precise
-      env:
-        - PG_VERSION=8.3
-        - XA=true
-        - COVERAGE=Y
-        - NO_HSTORE=Y
-        - CREATE_PLPGSQL=Y
-    - jdk: oraclejdk8
-      sudo: required
-      dist: precise
-      env:
-        - PG_VERSION=8.2
-        - XA=true
-        - COVERAGE=Y
-        - NO_HSTORE=Y
-        - CREATE_PLPGSQL=Y
     - jdk: oraclejdk8
       addons:
         postgresql: "9.6"
diff --git a/.travis/travis_build.sh b/.travis/travis_build.sh
index 0ccc6878..00ef433e 100755
--- a/.travis/travis_build.sh
+++ b/.travis/travis_build.sh
@@ -8,6 +8,10 @@ then
   python -c 'import os,sys,fcntl; flags = fcntl.fcntl(sys.stdout, fcntl.F_GETFL); fcntl.fcntl(sys.stdout, fcntl.F_SETFL, flags&~os.O_NONBLOCK);'
   export PROJECT_VERSION=$(mvn -B -N org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\[')
   export PARENT_VERSION=$(mvn -B -N org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.parent.version | grep -v '\[')
+  export CHECK_PARENT_VERSION=$(mvn help:evaluate -Dexpression=project.parent.version -q -DforceStdout -f pgjdbc/pom.xml)
+  # just make sure that pom.xml has the same value as pgjdbc/pom.xml
+  test "$PARENT_VERSION" = "$CHECK_PARENT_VERSION"
+
   exec ./packaging/rpm_ci
 fi
 
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77acdb89..c0adbf47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,8 +10,33 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 
 ### Fixed
 
+## [42.2.8] (2019-09-13)
+### Changed
+
+### Added
+
+### Fixed
+
+* fix: Revert inet default Java type to PGObject and handle values with net masks [PR 1568](https://github.com/pgjdbc/pgjdbc/pull/1568) 
+
+## [42.2.7] (2019-09-03)
+### Changed
+
+
+### Added
+- Expose parameter status messages (GUC_REPORT) to the user [PR 1435](https://github.com/pgjdbc/pgjdbc/pull/1435)
+- Add automatic module name to manifest for jdk9+ [PR 1538](https://github.com/pgjdbc/pgjdbc/pull/1538)
+- Log ignoring rollback when no transaction in progress [PR 1549](https://github.com/pgjdbc/pgjdbc/pull/1549)
+- Map inet type to InetAddress [PR 1527](https://github.com/pgjdbc/pgjdbc/pull/1527) [issue 1134](https://github.com/pgjdbc/pgjdbc/issues/1134)
+
+### Fixed
+- fix [issue 1547](https://github.com/pgjdbc/pgjdbc/issues/1547) As long as peek returns some bytes do not reset the timeout, this allows us to continue checking until any async notifies are consumed [PR 1548](https://github.com/pgjdbc/pgjdbc/pull/1548)
+- fix: [issue 1466](https://github.com/pgjdbc/pgjdbc/issues/1466) In logical decoding the if the backend was requesting a reply we… [PR 1467](https://github.com/pgjdbc/pgjdbc/pull/1467) 
+- fix: [issue 1534](https://github.com/pgjdbc/pgjdbc/issues/1534) Proleptic java.time support [PR 1539](https://github.com/pgjdbc/pgjdbc/pull/1539)
+- fix Ensure isValid() will not last more than timeout seconds [PR 1557](https://github.com/pgjdbc/pgjdbc/pull/1557)
 ## [42.2.6] (2019-06-19)
 ### Known issues
+- Waffle has [dropped support](https://github.com/Waffle/waffle/releases/tag/waffle-1.9.0) for 1.6, 1.7 as such the new waffle 1.9.x is only available in jre8
 - Microseconds in timestamps might be truncated when transferred in binary mode
 - 24:00 time handling is not consistent [issue 1385](https://github.com/pgjdbc/pgjdbc/issues/1385)
 - Unexpected packet type during stream replication [issue 1466](https://github.com/pgjdbc/pgjdbc/issues/1466)
@@ -219,4 +244,6 @@ thrown to caller to be dealt with so no need to log at this verbosity by pgjdbc
 [42.2.4]: https://github.com/pgjdbc/pgjdbc/compare/REL42.2.3...REL42.2.4
 [42.2.5]: https://github.com/pgjdbc/pgjdbc/compare/REL42.2.4...REL42.2.5
 [42.2.6]: https://github.com/pgjdbc/pgjdbc/compare/REL42.2.5...REL42.2.6
-[Unreleased]: https://github.com/pgjdbc/pgjdbc/compare/REL42.2.6...HEAD
+[42.2.7]: https://github.com/pgjdbc/pgjdbc/compare/REL42.2.6...REL42.2.7
+[42.2.8]: https://github.com/pgjdbc/pgjdbc/compare/REL42.2.7...REL42.2.8
+[Unreleased]: https://github.com/pgjdbc/pgjdbc/compare/REL42.2.8...HEAD
diff --git a/README.md b/README.md
index 4676cd0a..4fcf6a0f 100644
--- a/README.md
+++ b/README.md
@@ -24,36 +24,36 @@ Most people do not need to compile PgJDBC. You can download the precompiled driv
 ### Maven Central
 You can search on The Central Repository with GroupId and ArtifactId [![Maven Search](https://img.shields.io/badge/org.postgresql-postgresql-yellow.svg)][mvn-search] for:
 
-[![Java 8](https://img.shields.io/badge/Java_8-42.2.5-blue.svg)][mvn-jre8]
+[![Java 8](https://img.shields.io/badge/Java_8-42.2.7-blue.svg)][mvn-jre8]
 ```xml
 <dependency>
     <groupId>org.postgresql</groupId>
     <artifactId>postgresql</artifactId>
-    <version>42.2.5</version>
+    <version>42.2.7</version>
 </dependency>
 ```
 
-[![Java 7](https://img.shields.io/badge/Java_7-42.2.5.jre7-blue.svg)][mvn-jre7]
+[![Java 7](https://img.shields.io/badge/Java_7-42.2.7.jre7-blue.svg)][mvn-jre7]
 ```xml
 <dependency>
     <groupId>org.postgresql</groupId>
     <artifactId>postgresql</artifactId>
-    <version>42.2.5.jre7</version>
+    <version>42.2.7.jre7</version>
 </dependency>
 ```
 
-[![Java 6](https://img.shields.io/badge/Java_6-42.2.5.jre6-blue.svg)][mvn-jre6]
+[![Java 6](https://img.shields.io/badge/Java_6-42.2.7.jre6-blue.svg)][mvn-jre6]
 ```xml
 <dependency>
     <groupId>org.postgresql</groupId>
     <artifactId>postgresql</artifactId>
-    <version>42.2.5.jre6</version>
+    <version>42.2.7.jre6</version>
 </dependency>
 ```
 [mvn-search]: http://search.maven.org/#search%7Cgav%7C1%7Cg%3A%22org.postgresql%22%20AND%20a%3A%22postgresql%22 "Search on Maven Central"
-[mvn-jre6]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.5.jre6|bundle
-[mvn-jre7]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.5.jre7|bundle
-[mvn-jre8]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.5|bundle
+[mvn-jre6]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.7.jre6|bundle
+[mvn-jre7]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.7.jre7|bundle
+[mvn-jre8]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.7|bundle
 
 #### Development snapshots
 Snapshot builds (builds from `master` branch) are also deployed to Maven Central, so you can test current development version (test some bugfix) using:
@@ -61,9 +61,9 @@ Snapshot builds (builds from `master` branch) are also deployed to Maven Central
 <dependency>
   <groupId>org.postgresql</groupId>
   <artifactId>postgresql</artifactId>
-  <version>42.2.6-SNAPSHOT</version> <!-- Java 8 -->
-  <version>42.2.6.jre7-SNAPSHOT</version> <!-- Java 7 -->
-  <version>42.2.6.jre6-SNAPSHOT</version> <!-- Java 6 -->
+  <version>42.2.8-SNAPSHOT</version> <!-- Java 8 -->
+  <version>42.2.8.jre7-SNAPSHOT</version> <!-- Java 7 -->
+  <version>42.2.8.jre6-SNAPSHOT</version> <!-- Java 6 -->
 </dependency>
 ```
 
@@ -141,14 +141,14 @@ In addition to the standard connection parameters the driver supports a number o
 | readOnly                      | Boolean | true    | 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 to be set in the search-path |
+| 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 |
 | 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. |
+| 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. |
 
diff --git a/codecov.yml b/codecov.yml
index 72deba08..c4067ade 100644
--- a/codecov.yml
+++ b/codecov.yml
@@ -1,6 +1,6 @@
 codecov:
   notify:
-    after_n_builds: 10
+    after_n_builds: 11
     require_ci_to_pass: false
 comment:
   layout: header, changes, diff
diff --git a/contributors.json b/contributors.json
index d286662a..bbe3dcd6 100644
--- a/contributors.json
+++ b/contributors.json
@@ -1,5 +1,6 @@
 {
    "Adam Brusselback" : "https://github.com/Tostino",
+   "Adrien" : "https://github.com/arobert-delfingen",
    "AlBundy33" : "https://github.com/AlBundy33",
    "AlexElin" : "https://github.com/AlexElin",
    "Alexander Kjäll" : "https://github.com/alexanderkjall",
@@ -21,6 +22,7 @@
    "Eric McCormack" : "https://github.com/ericmack",
    "Florin Asăvoaie" : "https://github.com/FlorinAsavoaie",
    "George Kankava" : "https://github.com/georgekankava",
+   "Hajar Razip" : "https://github.com/mshajarrazip",
    "Hari Babu Kommi" : "https://github.com/kommiharibabu",
    "Harry Chan" : "https://github.com/hc-codersatlas",
    "Hugh Cole-Baker" : "https://github.com/sigmaris",
@@ -51,11 +53,13 @@
    "Marios Trivyzas" : "https://github.com/matriv",
    "Mark Nguyen" : "https://github.com/Mrk-Nguyen",
    "Mathias Fußenegger" : "https://github.com/mfussenegger",
+   "Matteo Melli" : "https://github.com/teoincontatto",
    "Michael Glaesemann" : "https://github.com/grzm",
    "MichaelZg" : "https://github.com/michaelzg",
    "Michele Mancioppi" : "https://github.com/michele-mancioppi",
    "Minglei Tu" : "https://github.com/tminglei",
    "Mykola Nikishov" : "https://github.com/manandbytes",
+   "Myo Wai Thant" : "https://github.com/myowaithant9",
    "Nikolai Ivanov" : "https://github.com/nicktorwald",
    "Pavel Raiskup" : "https://github.com/praiskup",
    "Pawel" : "https://github.com/veselov",
@@ -72,15 +76,18 @@
    "Selene Feigl" : "https://github.com/sfeigl",
    "Sidi Mohamed EL AATIFI" : "https://github.com/elaatifi",
    "Simon Stelling" : "https://github.com/stellingsimon",
+   "Steinar Bang" : "https://github.com/steinarb",
    "Stephen Nelson" : "https://github.com/lordnelson",
    "Steve Ungerer" : "https://github.com/scubasau",
    "Sualeh Fatehi" : "https://github.com/sualeh",
    "Tanya Gordeeva" : "https://github.com/tmgordeeva",
    "Thach Hoang" : "https://github.com/thachhoang",
+   "Tim Ward" : "https://github.com/timothyjward",
    "Trygve Laugstøl" : "https://github.com/trygvis",
    "Tyson Andre" : "https://github.com/TysonAndre",
    "Vladimir Gordiychuk" : "https://github.com/Gordiychuk",
    "Vladimir Sitnikov" : "https://github.com/vlsi",
+   "Vsevolod Kaimashnikov" : "https://github.com/vsevolodk",
    "Zemian Deng" : "https://github.com/zemian",
    "aryabukhin" : "https://github.com/aryabukhin",
    "bazzargh" : "https://github.com/bazzargh",
diff --git a/docs/_posts/2019-06-19-42.2.6-release.md b/docs/_posts/2019-06-19-42.2.6-release.md
index e3d1b38d..8ec9fb54 100644
--- a/docs/_posts/2019-06-19-42.2.6-release.md
+++ b/docs/_posts/2019-06-19-42.2.6-release.md
@@ -7,6 +7,13 @@ version: 42.2.6
 ---
 **Notable changes**
 
+### Known issues
+- Waffle has [dropped support](https://github.com/Waffle/waffle/releases/tag/waffle-1.9.0) for 1.6, 1.7 as such the new waffle 1.9.x is only available in jre8
+- Microseconds in timestamps might be truncated when transferred in binary mode
+- 24:00 time handling is not consistent [issue 1385](https://github.com/pgjdbc/pgjdbc/issues/1385)
+- Unexpected packet type during stream replication [issue 1466](https://github.com/pgjdbc/pgjdbc/issues/1466)
+- Driver goes missing after OSGi bundle restart [issue 1476](https://github.com/pgjdbc/pgjdbc/issues/1476)
+
 ### Changed
 - Change IS_GENERATED to IS_GENERATEDCOLUMN as per spec [PR 1485](https://github.com/pgjdbc/pgjdbc/pull/1485)
 - Fix missing metadata columns, and misspelled columns in PgDatabaseMetaData#getTables [PR 1323](https://github.com/pgjdbc/pgjdbc/pull/1323)
@@ -29,12 +36,6 @@ version: 42.2.6
 - Fix execution with big decimal in simple query mode. [PR 1463](https://github.com/pgjdbc/pgjdbc/pull/1463)
 - Fix rounding for timestamps truncated to dates before 1970 [PR 1502](https://github.com/pgjdbc/pgjdbc/pull/1502)
 
-### Known issues
-- Microseconds in timestamps might be truncated when transferred in binary mode
-- 24:00 time handling is not consistent [issue 1385](https://github.com/pgjdbc/pgjdbc/issues/1385)
-- Unexpected packet type during stream replication [issue 1466](https://github.com/pgjdbc/pgjdbc/issues/1466)
-- Driver goes missing after OSGi bundle restart [issue 1476](https://github.com/pgjdbc/pgjdbc/issues/1476)
-
 
 <!--more-->
 
diff --git a/docs/_posts/2019-09-10-42.2.7-release.md b/docs/_posts/2019-09-10-42.2.7-release.md
new file mode 100644
index 00000000..769adb36
--- /dev/null
+++ b/docs/_posts/2019-09-10-42.2.7-release.md
@@ -0,0 +1,103 @@
+---
+title: PostgreSQL JDBC Driver 42.2.7 Released
+date: 2019-09-10 13:50:37 +0000
+categories:
+  - new_release
+version: 42.2.7
+---
+**Notable changes**
+
+### Changed
+
+### Added
+- Expose parameter status messages (GUC_REPORT) to the user [PR 1435](https://github.com/pgjdbc/pgjdbc/pull/1435)
+- Add automatic module name to manifest for jdk9+ [PR 1538](https://github.com/pgjdbc/pgjdbc/pull/1538)
+- Log ignoring rollback when no transaction in progress [PR 1549](https://github.com/pgjdbc/pgjdbc/pull/1549)
+- Map inet type to InetAddress [PR 1527](https://github.com/pgjdbc/pgjdbc/pull/1527) [issue 1134](https://github.com/pgjdbc/pgjdbc/issues/1134)
+
+### Fixed
+- fix [issue 1547](https://github.com/pgjdbc/pgjdbc/issues/1547) As long as peek returns some bytes do not reset the timeout, this allows us to continue checking until any async notifies are consumed [PR 1548](https://github.com/pgjdbc/pgjdbc/pull/1548)
+- fix: [issue 1466](https://github.com/pgjdbc/pgjdbc/issues/1466) In logical decoding the if the backend was requesting a reply we… [PR 1467](https://github.com/pgjdbc/pgjdbc/pull/1467)
+- fix: [issue 1534](https://github.com/pgjdbc/pgjdbc/issues/1534) Proleptic java.time support [PR 1539](https://github.com/pgjdbc/pgjdbc/pull/1539)
+- fix Ensure isValid() will not last more than timeout seconds [PR 1557](https://github.com/pgjdbc/pgjdbc/pull/1557)
+
+<!--more-->
+
+**Commits by author**
+
+Adrien (1):
+
+* Ensure isValid() will not last more than timeout seconds [PR 1557](https://github.com/pgjdbc/pgjdbc/pull/1557) [b2eaefe](https://github.com/pgjdbc/pgjdbc/commit/b2eaefef1787b8ab0b8fcb266d10be4945a4fe25)
+
+Craig Ringer (2):
+
+* Expose parameter status messages (GUC_REPORT) to the user [PR 1435](https://github.com/pgjdbc/pgjdbc/pull/1435) [ce8333a](https://github.com/pgjdbc/pgjdbc/commit/ce8333a56ba74022adeb545b68e7d2bee32d966f)
+* Make ConnectTimeout test accept NoRouteToHostException [PR 1526](https://github.com/pgjdbc/pgjdbc/pull/1526) [08d8129](https://github.com/pgjdbc/pgjdbc/commit/08d81291c69d02d8973d6b39dac82bdaad91f2ee)
+
+Dave Cramer (8):
+
+* note that waffle 1.9.x is only supported in jre8 version Update README to point to new maven coordinates [58804e9](https://github.com/pgjdbc/pgjdbc/commit/58804e9af41368fc9e956a7dd6cf799cb1d72420)
+* add automatic module name to manifest for jdk9+ [PR 1538](https://github.com/pgjdbc/pgjdbc/pull/1538) [0600990](https://github.com/pgjdbc/pgjdbc/commit/0600990007669119b73ee2adb064184a4c62343f)
+* fix issue 1547, as long as peek returns some bytes do not reset the timeout, this allows us to continue checking until any async notifies are consumed [PR 1548](https://github.com/pgjdbc/pgjdbc/pull/1548) [36a75cb](https://github.com/pgjdbc/pgjdbc/commit/36a75cbaab4bda1b55f48aa9064258051cd89b10)
+* fix: In logical decoding the if the backend was requesting a reply we… [PR 1467](https://github.com/pgjdbc/pgjdbc/pull/1467) [f89e62c](https://github.com/pgjdbc/pgjdbc/commit/f89e62cdce4895f163dc95353cd31614347624b6)
+* Log ignoring rollback when no transaction in progress [PR 1549](https://github.com/pgjdbc/pgjdbc/pull/1549) [bda74d7](https://github.com/pgjdbc/pgjdbc/commit/bda74d7eaa341c02427af4065d8321c8e45bfd83)
+* fix assertion message [PR 1553](https://github.com/pgjdbc/pgjdbc/pull/1553) [ba82948](https://github.com/pgjdbc/pgjdbc/commit/ba8294841a2e589e15dfcb79dbd31a83ec615208)
+* remove failing tests for PostgreSQL version 8.3 add in tests for 11 [89464a5](https://github.com/pgjdbc/pgjdbc/commit/89464a54777b9272893ab24bc5e28a24623e41bb)
+* Fix travis parsing issue [634ac7d](https://github.com/pgjdbc/pgjdbc/commit/634ac7d8fee23eb5231ce91c616a64d120f8d8ee)
+
+Hajar Razip (2):
+
+* docs: add note on behavior of ResultSet.getString() [PR 1286](https://github.com/pgjdbc/pgjdbc/pull/1286) (#1528) [c30556c](https://github.com/pgjdbc/pgjdbc/commit/c30556c6c059f25d4ed43608d1b2a2f02a168389)
+* docs: update resultset.md in head to reflect 94 [PR 1528](https://github.com/pgjdbc/pgjdbc/pull/1528) (#1536) [fc8efc9](https://github.com/pgjdbc/pgjdbc/commit/fc8efc9a98e86059701e8674017947c0b702cab1)
+
+Matteo Melli (1):
+
+* Updated scram to version 2.0 [PR 1532](https://github.com/pgjdbc/pgjdbc/pull/1532) [fcbbc3e](https://github.com/pgjdbc/pgjdbc/commit/fcbbc3e6408cc1bcf459b740c683f3db40a5050c)
+
+Myo Wai Thant (1):
+
+* Issue 1134 Map inet type to InetAddress [PR 1527](https://github.com/pgjdbc/pgjdbc/pull/1527) [1d0c477](https://github.com/pgjdbc/pgjdbc/commit/1d0c477abbe23f23681a924ee0d216a5f7188079)
+
+Pavel Raiskup (2):
+
+* fix parent version in pgjdbc/pom.xml as well [3db55da](https://github.com/pgjdbc/pgjdbc/commit/3db55daf2dccdd49555fd73b70be5c15609cccfa)
+* rpm: remove karaf plugin [8495127](https://github.com/pgjdbc/pgjdbc/commit/8495127fe0450f94923251f67ab8e2208319437f)
+
+Philippe Marschall (1):
+
+* fix: proleptic java.time support [PR 1539](https://github.com/pgjdbc/pgjdbc/pull/1539) [60fa6d3](https://github.com/pgjdbc/pgjdbc/commit/60fa6d374a392d00475be0c128804c43b2852a35)
+
+Sehrope Sarkuni (2):
+
+* docs: Add note to GitHub PR templates about test suites [PR 1531](https://github.com/pgjdbc/pgjdbc/pull/1531) [51f3d0b](https://github.com/pgjdbc/pgjdbc/commit/51f3d0b75078e5c8687c7eae20ff37b28e65abec)
+* Sort test suites and enable missed tests [PR 1530](https://github.com/pgjdbc/pgjdbc/pull/1530) [aa8778d](https://github.com/pgjdbc/pgjdbc/commit/aa8778d91bd166e2f351343855d6e0b0b71b1e62)
+
+Steinar Bang (1):
+
+* Add an apache karaf feature for the PostgreSQL JDBC driver to fix [PR 1552](https://github.com/pgjdbc/pgjdbc/pull/1552) (#1554) [e2f6e9f](https://github.com/pgjdbc/pgjdbc/commit/e2f6e9fc54a60f4b310bab9d83d74dbdc8941f32)
+
+Tim Ward (1):
+
+* Check that JDBC Connections that are closed by the server do not report as valid [PR 1533](https://github.com/pgjdbc/pgjdbc/pull/1533) [56399ef](https://github.com/pgjdbc/pgjdbc/commit/56399efddd25281ccefd90c56e9db084c550d195)
+
+Vsevolod Kaimashnikov (1):
+
+* Add more info about currentSchema property. [PR 1481](https://github.com/pgjdbc/pgjdbc/pull/1481) [d96ed59](https://github.com/pgjdbc/pgjdbc/commit/d96ed59eba6a2279da337684e696b198ec60685c)
+
+<a name="contributors_{{ page.version }}"></a>
+### Contributors to this release
+
+We thank the following people for their contributions to this release.
+
+[Adrien](https://github.com/arobert-delfingen)  
+[Craig Ringer](https://github.com/ringerc)  
+[Dave Cramer](davec@postgresintl.com)  
+[Hajar Razip](https://github.com/mshajarrazip)  
+[Matteo Melli](https://github.com/teoincontatto)  
+[Myo Wai Thant](https://github.com/myowaithant9)  
+[Pavel Raiskup](https://github.com/praiskup)  
+[Philippe Marschall](https://github.com/marschall)  
+[Sehrope Sarkuni](https://github.com/sehrope)  
+[Steinar Bang](https://github.com/steinarb)  
+[Tim Ward](https://github.com/timothyjward)  
+[Vsevolod Kaimashnikov](https://github.com/vsevolodk)  
diff --git a/docs/_posts/2019-09-13-42.2.8-release.md b/docs/_posts/2019-09-13-42.2.8-release.md
new file mode 100644
index 00000000..5351f212
--- /dev/null
+++ b/docs/_posts/2019-09-13-42.2.8-release.md
@@ -0,0 +1,31 @@
+---
+title: PostgreSQL JDBC Driver 42.2.8 Released
+date: 2019-09-13 13:23:26 +0000
+categories:
+  - new_release
+version: 42.2.8
+---
+**Notable changes**
+
+### Changed
+
+### Added
+
+### Fixed
+
+
+<!--more-->
+
+**Commits by author**
+
+Sehrope Sarkuni (1):
+
+* fix: Revert inet default Java type to PGObject and handle values with net masks [PR 1568](https://github.com/pgjdbc/pgjdbc/pull/1568) [3df32f9](https://github.com/pgjdbc/pgjdbc/commit/3df32f9bf9da38cc5f0fbb212a3034e4634dd3c7)
+
+<a name="contributors_{{ page.version }}"></a>
+### Contributors to this release
+
+We thank the following people for their contributions to this release.
+
+[Dave Cramer](davec@postgresintl.com)  
+[Sehrope Sarkuni](https://github.com/sehrope)  
diff --git a/docs/documentation/92/query.md b/docs/documentation/92/query.md
index 88fa831e..89ee5e51 100644
--- a/docs/documentation/92/query.md
+++ b/docs/documentation/92/query.md
@@ -76,7 +76,7 @@ exhausted the next block of rows is retrieved by repositioning the cursor.
 * The `Connection` must not be in autocommit mode. The backend closes cursors at
 	the end of transactions, so in autocommit mode the backend will have
 	closed the cursor before anything can be fetched from it.
-*The `Statement` must be created with a `ResultSet` type of `ResultSet.TYPE_FORWARD_ONLY`.
+* The `Statement` must be created with a `ResultSet` type of `ResultSet.TYPE_FORWARD_ONLY`.
 	This is the default, so no code will need to be rewritten to take advantage
 	of this, but it also means that you cannot scroll backwards or otherwise
 	jump around in the `ResultSet`.
diff --git a/docs/documentation/93/query.md b/docs/documentation/93/query.md
index 88fa831e..89ee5e51 100644
--- a/docs/documentation/93/query.md
+++ b/docs/documentation/93/query.md
@@ -76,7 +76,7 @@ exhausted the next block of rows is retrieved by repositioning the cursor.
 * The `Connection` must not be in autocommit mode. The backend closes cursors at
 	the end of transactions, so in autocommit mode the backend will have
 	closed the cursor before anything can be fetched from it.
-*The `Statement` must be created with a `ResultSet` type of `ResultSet.TYPE_FORWARD_ONLY`.
+* The `Statement` must be created with a `ResultSet` type of `ResultSet.TYPE_FORWARD_ONLY`.
 	This is the default, so no code will need to be rewritten to take advantage
 	of this, but it also means that you cannot scroll backwards or otherwise
 	jump around in the `ResultSet`.
diff --git a/docs/documentation/94/query.md b/docs/documentation/94/query.md
index 88fa831e..89ee5e51 100644
--- a/docs/documentation/94/query.md
+++ b/docs/documentation/94/query.md
@@ -76,7 +76,7 @@ exhausted the next block of rows is retrieved by repositioning the cursor.
 * The `Connection` must not be in autocommit mode. The backend closes cursors at
 	the end of transactions, so in autocommit mode the backend will have
 	closed the cursor before anything can be fetched from it.
-*The `Statement` must be created with a `ResultSet` type of `ResultSet.TYPE_FORWARD_ONLY`.
+* The `Statement` must be created with a `ResultSet` type of `ResultSet.TYPE_FORWARD_ONLY`.
 	This is the default, so no code will need to be rewritten to take advantage
 	of this, but it also means that you cannot scroll backwards or otherwise
 	jump around in the `ResultSet`.
diff --git a/docs/documentation/94/resultset.md b/docs/documentation/94/resultset.md
index 58c5f5a4..02204858 100644
--- a/docs/documentation/94/resultset.md
+++ b/docs/documentation/94/resultset.md
@@ -16,4 +16,14 @@ The following must be considered when using the `ResultSet` interface:
 * You must close a `ResultSet` by calling `close()` once you have finished using
 	it.
 * Once you make another query with the `Statement` used to create a `ResultSet`,
-	the currently open `ResultSet` instance is closed automatically.
\ No newline at end of file
+	the currently open `ResultSet` instance is closed automatically.
+* When PreparedStatement API is used, `ResultSet` switches to binary mode after 
+	five query executions (this default is set by the `prepareThreshold` 
+	connection property, see [Server Prepared Statements](server-prepare.md)). 
+	This may cause unexpected behavior when some methods are called. For example, 
+	results on method calls such as `getString()` on non-string data types, 	
+	while logically　equivalent, may be formatted differently after execution exceeds 
+	the set `prepareThreshold` when conversion to object method switches to one 
+	matching the return mode. 
+	
+	
\ No newline at end of file
diff --git a/docs/documentation/head/connect.md b/docs/documentation/head/connect.md
index c455aca4..b412da03 100644
--- a/docs/documentation/head/connect.md
+++ b/docs/documentation/head/connect.md
@@ -93,6 +93,8 @@ Connection conn = DriverManager.getConnection(url);
 	of it specifies a SSL connection. However, for compatibility with future
 	versions, the value "true" is preferred. For more information see [Chapter
 	4, *Using SSL*](ssl.html).
+
+    Setting up the certificates and keys for ssl connection can be tricky see [The test documentation](https://github.com/pgjdbc/pgjdbc/blob/master/certdir/README.md) for detailed examples.
  
 * **sslfactory** = String
 
@@ -120,20 +122,30 @@ Connection conn = DriverManager.getConnection(url);
 
 	Provide the full path for the certificate file. Defaults to /defaultdir/postgresql.crt
 
+    It can be a PEM encoded X509v3 certificate
+
 	*Note:* defaultdir is ${user.home}/.postgresql/ in *nix systems and %appdata%/postgresql/ on windows 
 
 * **sslkey** = String
 
 	Provide the full path for the key file. Defaults to /defaultdir/postgresql.pk8. 
 	
-	*Note:* The key file **must** be in [DER format](https://wiki.openssl.org/index.php/DER). A PEM key can be converted to DER format using the openssl command:
+	*Note:* The key file **must** be in [PKCS-8](https://en.wikipedia.org/wiki/PKCS_8) [DER format](https://wiki.openssl.org/index.php/DER). A PEM key can be converted to DER format using the openssl command:
 	
-	`openssl pkcs8 -topk8 -inform PEM -in my.key -outform DER -out my.key.der`
+	`openssl pkcs8 -topk8 -inform PEM -in my.key -outform DER -out my.key.der -v1 PBE-MD5-DES`
+
+    *Note:* The use of -v1 PBE-MD5-DES might be inadequate in environments where high level of security is needed and the key is not protected
+    by other means (e.g. access control of the OS), or the key file is transmitted in untrusted channels.
+    We are depending on the cryptography providers provided by the java runtime. The solution documented here is known to work at
+    the time of writing. If you have stricter security needs, please see https://stackoverflow.com/questions/58488774/configure-tomcat-hibernate-to-have-a-cryptographic-provider-supporting-1-2-840-1
+    for a discussion of the problem and information on choosing a better cipher suite.
 
 * **sslrootcert** = String
 
 	File name of the SSL root certificate. Defaults to defaultdir/root.crt
 
+    It can be a PEM encoded X509v3 certificate
+
 * **sslhostnameverifier** = String
 
 	Class name of hostname verifier. Defaults to using `org.postgresql.ssl.PGjdbcHostnameVerifier`
@@ -208,7 +220,7 @@ Connection conn = DriverManager.getConnection(url);
 
     The default is `never` 
 
-* **cleanupSavePoints** = boolean
+* **cleanupSavepoints** = boolean
 
     Determines if the SAVEPOINT created in autosave mode is released prior to the statement. This is
     done to avoid running out of shared buffers on the server in the case where 1000's of queries are
@@ -411,7 +423,7 @@ Connection conn = DriverManager.getConnection(url);
 
 * **currentSchema** = String
 
-	Specify the schema to be set in the search-path. 
+	Specify the schema (or several schema separated by commas) to be set in the search-path. 
 	This schema will be used to resolve unqualified object names used in statements over this connection.
 
 * **targetServerType** = String
diff --git a/docs/documentation/head/ext.md b/docs/documentation/head/ext.md
index ea7cee9b..2a46d469 100644
--- a/docs/documentation/head/ext.md
+++ b/docs/documentation/head/ext.md
@@ -16,6 +16,7 @@ next: geometric.html
 * [Large Objects](largeobjects.html)
 * [Listen / Notify](listennotify.html)
 * [Server Prepared Statements](server-prepare.html)
+* [Parameter Status Messages](parameterstatus.html)
 * [Physical and Logical replication API](replication.html)
 * [Arrays](arrays.html)
 
diff --git a/docs/documentation/head/parameterstatus.md b/docs/documentation/head/parameterstatus.md
new file mode 100644
index 00000000..f83fda21
--- /dev/null
+++ b/docs/documentation/head/parameterstatus.md
@@ -0,0 +1,63 @@
+---
+layout: default_docs
+title: Physical and Logical replication API
+header: Chapter 9. PostgreSQL™ Extensions to the JDBC API
+resource: media
+previoustitle: Parameter Status Messages
+previous: server-prepare.html
+nexttitle: Physical and Logical replication API
+next: replication.html
+---
+
+# Parameter Status Messages
+
+PostgreSQL supports server parameters, also called server variables or,
+internally, Grand Unified Configuration (GUC) variables. These variables are
+manipulated by the `SET` command, `postgresql.conf`, `ALTER SYSTEM SET`, `ALTER
+USER SET`, `ALTER DATABASE SET`, the `set_config(...)` SQL-callable function,
+etc. See [the PostgreSQL manual](https://www.postgresql.org/docs/current/config-setting.html).
+
+For a subset of these variables the server will *automatically report changes
+to the value to the client driver and application*. These variables are known
+internally as `GUC_REPORT` variables after the name of the flag that enables
+the functionality.
+
+The server keeps track of all the variable scopes and reports when a variable
+reverts to a prior value, so the client doesn't have to guess what the current
+value is and whether some server-side function could've changed it.  Whenever
+the value changes, no matter why or how it changes, the server reports the new
+effective value in a *Parameter Status* protocol message to the client.  PgJDBC
+uses many of these reports internally.
+
+As of PgJDBC 42.2.6, it also exposes the parameter status information to user
+applications via the PGConnection extensions interface.
+
+## Methods
+
+Two methods on `org.postgresql.PGConnection` provide the client interface to
+reported parameters. Parameter names are case-insensitive and case-preserving.
+
+* `Map PGConnection.getParameterStatuses()` - return a map of all reported
+  parameters and their values.
+
+* `String PGConnection.getParameterStatus()` - shorthand to retrieve one
+  value by name, or null if no value has been reported.
+
+See the `PGConnection` JavaDoc for details.
+
+## Example
+
+If you're working directly with a `java.sql.Connection` you can
+
+    import org.postgresql.PGConnection;
+
+    void my_function(Connection conn) {
+
+        System.out.println("My application name is " +
+            ((PGConnection)conn).getParameterStatus("application_name"));
+
+    }
+
+## Other client drivers
+
+The `libpq` equivalent is the `PQparameterStatus(...)` API function.
diff --git a/docs/documentation/head/prepare.md b/docs/documentation/head/prepare.md
index d57b5253..b7b55991 100644
--- a/docs/documentation/head/prepare.md
+++ b/docs/documentation/head/prepare.md
@@ -21,5 +21,5 @@ default, and you must set `tcpip_socket = true` in the `postgresql.conf` file.
 Once you have made sure the server is correctly listening for TCP/IP connections
 the next step is to verify that users are allowed to connect to the server. Client
 authentication is setup in `pg_hba.conf`. Refer to the main PostgreSQL™ documentation
-for details. The JDBC driver supports the `trust`, `ident`, `password`, `md5`, and
-`crypt` authentication methods.
\ No newline at end of file
+for details. The JDBC driver supports the `trust`, `ident`, `password`, `md5`, `gss` and
+`crypt` authentication methods.
diff --git a/docs/documentation/head/query.md b/docs/documentation/head/query.md
index d6fde7f6..2bd8214c 100644
--- a/docs/documentation/head/query.md
+++ b/docs/documentation/head/query.md
@@ -81,7 +81,7 @@ exhausted the next block of rows is retrieved by repositioning the cursor.
 * The `Connection` must not be in autocommit mode. The backend closes cursors at
 	the end of transactions, so in autocommit mode the backend will have
 	closed the cursor before anything can be fetched from it.
-*The `Statement` must be created with a `ResultSet` type of `ResultSet.TYPE_FORWARD_ONLY`.
+* The `Statement` must be created with a `ResultSet` type of `ResultSet.TYPE_FORWARD_ONLY`.
 	This is the default, so no code will need to be rewritten to take advantage
 	of this, but it also means that you cannot scroll backwards or otherwise
 	jump around in the `ResultSet`.
diff --git a/docs/documentation/head/replication.md b/docs/documentation/head/replication.md
index 5d821fb2..223851df 100644
--- a/docs/documentation/head/replication.md
+++ b/docs/documentation/head/replication.md
@@ -3,8 +3,8 @@ layout: default_docs
 title: Physical and Logical replication API
 header: Chapter 9. PostgreSQL™ Extensions to the JDBC API
 resource: media
-previoustitle: Server Prepared Statements
-previous: server-prepare.html
+previoustitle: Parameter Status Messages
+previous: parameterstatus.html
 nexttitle: Arrays
 next: arrays.html
 ---
diff --git a/docs/documentation/head/resultset.md b/docs/documentation/head/resultset.md
index 58c5f5a4..2be54d19 100644
--- a/docs/documentation/head/resultset.md
+++ b/docs/documentation/head/resultset.md
@@ -16,4 +16,12 @@ The following must be considered when using the `ResultSet` interface:
 * You must close a `ResultSet` by calling `close()` once you have finished using
 	it.
 * Once you make another query with the `Statement` used to create a `ResultSet`,
-	the currently open `ResultSet` instance is closed automatically.
\ No newline at end of file
+	the currently open `ResultSet` instance is closed automatically.
+* When PreparedStatement API is used, `ResultSet` switches to binary mode after 
+	five query executions (this default is set by the `prepareThreshold` 
+	connection property, see [Server Prepared Statements](server-prepare.md)). 
+	This may cause unexpected behavior when some methods are called. For example, 
+	results on method calls such as `getString()` on non-string data types, 	
+	while logically　equivalent, may be formatted differently after execution exceeds 
+	the set `prepareThreshold` when conversion to object method switches to one 
+	matching the return mode. 
\ No newline at end of file
diff --git a/docs/documentation/head/server-prepare.md b/docs/documentation/head/server-prepare.md
index 76f1504e..c9a55953 100644
--- a/docs/documentation/head/server-prepare.md
+++ b/docs/documentation/head/server-prepare.md
@@ -5,8 +5,8 @@ header: Chapter 9. PostgreSQL™ Extensions to the JDBC API
 resource: media
 previoustitle: Listen / Notify
 previous: listennotify.html
-nexttitle: Physical and Logical replication API
-next: replication.html
+nexttitle: Parameter Status Messages
+next: parameterstatus.html
 ---
 
 ### Motivation
diff --git a/packaging/rpm/postgresql-jdbc.spec.tpl b/packaging/rpm/postgresql-jdbc.spec.tpl
index e0256ff4..45f81791 100644
--- a/packaging/rpm/postgresql-jdbc.spec.tpl
+++ b/packaging/rpm/postgresql-jdbc.spec.tpl
@@ -61,7 +61,6 @@ BuildArch:	noarch
 BuildRequires:	java-devel >= 1.8
 BuildRequires:	maven-local
 BuildRequires:	java-comment-preprocessor
-BuildRequires:	properties-maven-plugin
 BuildRequires:	maven-enforcer-plugin
 BuildRequires:	maven-plugin-bundle
 BuildRequires:	maven-plugin-build-helper
@@ -107,6 +106,9 @@ find -name "*.jar" -or -name "*.class" | xargs rm -f
 %pom_xpath_inject pom:parent "<relativePath>pgjdbc-parent-poms/pgjdbc-versions</relativePath>"
 %pom_xpath_set pom:relativePath ../pgjdbc-parent-poms/pgjdbc-core-parent pgjdbc
 %pom_xpath_remove "pom:plugin[pom:artifactId = 'maven-shade-plugin']" pgjdbc
+%pom_remove_plugin :karaf-maven-plugin pgjdbc
+%pom_remove_plugin :properties-maven-plugin pgjdbc-parent-poms/pgjdbc-core-parent
+%pom_remove_plugin :properties-maven-plugin pgjdbc-parent-poms/pgjdbc-versions
 
 # compat symlink: requested by dtardon (libreoffice), reverts part of
 # 0af97ce32de877 commit.
diff --git a/pgjdbc/pom.xml b/pgjdbc/pom.xml
index 8a859c38..128f39e4 100644
--- a/pgjdbc/pom.xml
+++ b/pgjdbc/pom.xml
@@ -3,14 +3,14 @@
   <parent>
     <groupId>org.postgresql</groupId>
     <artifactId>pgjdbc-core-parent</artifactId>
-    <version>1.1.5</version>
+    <version>1.1.6</version>
     <relativePath />
   </parent>
 
   <artifactId>postgresql</artifactId>
   <packaging>bundle</packaging>
   <name>PostgreSQL JDBC Driver - JDBC 4.2</name>
-  <version>42.2.7-SNAPSHOT</version>
+  <version>42.2.9-SNAPSHOT</version>
   <description>Java JDBC 4.2 (JRE 8+) driver for PostgreSQL database</description>
   <url>https://github.com/pgjdbc/pgjdbc</url>
 
@@ -40,7 +40,7 @@
     <dependency>
       <groupId>com.ongres.scram</groupId>
       <artifactId>client</artifactId>
-      <version>1.0.0-beta.2</version>
+      <version>2.1</version>
     </dependency>
   </dependencies>
 
@@ -240,6 +240,17 @@
           <outputEncoding>UTF-8</outputEncoding>
         </configuration>
       </plugin>
+      <plugin>
+        <groupId>org.apache.maven.plugins</groupId>
+        <artifactId>maven-jar-plugin</artifactId>
+        <configuration>
+          <archive>
+            <manifestEntries>
+              <Automatic-Module-Name>org.postgresql.jdbc</Automatic-Module-Name>
+            </manifestEntries>
+          </archive>
+        </configuration>
+      </plugin>
       <plugin>
         <groupId>org.apache.maven.plugins</groupId>
         <artifactId>maven-shade-plugin</artifactId>
@@ -290,6 +301,27 @@
           </execution>
         </executions>
       </plugin>
+      <plugin>
+        <groupId>org.apache.karaf.tooling</groupId>
+        <artifactId>karaf-maven-plugin</artifactId>
+        <version>4.2.5</version>
+        <extensions>true</extensions>
+        <configuration>
+          <startLevel>80</startLevel>
+          <includeTransitiveDependency>false</includeTransitiveDependency>
+          <aggregateFeatures>false</aggregateFeatures>
+          <includeProjectArtifact>true</includeProjectArtifact>
+        </configuration>
+        <executions>
+          <execution>
+            <id>generate-features-file</id>
+            <phase>package</phase>
+            <goals>
+              <goal>features-generate-descriptor</goal>
+            </goals>
+          </execution>
+        </executions>
+      </plugin>
     </plugins>
 
     <pluginManagement>
@@ -297,7 +329,7 @@
         <plugin>
           <groupId>org.apache.maven.plugins</groupId>
           <artifactId>maven-checkstyle-plugin</artifactId>
-          <version>3.0.0</version>
+          <version>3.1.0</version>
           <dependencies>
             <dependency>
               <groupId>com.puppycrawl.tools</groupId>
diff --git a/pgjdbc/src/main/feature/feature.xml b/pgjdbc/src/main/feature/feature.xml
new file mode 100644
index 00000000..1beb662e
--- /dev/null
+++ b/pgjdbc/src/main/feature/feature.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<features xmlns="http://karaf.apache.org/xmlns/features/v1.4.0">
+  <feature name="postgresql" description="PostgreSQL JDBC driver karaf feature" version="${project.version}">
+    <feature>transaction-api</feature>
+  </feature>
+</features>
diff --git a/pgjdbc/src/main/java/org/postgresql/PGConnection.java b/pgjdbc/src/main/java/org/postgresql/PGConnection.java
index ba2a04e9..fd764563 100644
--- a/pgjdbc/src/main/java/org/postgresql/PGConnection.java
+++ b/pgjdbc/src/main/java/org/postgresql/PGConnection.java
@@ -17,6 +17,8 @@ import java.sql.Array;
 import java.sql.SQLException;
 import java.sql.Statement;
 
+import java.util.Map;
+
 /**
  * This interface defines the public PostgreSQL extensions to java.sql.Connection. All Connections
  * returned by the PostgreSQL driver implement PGConnection.
@@ -236,4 +238,76 @@ public interface PGConnection {
    * @return replication API for the current connection
    */
   PGReplicationConnection getReplicationAPI();
+
+  /**
+   * <p>Returns the current values of all parameters reported by the server.</p>
+   *
+   * <p>PostgreSQL reports values for a subset of parameters (GUCs) to the client
+   * at connect-time, then sends update messages whenever the values change
+   * during a session. PgJDBC records the latest values and exposes it to client
+   * applications via <code>getParameterStatuses()</code>.</p>
+   *
+   * <p>PgJDBC exposes individual accessors for some of these parameters as
+   * listed below. They are more backwarrds-compatible and should be preferred
+   * where possible.</p>
+   *
+   * <p>Not all parameters are reported, only those marked
+   * <code>GUC_REPORT</code> in the source code. The <code>pg_settings</code>
+   * view does not expose information about which parameters are reportable.
+   * PgJDBC's map will only contain the parameters the server reports values
+   * for, so you cannot use this method as a substitute for running a
+   * <code>SHOW paramname;</code> or <code>SELECT
+   * current_setting('paramname');</code> query for arbitrary parameters.</p>
+   *
+   * <p>Parameter names are <i>case-insensitive</i> and <i>case-preserving</i>
+   * in this map, like in PostgreSQL itself. So <code>DateStyle</code> and
+   * <code>datestyle</code> are the same key.</p>
+   *
+   * <p>
+   *  As of PostgreSQL 11 the reportable parameter list, and related PgJDBC
+   *  interfaces or accesors, are:
+   * </p>
+   *
+   * <ul>
+   *  <li>
+   *    <code>application_name</code> -
+   *    {@link java.sql.Connection#getClientInfo()},
+   *    {@link java.sql.Connection#setClientInfo(java.util.Properties)}
+   *    and <code>ApplicationName</code> connection property.
+   *  </li>
+   *  <li>
+   *    <code>client_encoding</code> - PgJDBC always sets this to <code>UTF8</code>.
+   *    See <code>allowEncodingChanges</code> connection property.
+   *  </li>
+   *  <li><code>DateStyle</code> - PgJDBC requires this to always be set to <code>ISO</code></li>
+   *  <li><code>standard_conforming_strings</code> - indirectly via {@link #escapeLiteral(String)}</li>
+   *  <li>
+   *    <code>TimeZone</code> - set from JDK timezone see {@link java.util.TimeZone#getDefault()}
+   *    and {@link java.util.TimeZone#setDefault(TimeZone)}
+   *  </li>
+   *  <li><code>integer_datetimes</code></li>
+   *  <li><code>IntervalStyle</code></li>
+   *  <li><code>server_encoding</code></li>
+   *  <li><code>server_version</code></li>
+   *  <li><code>is_superuser</code> </li>
+   *  <li><code>session_authorization</code></li>
+   * </ul>
+   *
+   * <p>Note that some PgJDBC operations will change server parameters
+   * automatically.</p>
+   *
+   * @return unmodifiable map of case-insensitive parameter names to parameter values
+   * @since 42.2.6
+   */
+  Map<String,String> getParameterStatuses();
+
+  /**
+   * Shorthand for getParameterStatuses().get(...) .
+   *
+   * @param parameterName case-insensitive parameter name
+   * @return parameter value if defined, or null if no parameter known
+   * @see #getParameterStatuses
+   * @since 42.2.6
+   */
+  String getParameterStatus(String parameterName);
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/PGProperty.java b/pgjdbc/src/main/java/org/postgresql/PGProperty.java
index 37d2f950..b342b44c 100644
--- a/pgjdbc/src/main/java/org/postgresql/PGProperty.java
+++ b/pgjdbc/src/main/java/org/postgresql/PGProperty.java
@@ -367,10 +367,10 @@ public enum PGProperty {
   ALLOW_ENCODING_CHANGES("allowEncodingChanges", "false", "Allow for changes in client_encoding"),
 
   /**
-   * Specify the schema to be set in the search-path. This schema will be used to resolve
+   * Specify the schema (or several schema separated by commas) to be set in the search-path. This schema will be used to resolve
    * unqualified object names used in statements over this connection.
    */
-  CURRENT_SCHEMA("currentSchema", null, "Specify the schema to be set in the search-path"),
+  CURRENT_SCHEMA("currentSchema", null, "Specify the schema (or several schema separated by commas) to be set in the search-path"),
 
   TARGET_SERVER_TYPE("targetServerType", "any", "Specifies what kind of server to connect", false,
       "any", "master", "slave", "secondary",  "preferSlave", "preferSecondary"),
diff --git a/pgjdbc/src/main/java/org/postgresql/core/PGStream.java b/pgjdbc/src/main/java/org/postgresql/core/PGStream.java
index 7ed6fc3e..d8bcb3a6 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/PGStream.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/PGStream.java
@@ -115,25 +115,39 @@ public class PGStream implements Closeable, Flushable {
    * @throws IOException if something wrong happens
    */
   public boolean hasMessagePending() throws IOException {
+
+    boolean available = false;
+
+    // In certain cases, available returns 0, yet there are bytes
     if (pgInput.available() > 0) {
       return true;
     }
-    // In certain cases, available returns 0, yet there are bytes
-    long now = System.currentTimeMillis();
+    long now = System.nanoTime() / 1000000;
+
     if (now < nextStreamAvailableCheckTime && minStreamAvailableCheckDelay != 0) {
       // Do not use ".peek" too often
       return false;
     }
-    nextStreamAvailableCheckTime = now + minStreamAvailableCheckDelay;
+
     int soTimeout = getNetworkTimeout();
     setNetworkTimeout(1);
     try {
-      return pgInput.peek() != -1;
+      available = (pgInput.peek() != -1);
     } catch (SocketTimeoutException e) {
       return false;
     } finally {
       setNetworkTimeout(soTimeout);
     }
+
+    /*
+    If none available then set the next check time
+    In the event that there more async bytes available we will continue to get them all
+    see issue 1547 https://github.com/pgjdbc/pgjdbc/issues/1547
+     */
+    if (!available) {
+      nextStreamAvailableCheckTime = now + minStreamAvailableCheckDelay;
+    }
+    return available;
   }
 
   public void setMinStreamAvailableCheckDelay(int delay) {
diff --git a/pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java b/pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java
index d6db7292..8d95e8d2 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/QueryExecutor.java
@@ -18,6 +18,7 @@ import java.io.IOException;
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 import java.util.TimeZone;
 
@@ -452,4 +453,9 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
   void setNetworkTimeout(int milliseconds) throws IOException;
 
   int getNetworkTimeout() throws IOException;
+
+  // Expose parameter status to PGConnection
+  Map<String,String> getParameterStatuses();
+
+  String getParameterStatus(String parameterName);
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/core/QueryExecutorBase.java b/pgjdbc/src/main/java/org/postgresql/core/QueryExecutorBase.java
index 8fe0bbb8..fbe5c498 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/QueryExecutorBase.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/QueryExecutorBase.java
@@ -19,7 +19,10 @@ import java.io.IOException;
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Map;
 import java.util.Properties;
+import java.util.TreeMap;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -52,6 +55,10 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   private final LruCache<Object, CachedQuery> statementCache;
   private final CachedQueryCreateAction cachedQueryCreateAction;
 
+  // For getParameterStatuses(), GUC_REPORT tracking
+  private final TreeMap<String,String> parameterStatuses
+      = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
+
   protected QueryExecutorBase(PGStream pgStream, String user,
       String database, int cancelSignalTimeout, Properties info) throws SQLException {
     this.pgStream = pgStream;
@@ -344,6 +351,10 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   }
 
   protected boolean willHealViaReparse(SQLException e) {
+    if (e == null || e.getSQLState() == null) {
+      return false;
+    }
+
     // "prepared statement \"S_2\" does not exist"
     if (PSQLState.INVALID_SQL_STATEMENT_NAME.getState().equals(e.getSQLState())) {
       return true;
@@ -389,4 +400,42 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   protected boolean hasNotifications() {
     return notifications.size() > 0;
   }
+
+  @Override
+  public final Map<String,String> getParameterStatuses() {
+    return Collections.unmodifiableMap(parameterStatuses);
+  }
+
+  @Override
+  public final String getParameterStatus(String parameterName) {
+    return parameterStatuses.get(parameterName);
+  }
+
+  /**
+   * Update the parameter status map in response to a new ParameterStatus
+   * wire protocol message.
+   *
+   * <p>The server sends ParameterStatus messages when GUC_REPORT settings are
+   * initially assigned and whenever they change.</p>
+   *
+   * <p>A future version may invoke a client-defined listener class at this point,
+   * so this should be the only access path.</p>
+   *
+   * <p>Keys are case-insensitive and case-preserving.</p>
+   *
+   * <p>The server doesn't provide a way to report deletion of a reportable
+   * parameter so we don't expose one here.</p>
+   *
+   * @param parameterName case-insensitive case-preserving name of parameter to create or update
+   * @param parameterStatus new value of parameter
+   * @see org.postgresql.PGConnection#getParameterStatuses
+   * @see org.postgresql.PGConnection#getParameterStatus
+   */
+  protected void onParameterStatus(String parameterName, String parameterStatus) {
+    if (parameterName == null || parameterName.equals("")) {
+      throw new IllegalStateException("attempt to set GUC_REPORT parameter with null or empty-string name");
+    }
+
+    parameterStatuses.put(parameterName, parameterStatus);
+  }
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/core/ResultHandler.java b/pgjdbc/src/main/java/org/postgresql/core/ResultHandler.java
index 4b9e01c4..cb25afdf 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/ResultHandler.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/ResultHandler.java
@@ -48,7 +48,7 @@ public interface ResultHandler {
    * @param insertOID for a single-row INSERT query, the OID of the newly inserted row; 0 if not
    *        available.
    */
-  void handleCommandStatus(String status, int updateCount, long insertOID);
+  void handleCommandStatus(String status, long updateCount, long insertOID);
 
   /**
    * Called when a warning is emitted.
diff --git a/pgjdbc/src/main/java/org/postgresql/core/ResultHandlerBase.java b/pgjdbc/src/main/java/org/postgresql/core/ResultHandlerBase.java
index d16da9e8..c6dd206d 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/ResultHandlerBase.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/ResultHandlerBase.java
@@ -30,7 +30,7 @@ public class ResultHandlerBase implements ResultHandler {
   }
 
   @Override
-  public void handleCommandStatus(String status, int updateCount, long insertOID) {
+  public void handleCommandStatus(String status, long updateCount, long insertOID) {
   }
 
   @Override
diff --git a/pgjdbc/src/main/java/org/postgresql/core/ResultHandlerDelegate.java b/pgjdbc/src/main/java/org/postgresql/core/ResultHandlerDelegate.java
index 34e96d18..926e59bc 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/ResultHandlerDelegate.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/ResultHandlerDelegate.java
@@ -31,7 +31,7 @@ public class ResultHandlerDelegate implements ResultHandler {
   }
 
   @Override
-  public void handleCommandStatus(String status, int updateCount, long insertOID) {
+  public void handleCommandStatus(String status, long updateCount, long insertOID) {
     if (delegate != null) {
       delegate.handleCommandStatus(status, updateCount, insertOID);
     }
diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
index 78cdcc4d..662cc263 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
@@ -90,9 +90,6 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
 
     PGStream newStream = new PGStream(socketFactory, hostSpec, connectTimeout);
 
-    // Construct and send an ssl startup packet if requested.
-    newStream = enableSSL(newStream, sslMode, info, connectTimeout);
-
     // Set the socket timeout if the "socketTimeout" property has been set.
     int socketTimeout = PGProperty.SOCKET_TIMEOUT.getInt(info);
     if (socketTimeout > 0) {
@@ -134,6 +131,9 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
       LOGGER.log(Level.FINE, "Send Buffer Size is {0}", newStream.getSocket().getSendBufferSize());
     }
 
+    // Construct and send an ssl startup packet if requested.
+    newStream = enableSSL(newStream, sslMode, info, connectTimeout);
+
     List<String[]> paramList = getParametersForStartup(user, database, info);
     sendStartupPacket(newStream, paramList);
 
@@ -495,9 +495,9 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
     /* SSPI negotiation state, if used */
     ISSPIClient sspiClient = null;
 
-    //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+    //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
     /* SCRAM authentication state, if used */
-    org.postgresql.jre8.sasl.ScramAuthenticator scramAuthenticator = null;
+    org.postgresql.jre7.sasl.ScramAuthenticator scramAuthenticator = null;
     //#endif
 
     try {
@@ -661,8 +661,8 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
               case AUTH_REQ_SASL:
                 LOGGER.log(Level.FINEST, " <=BE AuthenticationSASL");
 
-                //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
-                scramAuthenticator = new org.postgresql.jre8.sasl.ScramAuthenticator(user, password, pgStream);
+                //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
+                scramAuthenticator = new org.postgresql.jre7.sasl.ScramAuthenticator(user, password, pgStream);
                 scramAuthenticator.processServerMechanismsAndInit();
                 scramAuthenticator.sendScramClientFirstMessage();
                 // This works as follows:
@@ -674,12 +674,12 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
                           "SCRAM authentication is not supported by this driver. You need JDK >= 8 and pgjdbc >= 42.2.0 (not \".jre\" versions)",
                           areq), PSQLState.CONNECTION_REJECTED);
                   //#endif
-                  //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+                  //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
                 }
                 break;
                 //#endif
 
-              //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+              //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
               case AUTH_REQ_SASL_CONTINUE:
                 scramAuthenticator.processServerFirstMessage(msgLen - 4 - 4);
                 break;
diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
index f43bb78b..13326c98 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
@@ -51,7 +51,6 @@ import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.sql.SQLException;
 import java.sql.SQLWarning;
-import java.sql.Statement;
 import java.util.ArrayDeque;
 import java.util.ArrayList;
 import java.util.Collections;
@@ -62,6 +61,7 @@ import java.util.List;
 import java.util.Properties;
 import java.util.Set;
 import java.util.TimeZone;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
@@ -518,6 +518,9 @@ public class QueryExecutorImpl extends QueryExecutorBase {
 
     try {
       handler.handleCompletion();
+      if (cleanupSavePoints) {
+        releaseSavePoint(autosave, flags);
+      }
     } catch (SQLException e) {
       rollbackIfRequired(autosave, e);
     }
@@ -557,7 +560,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
         }
       }
 
-      public void handleCommandStatus(String status, int updateCount, long insertOID) {
+      @Override
+      public void handleCommandStatus(String status, long updateCount, long insertOID) {
         if (!sawBegin) {
           sawBegin = true;
           if (!status.equals("BEGIN")) {
@@ -599,7 +603,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
       ResultHandler handler = new ResultHandlerBase() {
         private boolean sawBegin = false;
 
-        public void handleCommandStatus(String status, int updateCount, long insertOID) {
+        @Override
+        public void handleCommandStatus(String status, long updateCount, long insertOID) {
           if (!sawBegin) {
             if (!status.equals("BEGIN")) {
               handleError(
@@ -613,6 +618,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
           }
         }
 
+        @Override
         public void handleWarning(SQLWarning warning) {
           // we don't want to ignore warnings and it would be tricky
           // to chain them back to the connection, so since we don't
@@ -1023,7 +1029,6 @@ public class QueryExecutorImpl extends QueryExecutorBase {
       pgStream.sendInteger4(siz + 4);
       pgStream.send(data, off, siz);
 
-      processCopyResults(op, false); // collect any pending notifications without blocking
     } catch (IOException ioe) {
       throw new PSQLException(GT.tr("Database connection failed when writing to copy"),
           PSQLState.CONNECTION_FAILURE, ioe);
@@ -1038,7 +1043,6 @@ public class QueryExecutorImpl extends QueryExecutorBase {
 
     try {
       pgStream.flush();
-      processCopyResults(op, false); // collect any pending notifications without blocking
     } catch (IOException ioe) {
       throw new PSQLException(GT.tr("Database connection failed when writing to copy"),
           PSQLState.CONNECTION_FAILURE, ioe);
@@ -1067,6 +1071,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     }
   }
 
+  AtomicBoolean processingCopyResults = new AtomicBoolean(false);
+
   /**
    * Handles copy sub protocol responses from server. Unlocks at end of sub protocol, so operations
    * on pgStream or QueryExecutor are not allowed in a method after calling this!
@@ -1080,205 +1086,226 @@ public class QueryExecutorImpl extends QueryExecutorBase {
   CopyOperationImpl processCopyResults(CopyOperationImpl op, boolean block)
       throws SQLException, IOException {
 
-    boolean endReceiving = false;
-    SQLException error = null;
-    SQLException errors = null;
-    int len;
-
-    while (!endReceiving && (block || pgStream.hasMessagePending())) {
-
-      // There is a bug in the server's implementation of the copy
-      // protocol. It returns command complete immediately upon
-      // receiving the EOF marker in the binary protocol,
-      // potentially before we've issued CopyDone. When we are not
-      // blocking, we don't think we are done, so we hold off on
-      // processing command complete and any subsequent messages
-      // until we actually are done with the copy.
-      //
-      if (!block) {
-        int c = pgStream.peekChar();
-        if (c == 'C') {
-          // CommandComplete
-          LOGGER.log(Level.FINEST, " <=BE CommandStatus, Ignored until CopyDone");
-          break;
+    /*
+    *  This is a hack as we should not end up here, but sometimes do with large copy operations.
+     */
+    if ( processingCopyResults.compareAndSet(false,true) == false ) {
+      LOGGER.log(Level.INFO, "Ignoring request to process copy results, already processing");
+      return null;
+    }
+
+    // put this all in a try, finally block and reset the processingCopyResults in the finally clause
+    try {
+      boolean endReceiving = false;
+      SQLException error = null;
+      SQLException errors = null;
+      int len;
+
+      while (!endReceiving && (block || pgStream.hasMessagePending())) {
+
+        // There is a bug in the server's implementation of the copy
+        // protocol. It returns command complete immediately upon
+        // receiving the EOF marker in the binary protocol,
+        // potentially before we've issued CopyDone. When we are not
+        // blocking, we don't think we are done, so we hold off on
+        // processing command complete and any subsequent messages
+        // until we actually are done with the copy.
+        //
+        if (!block) {
+          int c = pgStream.peekChar();
+          if (c == 'C') {
+            // CommandComplete
+            LOGGER.log(Level.FINEST, " <=BE CommandStatus, Ignored until CopyDone");
+            break;
+          }
         }
-      }
 
-      int c = pgStream.receiveChar();
-      switch (c) {
+        int c = pgStream.receiveChar();
+        switch (c) {
 
-        case 'A': // Asynchronous Notify
+          case 'A': // Asynchronous Notify
 
-          LOGGER.log(Level.FINEST, " <=BE Asynchronous Notification while copying");
+            LOGGER.log(Level.FINEST, " <=BE Asynchronous Notification while copying");
 
-          receiveAsyncNotify();
-          break;
+            receiveAsyncNotify();
+            break;
 
-        case 'N': // Notice Response
+          case 'N': // Notice Response
 
-          LOGGER.log(Level.FINEST, " <=BE Notification while copying");
+            LOGGER.log(Level.FINEST, " <=BE Notification while copying");
 
-          addWarning(receiveNoticeResponse());
-          break;
+            addWarning(receiveNoticeResponse());
+            break;
 
-        case 'C': // Command Complete
+          case 'C': // Command Complete
 
-          String status = receiveCommandStatus();
+            String status = receiveCommandStatus();
 
-          try {
-            if (op == null) {
-              throw new PSQLException(GT
+            try {
+              if (op == null) {
+                throw new PSQLException(GT
                   .tr("Received CommandComplete ''{0}'' without an active copy operation", status),
                   PSQLState.OBJECT_NOT_IN_STATE);
+              }
+              op.handleCommandStatus(status);
+            } catch (SQLException se) {
+              error = se;
             }
-            op.handleCommandStatus(status);
-          } catch (SQLException se) {
-            error = se;
-          }
 
-          block = true;
-          break;
+            block = true;
+            break;
 
-        case 'E': // ErrorMessage (expected response to CopyFail)
+          case 'E': // ErrorMessage (expected response to CopyFail)
 
-          error = receiveErrorResponse();
-          // We've received the error and we now expect to receive
-          // Ready for query, but we must block because it might still be
-          // on the wire and not here yet.
-          block = true;
-          break;
+            error = receiveErrorResponse();
+            // We've received the error and we now expect to receive
+            // Ready for query, but we must block because it might still be
+            // on the wire and not here yet.
+            block = true;
+            break;
 
-        case 'G': // CopyInResponse
+          case 'G': // CopyInResponse
 
-          LOGGER.log(Level.FINEST, " <=BE CopyInResponse");
+            LOGGER.log(Level.FINEST, " <=BE CopyInResponse");
 
-          if (op != null) {
-            error = new PSQLException(GT.tr("Got CopyInResponse from server during an active {0}",
+            if (op != null) {
+              error = new PSQLException(GT.tr("Got CopyInResponse from server during an active {0}",
                 op.getClass().getName()), PSQLState.OBJECT_NOT_IN_STATE);
-          }
+            }
 
-          op = new CopyInImpl();
-          initCopy(op);
-          endReceiving = true;
-          break;
+            op = new CopyInImpl();
+            initCopy(op);
+            endReceiving = true;
+            break;
 
-        case 'H': // CopyOutResponse
+          case 'H': // CopyOutResponse
 
-          LOGGER.log(Level.FINEST, " <=BE CopyOutResponse");
+            LOGGER.log(Level.FINEST, " <=BE CopyOutResponse");
 
-          if (op != null) {
-            error = new PSQLException(GT.tr("Got CopyOutResponse from server during an active {0}",
-                op.getClass().getName()), PSQLState.OBJECT_NOT_IN_STATE);
-          }
+            if (op != null) {
+              error =
+                new PSQLException(GT.tr("Got CopyOutResponse from server during an active {0}",
+                  op.getClass().getName()), PSQLState.OBJECT_NOT_IN_STATE);
+            }
 
-          op = new CopyOutImpl();
-          initCopy(op);
-          endReceiving = true;
-          break;
+            op = new CopyOutImpl();
+            initCopy(op);
+            endReceiving = true;
+            break;
 
-        case 'W': // CopyBothResponse
+          case 'W': // CopyBothResponse
 
-          LOGGER.log(Level.FINEST, " <=BE CopyBothResponse");
+            LOGGER.log(Level.FINEST, " <=BE CopyBothResponse");
 
-          if (op != null) {
-            error = new PSQLException(GT.tr("Got CopyBothResponse from server during an active {0}",
-                    op.getClass().getName()), PSQLState.OBJECT_NOT_IN_STATE);
-          }
+            if (op != null) {
+              error =
+                new PSQLException(GT.tr("Got CopyBothResponse from server during an active {0}",
+                  op.getClass().getName()), PSQLState.OBJECT_NOT_IN_STATE);
+            }
 
-          op = new CopyDualImpl();
-          initCopy(op);
-          endReceiving = true;
-          break;
+            op = new CopyDualImpl();
+            initCopy(op);
+            endReceiving = true;
+            break;
 
-        case 'd': // CopyData
+          case 'd': // CopyData
 
-          LOGGER.log(Level.FINEST, " <=BE CopyData");
+            LOGGER.log(Level.FINEST, " <=BE CopyData");
+
+            len = pgStream.receiveInteger4() - 4;
+
+            assert len > 0 : "Copy Data length must be greater than 4";
 
-          len = pgStream.receiveInteger4() - 4;
-          byte[] buf = pgStream.receive(len);
-          if (op == null) {
-            error = new PSQLException(GT.tr("Got CopyData without an active copy operation"),
+            byte[] buf = pgStream.receive(len);
+            if (op == null) {
+              error = new PSQLException(GT.tr("Got CopyData without an active copy operation"),
                 PSQLState.OBJECT_NOT_IN_STATE);
-          } else if (!(op instanceof CopyOut)) {
-            error = new PSQLException(
+            } else if (!(op instanceof CopyOut)) {
+              error = new PSQLException(
                 GT.tr("Unexpected copydata from server for {0}", op.getClass().getName()),
                 PSQLState.COMMUNICATION_ERROR);
-          } else {
-            op.handleCopydata(buf);
-          }
-          endReceiving = true;
-          break;
+            } else {
+              op.handleCopydata(buf);
+            }
+            endReceiving = true;
+            break;
 
-        case 'c': // CopyDone (expected after all copydata received)
+          case 'c': // CopyDone (expected after all copydata received)
 
-          LOGGER.log(Level.FINEST, " <=BE CopyDone");
+            LOGGER.log(Level.FINEST, " <=BE CopyDone");
 
-          len = pgStream.receiveInteger4() - 4;
-          if (len > 0) {
-            pgStream.receive(len); // not in specification; should never appear
-          }
+            len = pgStream.receiveInteger4() - 4;
+            if (len > 0) {
+              pgStream.receive(len); // not in specification; should never appear
+            }
 
-          if (!(op instanceof CopyOut)) {
-            error = new PSQLException("Got CopyDone while not copying from server",
+            if (!(op instanceof CopyOut)) {
+              error = new PSQLException("Got CopyDone while not copying from server",
                 PSQLState.OBJECT_NOT_IN_STATE);
-          }
+            }
 
-          // keep receiving since we expect a CommandComplete
-          block = true;
-          break;
-        case 'S': // Parameter Status
-          try {
-            receiveParameterStatus();
-          } catch (SQLException e) {
-            error = e;
-            endReceiving = true;
-          }
-          break;
+            // keep receiving since we expect a CommandComplete
+            block = true;
+            break;
+          case 'S': // Parameter Status
+            try {
+              receiveParameterStatus();
+            } catch (SQLException e) {
+              error = e;
+              endReceiving = true;
+            }
+            break;
 
-        case 'Z': // ReadyForQuery: After FE:CopyDone => BE:CommandComplete
+          case 'Z': // ReadyForQuery: After FE:CopyDone => BE:CommandComplete
 
-          receiveRFQ();
-          if (hasLock(op)) {
-            unlock(op);
-          }
-          op = null;
-          endReceiving = true;
-          break;
+            receiveRFQ();
+            if (hasLock(op)) {
+              unlock(op);
+            }
+            op = null;
+            endReceiving = true;
+            break;
 
-        // If the user sends a non-copy query, we've got to handle some additional things.
-        //
-        case 'T': // Row Description (response to Describe)
-          LOGGER.log(Level.FINEST, " <=BE RowDescription (during copy ignored)");
+          // If the user sends a non-copy query, we've got to handle some additional things.
+          //
+          case 'T': // Row Description (response to Describe)
+            LOGGER.log(Level.FINEST, " <=BE RowDescription (during copy ignored)");
 
-          skipMessage();
-          break;
+            skipMessage();
+            break;
 
-        case 'D': // DataRow
-          LOGGER.log(Level.FINEST, " <=BE DataRow (during copy ignored)");
+          case 'D': // DataRow
+            LOGGER.log(Level.FINEST, " <=BE DataRow (during copy ignored)");
 
-          skipMessage();
-          break;
+            skipMessage();
+            break;
 
-        default:
-          throw new IOException(
+          default:
+            throw new IOException(
               GT.tr("Unexpected packet type during copy: {0}", Integer.toString(c)));
-      }
+        }
 
-      // Collect errors into a neat chain for completeness
-      if (error != null) {
-        if (errors != null) {
-          error.setNextException(errors);
+        // Collect errors into a neat chain for completeness
+        if (error != null) {
+          if (errors != null) {
+            error.setNextException(errors);
+          }
+          errors = error;
+          error = null;
         }
-        errors = error;
-        error = null;
       }
-    }
 
-    if (errors != null) {
-      throw errors;
-    }
+      if (errors != null) {
+        throw errors;
+      }
+      return op;
 
-    return op;
+    } finally {
+      /*
+      reset here in the finally block to make sure it really is cleared
+       */
+      processingCopyResults.set(false);
+    }
   }
 
   /*
@@ -2370,6 +2397,9 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    */
   private void skipMessage() throws IOException {
     int len = pgStream.receiveInteger4();
+
+    assert len >= 4 : "Length from skip message must be at least 4 ";
+
     // skip len-4 (length includes the 4 bytes for message length itself
     pgStream.skip(len - 4);
   }
@@ -2383,7 +2413,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     // (if the fetch returns no rows, we see just a CommandStatus..)
     final ResultHandler delegateHandler = handler;
     handler = new ResultHandlerDelegate(delegateHandler) {
-      public void handleCommandStatus(String status, int updateCount, long insertOID) {
+      @Override
+      public void handleCommandStatus(String status, long updateCount, long insertOID) {
         handleResultRows(portal.getQuery(), null, new ArrayList<byte[][]>(), null);
       }
     };
@@ -2440,7 +2471,9 @@ public class QueryExecutorImpl extends QueryExecutorBase {
   }
 
   private void receiveAsyncNotify() throws IOException {
-    pgStream.receiveInteger4(); // MESSAGE SIZE
+    int len = pgStream.receiveInteger4(); // MESSAGE SIZE
+    assert len > 4 : "Length for AsyncNotify must be at least 4";
+
     int pid = pgStream.receiveInteger4();
     String msg = pgStream.receiveString();
     String param = pgStream.receiveString();
@@ -2458,6 +2491,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     // check at the bottom to see if we need to throw an exception
 
     int elen = pgStream.receiveInteger4();
+    assert elen > 4 : "Error response length must be greater than 4";
+
     EncodingPredictor.DecodeResult totalMessage = pgStream.receiveErrorString(elen - 4);
     ServerErrorMessage errorMsg = new ServerErrorMessage(totalMessage);
 
@@ -2476,6 +2511,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
 
   private SQLWarning receiveNoticeResponse() throws IOException {
     int nlen = pgStream.receiveInteger4();
+    assert nlen > 4 : "Notice Response length must be greater than 4";
+
     ServerErrorMessage warnMsg = new ServerErrorMessage(pgStream.receiveString(nlen - 4));
 
     if (LOGGER.isLoggable(Level.FINEST)) {
@@ -2508,16 +2545,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     long oid = commandCompleteParser.getOid();
     long count = commandCompleteParser.getRows();
 
-    int countAsInt = 0;
-    if (count > Integer.MAX_VALUE) {
-      // If command status indicates that we've modified more than Integer.MAX_VALUE rows
-      // then we set the result count to reflect that we cannot provide the actual number
-      // due to the JDBC field being an int rather than a long.
-      countAsInt = Statement.SUCCESS_NO_INFO;
-    } else if (count > 0) {
-      countAsInt = (int) count;
-    }
-    handler.handleCommandStatus(status, countAsInt, oid);
+    handler.handleCommandStatus(status, count, oid);
   }
 
   private void receiveRFQ() throws IOException {
@@ -2619,6 +2647,11 @@ public class QueryExecutorImpl extends QueryExecutorBase {
       LOGGER.log(Level.FINEST, " <=BE ParameterStatus({0} = {1})", new Object[]{name, value});
     }
 
+    /* Update client-visible parameter status map for getParameterStatuses() */
+    if (name != null && !name.equals("")) {
+      onParameterStatus(name, value);
+    }
+
     if (name.equals("client_encoding")) {
       if (allowEncodingChanges) {
         if (!value.equalsIgnoreCase("UTF8") && !value.equalsIgnoreCase("UTF-8")) {
diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java b/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
index 11b4defb..66fd2f5e 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
@@ -25,6 +25,8 @@ public class V3PGReplicationStream implements PGReplicationStream {
 
   private static final Logger LOGGER = Logger.getLogger(V3PGReplicationStream.class.getName());
   public static final long POSTGRES_EPOCH_2000_01_01 = 946684800000L;
+  private static final long NANOS_PER_MILLISECOND = 1000000L;
+
   private final CopyDual copyDual;
   private final long updateInterval;
   private final ReplicationType replicationType;
@@ -55,8 +57,8 @@ public class V3PGReplicationStream implements PGReplicationStream {
       ReplicationType replicationType
   ) {
     this.copyDual = copyDual;
-    this.updateInterval = updateIntervalMs;
-    this.lastStatusUpdate = System.currentTimeMillis() - updateIntervalMs;
+    this.updateInterval = updateIntervalMs * NANOS_PER_MILLISECOND;
+    this.lastStatusUpdate = System.nanoTime() - (updateIntervalMs * NANOS_PER_MILLISECOND);
     this.lastReceiveLSN = startLSN;
     this.replicationType = replicationType;
   }
@@ -117,12 +119,13 @@ public class V3PGReplicationStream implements PGReplicationStream {
   private ByteBuffer readInternal(boolean block) throws SQLException {
     boolean updateStatusRequired = false;
     while (copyDual.isActive()) {
+
+      ByteBuffer buffer = receiveNextData(block);
+
       if (updateStatusRequired || isTimeUpdate()) {
         timeUpdateStatus();
       }
 
-      ByteBuffer buffer = receiveNextData(block);
-
       if (buffer == null) {
         return null;
       }
@@ -173,7 +176,7 @@ public class V3PGReplicationStream implements PGReplicationStream {
     if ( updateInterval == 0 ) {
       return false;
     }
-    long diff = System.currentTimeMillis() - lastStatusUpdate;
+    long diff = System.nanoTime() - lastStatusUpdate;
     return diff >= updateInterval;
   }
 
@@ -189,14 +192,14 @@ public class V3PGReplicationStream implements PGReplicationStream {
     copyDual.writeToCopy(reply, 0, reply.length);
     copyDual.flushCopy();
 
-    lastStatusUpdate = System.currentTimeMillis();
+    lastStatusUpdate = System.nanoTime();
   }
 
   private byte[] prepareUpdateStatus(LogSequenceNumber received, LogSequenceNumber flushed,
       LogSequenceNumber applied, boolean replyRequired) {
     ByteBuffer byteBuffer = ByteBuffer.allocate(1 + 8 + 8 + 8 + 8 + 1);
 
-    long now = System.currentTimeMillis();
+    long now = System.nanoTime() / NANOS_PER_MILLISECOND;
     long systemClock = TimeUnit.MICROSECONDS.convert((now - POSTGRES_EPOCH_2000_01_01),
         TimeUnit.MICROSECONDS);
 
diff --git a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
index ab64f022..64e9f04b 100644
--- a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
+++ b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
@@ -23,6 +23,7 @@ import java.io.PrintWriter;
 import java.sql.Connection;
 import java.sql.DriverManager;
 import java.sql.SQLException;
+import java.util.Arrays;
 import java.util.Properties;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -39,16 +40,16 @@ import javax.sql.CommonDataSource;
  *
  * @author Aaron Mulder (ammulder@chariotsolutions.com)
  */
-public abstract class BaseDataSource implements CommonDataSource, Referenceable {
 
+public abstract class BaseDataSource implements CommonDataSource, Referenceable {
   private static final Logger LOGGER = Logger.getLogger(BaseDataSource.class.getName());
 
   // Standard properties, defined in the JDBC 2.0 Optional Package spec
-  private String serverName = "localhost";
+  private String[] serverNames = new String[] {"localhost"};
   private String databaseName = "";
   private String user;
   private String password;
-  private int portNumber = 0;
+  private int[] portNumbers = new int[] {0};
 
   // Map for all other properties
   private Properties properties = new Properties();
@@ -129,9 +130,20 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * Gets the name of the host the PostgreSQL database is running on.
    *
    * @return name of the host the PostgreSQL database is running on
+   * @deprecated use {@link #getServerNames()}
    */
+  @Deprecated
   public String getServerName() {
-    return serverName;
+    return serverNames[0];
+  }
+
+  /**
+   * Gets the name of the host(s) the PostgreSQL database is running on.
+   *
+   * @return name of the host(s) the PostgreSQL database is running on
+   */
+  public String[] getServerNames() {
+    return serverNames;
   }
 
   /**
@@ -139,12 +151,30 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * only affect future calls to getConnection. The default value is <tt>localhost</tt>.
    *
    * @param serverName name of the host the PostgreSQL database is running on
+   * @deprecated use {@link #setServerNames(String[])}
    */
+  @Deprecated
   public void setServerName(String serverName) {
-    if (serverName == null || serverName.equals("")) {
-      this.serverName = "localhost";
+    this.setServerNames(new String[] { serverName });
+  }
+
+  /**
+   * Sets the name of the host(s) the PostgreSQL database is running on. If this is changed, it will
+   * only affect future calls to getConnection. The default value is <tt>localhost</tt>.
+   *
+   * @param serverNames name of the host(s) the PostgreSQL database is running on
+   */
+  public void setServerNames(String[] serverNames) {
+    if (serverNames == null || serverNames.length == 0) {
+      this.serverNames = new String[] {"localhost"};
     } else {
-      this.serverName = serverName;
+      serverNames = Arrays.copyOf(serverNames, serverNames.length);
+      for (int i = 0; i < serverNames.length; i++) {
+        if (serverNames[i] == null || serverNames[i].equals("")) {
+          serverNames[i] = "localhost";
+        }
+      }
+      this.serverNames = serverNames;
     }
   }
 
@@ -221,20 +251,50 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * Gets the port which the PostgreSQL server is listening on for TCP/IP connections.
    *
    * @return The port, or 0 if the default port will be used.
+   * @deprecated use {@link #getPortNumbers()}
    */
+  @Deprecated
   public int getPortNumber() {
-    return portNumber;
+    if (portNumbers == null || portNumbers.length == 0) {
+      return 0;
+    }
+    return portNumbers[0];
+  }
+
+  /**
+   * Gets the port(s) which the PostgreSQL server is listening on for TCP/IP connections.
+   *
+   * @return The port(s), or 0 if the default port will be used.
+   */
+  public int[] getPortNumbers() {
+    return portNumbers;
   }
 
   /**
-   * Gets the port which the PostgreSQL server is listening on for TCP/IP connections. Be sure the
+   * Sets the port which the PostgreSQL server is listening on for TCP/IP connections. Be sure the
    * -i flag is passed to postmaster when PostgreSQL is started. If this is not set, or set to 0,
    * the default port will be used.
    *
    * @param portNumber port which the PostgreSQL server is listening on for TCP/IP
+   * @deprecated use {@link #setPortNumbers(int[])}
    */
+  @Deprecated
   public void setPortNumber(int portNumber) {
-    this.portNumber = portNumber;
+    setPortNumbers(new int[] { portNumber });
+  }
+
+  /**
+   * Sets the port(s) which the PostgreSQL server is listening on for TCP/IP connections. Be sure the
+   * -i flag is passed to postmaster when PostgreSQL is started. If this is not set, or set to 0,
+   * the default port will be used.
+   *
+   * @param portNumbers port(s) which the PostgreSQL server is listening on for TCP/IP
+   */
+  public void setPortNumbers(int[] portNumbers) {
+    if (portNumbers == null || portNumbers.length == 0) {
+      portNumbers = new int[] { 0 };
+    }
+    this.portNumbers = Arrays.copyOf(portNumbers, portNumbers.length);
   }
 
   /**
@@ -1085,9 +1145,14 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
   public String getUrl() {
     StringBuilder url = new StringBuilder(100);
     url.append("jdbc:postgresql://");
-    url.append(serverName);
-    if (portNumber != 0) {
-      url.append(":").append(portNumber);
+    for (int i = 0; i < serverNames.length; i++) {
+      if (i > 0) {
+        url.append(",");
+      }
+      url.append(serverNames[i]);
+      if (portNumbers != null && portNumbers.length >= i && portNumbers[i] != 0) {
+        url.append(":").append(portNumbers[i]);
+      }
     }
     url.append("/").append(URLCoder.encode(databaseName));
 
@@ -1179,23 +1244,28 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
     }
     switch (property) {
       case PG_HOST:
-        serverName = value;
+        setServerNames(value.split(","));
         break;
       case PG_PORT:
-        try {
-          portNumber = Integer.parseInt(value);
-        } catch (NumberFormatException e) {
-          portNumber = 0;
+        String[] ps = value.split(",");
+        int[] ports = new int[ps.length];
+        for (int i = 0 ; i < ps.length; i++) {
+          try {
+            ports[i] = Integer.parseInt(ps[i]);
+          } catch (NumberFormatException e) {
+            ports[i] = 0;
+          }
         }
+        setPortNumbers(ports);
         break;
       case PG_DBNAME:
-        databaseName = value;
+        setDatabaseName(value);
         break;
       case USER:
-        user = value;
+        setUser(value);
         break;
       case PASSWORD:
-        password = value;
+        setPassword(value);
         break;
       default:
         properties.setProperty(property.getName(), value);
@@ -1213,10 +1283,25 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
 
   public Reference getReference() throws NamingException {
     Reference ref = createReference();
-    ref.add(new StringRefAddr("serverName", serverName));
-    if (portNumber != 0) {
-      ref.add(new StringRefAddr("portNumber", Integer.toString(portNumber)));
+    StringBuilder serverString = new StringBuilder();
+    for (int i = 0; i < serverNames.length; i++) {
+      if (i > 0) {
+        serverString.append(",");
+      }
+      String serverName = serverNames[i];
+      serverString.append(serverName);
+    }
+    ref.add(new StringRefAddr("serverName", serverString.toString()));
+
+    StringBuilder portString = new StringBuilder();
+    for (int i = 0; i < portNumbers.length; i++) {
+      if (i > 0) {
+        portString.append(",");
+      }
+      int p = portNumbers[i];
+      portString.append(Integer.toString(p));
     }
+    ref.add(new StringRefAddr("portNumber", portString.toString()));
     ref.add(new StringRefAddr("databaseName", databaseName));
     if (user != null) {
       ref.add(new StringRefAddr("user", user));
@@ -1236,11 +1321,22 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
 
   public void setFromReference(Reference ref) {
     databaseName = getReferenceProperty(ref, "databaseName");
-    String port = getReferenceProperty(ref, "portNumber");
-    if (port != null) {
-      portNumber = Integer.parseInt(port);
+    String portNumberString = getReferenceProperty(ref, "portNumber");
+    if (portNumberString != null) {
+      String[] ps = portNumberString.split(",");
+      int[] ports = new int[ps.length];
+      for (int i = 0; i < ps.length; i++) {
+        try {
+          ports[i] = Integer.parseInt(ps[i]);
+        } catch (NumberFormatException e) {
+          ports[i] = 0;
+        }
+      }
+      setPortNumbers(ports);
+    } else {
+      setPortNumbers(null);
     }
-    serverName = getReferenceProperty(ref, "serverName");
+    setServerNames(getReferenceProperty(ref, "serverName").split(","));
 
     for (PGProperty property : PGProperty.values()) {
       setProperty(property, getReferenceProperty(ref, property.getName()));
@@ -1256,21 +1352,21 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
   }
 
   protected void writeBaseObject(ObjectOutputStream out) throws IOException {
-    out.writeObject(serverName);
+    out.writeObject(serverNames);
     out.writeObject(databaseName);
     out.writeObject(user);
     out.writeObject(password);
-    out.writeInt(portNumber);
+    out.writeObject(portNumbers);
 
     out.writeObject(properties);
   }
 
   protected void readBaseObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
-    serverName = (String) in.readObject();
+    serverNames = (String[]) in.readObject();
     databaseName = (String) in.readObject();
     user = (String) in.readObject();
     password = (String) in.readObject();
-    portNumber = in.readInt();
+    portNumbers = (int[]) in.readObject();
 
     properties = (Properties) in.readObject();
   }
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/BatchResultHandler.java b/pgjdbc/src/main/java/org/postgresql/jdbc/BatchResultHandler.java
index 5cf84ff3..b88807c7 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/BatchResultHandler.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/BatchResultHandler.java
@@ -28,16 +28,17 @@ import java.util.List;
  * Internal class, it is not a part of public API.
  */
 public class BatchResultHandler extends ResultHandlerBase {
-  private PgStatement pgStatement;
+
+  private final PgStatement pgStatement;
   private int resultIndex = 0;
 
   private final Query[] queries;
-  private final int[] updateCounts;
+  private final long[] longUpdateCounts;
   private final ParameterList[] parameterLists;
   private final boolean expectGeneratedKeys;
   private PgResultSet generatedKeys;
   private int committedRows; // 0 means no rows committed. 1 means row 0 was committed, and so on
-  private List<List<byte[][]>> allGeneratedRows;
+  private final List<List<byte[][]>> allGeneratedRows;
   private List<byte[][]> latestGeneratedRows;
   private PgResultSet latestGeneratedKeysRs;
 
@@ -46,11 +47,12 @@ public class BatchResultHandler extends ResultHandlerBase {
     this.pgStatement = pgStatement;
     this.queries = queries;
     this.parameterLists = parameterLists;
-    this.updateCounts = new int[queries.length];
+    this.longUpdateCounts = new long[queries.length];
     this.expectGeneratedKeys = expectGeneratedKeys;
     this.allGeneratedRows = !expectGeneratedKeys ? null : new ArrayList<List<byte[][]>>();
   }
 
+  @Override
   public void handleResultRows(Query fromQuery, Field[] fields, List<byte[][]> tuples,
       ResultCursor cursor) {
     // If SELECT, then handleCommandStatus call would just be missing
@@ -63,9 +65,8 @@ public class BatchResultHandler extends ResultHandlerBase {
       try {
         // If SELECT, the resulting ResultSet is not valid
         // Thus it is up to handleCommandStatus to decide if resultSet is good enough
-        latestGeneratedKeysRs =
-            (PgResultSet) pgStatement.createResultSet(fromQuery, fields,
-                new ArrayList<byte[][]>(), cursor);
+        latestGeneratedKeysRs = (PgResultSet) pgStatement.createResultSet(fromQuery, fields,
+            new ArrayList<byte[][]>(), cursor);
       } catch (SQLException e) {
         handleError(e);
       }
@@ -73,7 +74,8 @@ public class BatchResultHandler extends ResultHandlerBase {
     latestGeneratedRows = tuples;
   }
 
-  public void handleCommandStatus(String status, int updateCount, long insertOID) {
+  @Override
+  public void handleCommandStatus(String status, long updateCount, long insertOID) {
     if (latestGeneratedRows != null) {
       // We have DML. Decrease resultIndex that was just increased in handleResultRows
       resultIndex--;
@@ -95,7 +97,7 @@ public class BatchResultHandler extends ResultHandlerBase {
     }
     latestGeneratedKeysRs = null;
 
-    updateCounts[resultIndex++] = updateCount;
+    longUpdateCounts[resultIndex++] = updateCount;
   }
 
   private boolean isAutoCommit() {
@@ -125,6 +127,7 @@ public class BatchResultHandler extends ResultHandlerBase {
     allGeneratedRows.clear();
   }
 
+  @Override
   public void handleWarning(SQLWarning warning) {
     pgStatement.addWarning(warning);
   }
@@ -132,7 +135,7 @@ public class BatchResultHandler extends ResultHandlerBase {
   @Override
   public void handleError(SQLException newError) {
     if (getException() == null) {
-      Arrays.fill(updateCounts, committedRows, updateCounts.length, Statement.EXECUTE_FAILED);
+      Arrays.fill(longUpdateCounts, committedRows, longUpdateCounts.length, Statement.EXECUTE_FAILED);
       if (allGeneratedRows != null) {
         allGeneratedRows.clear();
       }
@@ -142,11 +145,19 @@ public class BatchResultHandler extends ResultHandlerBase {
         queryString = queries[resultIndex].toString(parameterLists[resultIndex]);
       }
 
-      BatchUpdateException batchException = new BatchUpdateException(
+      BatchUpdateException batchException;
+      //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+      batchException = new BatchUpdateException(
+          GT.tr("Batch entry {0} {1} was aborted: {2}  Call getNextException to see other errors in the batch.",
+              resultIndex, queryString, newError.getMessage()),
+          newError.getSQLState(), 0, uncompressLongUpdateCount(), newError);
+      //#else
+      batchException = new BatchUpdateException(
           GT.tr("Batch entry {0} {1} was aborted: {2}  Call getNextException to see other errors in the batch.",
               resultIndex, queryString, newError.getMessage()),
-          newError.getSQLState(), uncompressUpdateCount());
-      batchException.initCause(newError);
+          newError.getSQLState(), 0, uncompressUpdateCount(), newError);
+      //#endif
+
       super.handleError(batchException);
     }
     resultIndex++;
@@ -154,18 +165,30 @@ public class BatchResultHandler extends ResultHandlerBase {
     super.handleError(newError);
   }
 
+  @Override
   public void handleCompletion() throws SQLException {
     updateGeneratedKeys();
     SQLException batchException = getException();
     if (batchException != null) {
       if (isAutoCommit()) {
         // Re-create batch exception since rows after exception might indeed succeed.
-        BatchUpdateException newException = new BatchUpdateException(
+        BatchUpdateException newException;
+        //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+        newException = new BatchUpdateException(
             batchException.getMessage(),
-            batchException.getSQLState(),
-            uncompressUpdateCount()
+            batchException.getSQLState(), 0,
+            uncompressLongUpdateCount(),
+            batchException.getCause()
         );
-        newException.initCause(batchException.getCause());
+        //#else
+        newException = new BatchUpdateException(
+            batchException.getMessage(),
+            batchException.getSQLState(), 0,
+            uncompressUpdateCount(),
+            batchException.getCause()
+        );
+        //#endif
+
         SQLException next = batchException.getNextException();
         if (next != null) {
           newException.setNextException(next);
@@ -181,8 +204,21 @@ public class BatchResultHandler extends ResultHandlerBase {
   }
 
   private int[] uncompressUpdateCount() {
+    long[] original = uncompressLongUpdateCount();
+    int[] copy = new int[original.length];
+    for (int i = 0; i < original.length; i++) {
+      copy[i] = original[i] > Integer.MAX_VALUE ? Statement.SUCCESS_NO_INFO : (int) original[i];
+    }
+    return copy;
+  }
+
+  public int[] getUpdateCount() {
+    return uncompressUpdateCount();
+  }
+
+  private long[] uncompressLongUpdateCount() {
     if (!(queries[0] instanceof BatchedQuery)) {
-      return updateCounts;
+      return longUpdateCounts;
     }
     int totalRows = 0;
     boolean hasRewrites = false;
@@ -192,7 +228,7 @@ public class BatchResultHandler extends ResultHandlerBase {
       hasRewrites |= batchSize > 1;
     }
     if (!hasRewrites) {
-      return updateCounts;
+      return longUpdateCounts;
     }
 
     /* In this situation there is a batch that has been rewritten. Substitute
@@ -200,12 +236,12 @@ public class BatchResultHandler extends ResultHandlerBase {
      * indicate successful completion for each row the driver client added
      * to the batch.
      */
-    int[] newUpdateCounts = new int[totalRows];
+    long[] newUpdateCounts = new long[totalRows];
     int offset = 0;
     for (int i = 0; i < queries.length; i++) {
       Query query = queries[i];
       int batchSize = query.getBatchSize();
-      int superBatchResult = updateCounts[i];
+      long superBatchResult = longUpdateCounts[i];
       if (batchSize == 1) {
         newUpdateCounts[offset++] = superBatchResult;
         continue;
@@ -221,7 +257,8 @@ public class BatchResultHandler extends ResultHandlerBase {
     return newUpdateCounts;
   }
 
-  public int[] getUpdateCount() {
-    return uncompressUpdateCount();
+  public long[] getLargeUpdateCount() {
+    return uncompressLongUpdateCount();
   }
+
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
index d1271743..d9f4debc 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgConnection.java
@@ -795,6 +795,9 @@ public class PgConnection implements BaseConnection {
 
     if (queryExecutor.getTransactionState() != TransactionState.IDLE) {
       executeTransactionCommand(rollbackQuery);
+    } else {
+      // just log for debugging
+      LOGGER.log(Level.FINE, "Rollback requested but no transaction in progress");
     }
   }
 
@@ -1365,18 +1368,24 @@ public class PgConnection implements BaseConnection {
       return false;
     }
     try {
-      if (replicationConnection) {
-        Statement statement = createStatement();
-        statement.execute("IDENTIFY_SYSTEM");
-        statement.close();
-      } else {
-        if (checkConnectionQuery == null) {
-          checkConnectionQuery = prepareStatement("");
+      int savedNetworkTimeOut = getNetworkTimeout();
+      try {
+        setNetworkTimeout(null, timeout * 1000);
+        if (replicationConnection) {
+          Statement statement = createStatement();
+          statement.execute("IDENTIFY_SYSTEM");
+          statement.close();
+        } else {
+          if (checkConnectionQuery == null) {
+            checkConnectionQuery = prepareStatement("");
+          }
+          checkConnectionQuery.setQueryTimeout(timeout);
+          checkConnectionQuery.executeUpdate();
         }
-        checkConnectionQuery.setQueryTimeout(timeout);
-        checkConnectionQuery.executeUpdate();
+        return true;
+      } finally {
+        setNetworkTimeout(null, savedNetworkTimeOut);
       }
-      return true;
     } catch (SQLException e) {
       if (PSQLState.IN_FAILED_SQL_TRANSACTION.getState().equals(e.getSQLState())) {
         // "current transaction aborted", assume the connection is up and running
@@ -1724,4 +1733,15 @@ public class PgConnection implements BaseConnection {
     }
     return ps;
   }
+
+  @Override
+  public final Map<String,String> getParameterStatuses() {
+    return queryExecutor.getParameterStatuses();
+  }
+
+  @Override
+  public final String getParameterStatus(String parameterName) {
+    return queryExecutor.getParameterStatus(parameterName);
+  }
+
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
index 56d13506..ef3ac750 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
@@ -9,11 +9,13 @@ import org.postgresql.core.BaseStatement;
 import org.postgresql.core.Field;
 import org.postgresql.core.Oid;
 import org.postgresql.core.ServerVersion;
+import org.postgresql.util.ByteConverter;
 import org.postgresql.util.GT;
 import org.postgresql.util.JdbcBlackHole;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+import java.math.BigInteger;
 import java.sql.Array;
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
@@ -24,6 +26,9 @@ import java.sql.Statement;
 import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Arrays;
+
+import java.util.Collections;
+import java.util.Comparator;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.List;
@@ -2247,7 +2252,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
               connection.encodeString(Integer.toString(java.sql.DatabaseMetaData.typeSearchable));
 
     while (rs.next()) {
-      byte[][] tuple = new byte[18][];
+      byte[][] tuple = new byte[19][];
       String typname = rs.getString(1);
       int typeOid = (int) rs.getLong(2);
 
@@ -2255,6 +2260,10 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       int sqlType = connection.getTypeInfo().getSQLType(typname);
       tuple[1] =
           connection.encodeString(Integer.toString(sqlType));
+
+      /* this is just for sorting below, the result set never sees this */
+      tuple[18] = BigInteger.valueOf(sqlType).toByteArray();
+
       tuple[2] = connection
           .encodeString(Integer.toString(connection.getTypeInfo().getMaximumPrecision(typeOid)));
 
@@ -2300,6 +2309,14 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     rs.close();
     stmt.close();
 
+    Collections.sort(v, new Comparator<byte[][]>() {
+      @Override
+      public int compare(byte[][] o1, byte[][] o2) {
+        int i1 = ByteConverter.bytesToInt(o1[18]);
+        int i2 = ByteConverter.bytesToInt(o2[18]);
+        return (i1 < i2) ? -1 : ((i1 == i2) ? 0 : 1);
+      }
+    });
     return ((BaseStatement) createMetaDataStatement()).createDriverResultSet(f, v);
   }
 
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
index 8e66bc77..3c5ee626 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
@@ -122,9 +122,18 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
   @Override
   public int executeUpdate() throws SQLException {
     executeWithFlags(QueryExecutor.QUERY_NO_RESULTS);
+    checkNoResultUpdate();
+    return getUpdateCount();
+  }
 
-    return getNoResultUpdateCount();
+  //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+  @Override
+  public long executeLargeUpdate() throws SQLException {
+    executeWithFlags(QueryExecutor.QUERY_NO_RESULTS);
+    checkNoResultUpdate();
+    return getLargeUpdateCount();
   }
+  //#endif
 
   @Override
   public boolean execute(String sql) throws SQLException {
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
index 403acc97..9cc490a0 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
@@ -59,7 +59,6 @@ import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.OffsetDateTime;
-import java.time.ZoneOffset;
 //#endif
 import java.util.ArrayList;
 import java.util.Calendar;
@@ -601,6 +600,44 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   }
 
   //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+  private OffsetDateTime getOffsetDateTime(int i) throws SQLException {
+    checkResultSet(i);
+    if (wasNullFlag) {
+      return null;
+    }
+
+    int col = i - 1;
+    int oid = fields[col].getOID();
+
+    if (isBinary(i)) {
+      if (oid == Oid.TIMESTAMPTZ || oid == Oid.TIMESTAMP) {
+        return connection.getTimestampUtils().toOffsetDateTimeBin(thisRow[col]);
+      } else if (oid == Oid.TIMETZ) {
+        // JDBC spec says timetz must be supported
+        Time time = getTime(i);
+        return connection.getTimestampUtils().toOffsetDateTime(time);
+      } else {
+        throw new PSQLException(
+            GT.tr("Cannot convert the column of type {0} to requested type {1}.",
+                Oid.toString(oid), "timestamptz"),
+            PSQLState.DATA_TYPE_MISMATCH);
+      }
+    }
+
+    // If this is actually a timestamptz, the server-provided timezone will override
+    // the one we pass in, which is the desired behaviour. Otherwise, we'll
+    // interpret the timezone-less value in the provided timezone.
+    String string = getString(i);
+    if (oid == Oid.TIMETZ) {
+      // JDBC spec says timetz must be supported
+      // If server sends us a TIMETZ, we ensure java counterpart has date of 1970-01-01
+      Calendar cal = getDefaultCalendar();
+      Time time = connection.getTimestampUtils().toTime(cal, string);
+      return connection.getTimestampUtils().toOffsetDateTime(time);
+    }
+    return connection.getTimestampUtils().toOffsetDateTime(string);
+  }
+
   private LocalDateTime getLocalDateTime(int i) throws SQLException {
     checkResultSet(i);
     if (wasNullFlag) {
@@ -616,8 +653,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
               PSQLState.DATA_TYPE_MISMATCH);
     }
     if (isBinary(i)) {
-      TimeZone timeZone = getDefaultCalendar().getTimeZone();
-      return connection.getTimestampUtils().toLocalDateTimeBin(timeZone, thisRow[col]);
+      return connection.getTimestampUtils().toLocalDateTimeBin(thisRow[col]);
     }
 
     String string = getString(i);
@@ -1708,17 +1744,20 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
   public class CursorResultHandler extends ResultHandlerBase {
 
+    @Override
     public void handleResultRows(Query fromQuery, Field[] fields, List<byte[][]> tuples,
         ResultCursor cursor) {
       PgResultSet.this.rows = tuples;
       PgResultSet.this.cursor = cursor;
     }
 
-    public void handleCommandStatus(String status, int updateCount, long insertOID) {
+    @Override
+    public void handleCommandStatus(String status, long updateCount, long insertOID) {
       handleError(new PSQLException(GT.tr("Unexpected command status: {0}.", status),
           PSQLState.PROTOCOL_VIOLATION));
     }
 
+    @Override
     public void handleCompletion() throws SQLException {
       SQLWarning warning = getWarning();
       if (warning != null) {
@@ -3306,14 +3345,15 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     } else if (type == UUID.class) {
       return type.cast(getObject(columnIndex));
     } else if (type == InetAddress.class) {
-      Object addressString = getObject(columnIndex);
-      if (addressString == null) {
+      String inetText = getString(columnIndex);
+      if (inetText == null) {
         return null;
       }
+      int slash = inetText.indexOf("/");
       try {
-        return type.cast(InetAddress.getByName(((PGobject) addressString).getValue()));
-      } catch (UnknownHostException e) {
-        throw new SQLException("could not create inet address from string '" + addressString + "'");
+        return type.cast(InetAddress.getByName(slash < 0 ? inetText : inetText.substring(0, slash)));
+      } catch (UnknownHostException ex) {
+        throw new PSQLException(GT.tr("Invalid Inet data."), PSQLState.INVALID_PARAMETER_VALUE, ex);
       }
       // JSR-310 support
       //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
@@ -3357,19 +3397,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       }
     } else if (type == OffsetDateTime.class) {
       if (sqlType == Types.TIMESTAMP_WITH_TIMEZONE || sqlType == Types.TIMESTAMP) {
-        Timestamp timestampValue = getTimestamp(columnIndex);
-        if (wasNull()) {
-          return null;
-        }
-        long time = timestampValue.getTime();
-        if (time == PGStatement.DATE_POSITIVE_INFINITY) {
-          return type.cast(OffsetDateTime.MAX);
-        }
-        if (time == PGStatement.DATE_NEGATIVE_INFINITY) {
-          return type.cast(OffsetDateTime.MIN);
-        }
-        // Postgres stores everything in UTC and does not keep original time zone
-        OffsetDateTime offsetDateTime = OffsetDateTime.ofInstant(timestampValue.toInstant(), ZoneOffset.UTC);
+        OffsetDateTime offsetDateTime = getOffsetDateTime(columnIndex);
         return type.cast(offsetDateTime);
       } else {
         throw new PSQLException(GT.tr("conversion to {0} from {1} not supported", type, getPGType(columnIndex)),
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgStatement.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgStatement.java
index 5dcf4c4f..3940e7a2 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgStatement.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgStatement.java
@@ -209,7 +209,7 @@ public class PgStatement implements Statement, BaseStatement {
     }
 
     @Override
-    public void handleCommandStatus(String status, int updateCount, long insertOID) {
+    public void handleCommandStatus(String status, long updateCount, long insertOID) {
       append(new ResultWrapper(updateCount, insertOID));
     }
 
@@ -244,10 +244,11 @@ public class PgStatement implements Statement, BaseStatement {
   @Override
   public int executeUpdate(String sql) throws SQLException {
     executeWithFlags(sql, QueryExecutor.QUERY_NO_RESULTS);
-    return getNoResultUpdateCount();
+    checkNoResultUpdate();
+    return getUpdateCount();
   }
 
-  protected int getNoResultUpdateCount() throws SQLException {
+  protected final void checkNoResultUpdate() throws SQLException {
     synchronized (this) {
       checkClosed();
       ResultWrapper iter = result;
@@ -255,12 +256,9 @@ public class PgStatement implements Statement, BaseStatement {
         if (iter.getResultSet() != null) {
           throw new PSQLException(GT.tr("A result was returned when none was expected."),
               PSQLState.TOO_MANY_RESULTS);
-
         }
         iter = iter.getNext();
       }
-
-      return getUpdateCount();
     }
   }
 
@@ -470,6 +468,7 @@ public class PgStatement implements Statement, BaseStatement {
 
   private volatile boolean isClosed = false;
 
+  @Override
   public int getUpdateCount() throws SQLException {
     synchronized (this) {
       checkClosed();
@@ -477,7 +476,8 @@ public class PgStatement implements Statement, BaseStatement {
         return -1;
       }
 
-      return result.getUpdateCount();
+      long count = result.getUpdateCount();
+      return count > Integer.MAX_VALUE ? Statement.SUCCESS_NO_INFO : (int) count;
     }
   }
 
@@ -739,26 +739,17 @@ public class PgStatement implements Statement, BaseStatement {
         wantsGeneratedKeysAlways);
   }
 
-  public int[] executeBatch() throws SQLException {
-    checkClosed();
-
-    closeForNextExecution();
-
-    if (batchStatements == null || batchStatements.isEmpty()) {
-      return new int[0];
-    }
-
+  private BatchResultHandler internalExecuteBatch() throws SQLException {
     // Construct query/parameter arrays.
     transformQueriesAndParameters();
     // Empty arrays should be passed to toArray
     // see http://shipilev.net/blog/2016/arrays-wisdom-ancients/
     Query[] queries = batchStatements.toArray(new Query[0]);
-    ParameterList[] parameterLists =
-        batchParameters.toArray(new ParameterList[0]);
+    ParameterList[] parameterLists = batchParameters.toArray(new ParameterList[0]);
     batchStatements.clear();
     batchParameters.clear();
 
-    int flags = 0;
+    int flags;
 
     // Force a Describe before any execution? We need to do this if we're going
     // to send anything dependent on the Describe results, e.g. binary parameters.
@@ -862,8 +853,18 @@ public class PgStatement implements Statement, BaseStatement {
         }
       }
     }
+    return handler;
+  }
+
+  public int[] executeBatch() throws SQLException {
+    checkClosed();
+    closeForNextExecution();
+
+    if (batchStatements == null || batchStatements.isEmpty()) {
+      return new int[0];
+    }
 
-    return handler.getUpdateCount();
+    return internalExecuteBatch().getUpdateCount();
   }
 
   public void cancel() throws SQLException {
@@ -1012,8 +1013,17 @@ public class PgStatement implements Statement, BaseStatement {
     return forceBinaryTransfers;
   }
 
+  //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+  @Override
   public long getLargeUpdateCount() throws SQLException {
-    throw Driver.notImplemented(this.getClass(), "getLargeUpdateCount");
+    synchronized (this) {
+      checkClosed();
+      if (result == null || result.getResultSet() != null) {
+        return -1;
+      }
+
+      return result.getUpdateCount();
+    }
   }
 
   public void setLargeMaxRows(long max) throws SQLException {
@@ -1024,29 +1034,57 @@ public class PgStatement implements Statement, BaseStatement {
     throw Driver.notImplemented(this.getClass(), "getLargeMaxRows");
   }
 
+  @Override
   public long[] executeLargeBatch() throws SQLException {
-    throw Driver.notImplemented(this.getClass(), "executeLargeBatch");
+    checkClosed();
+    closeForNextExecution();
+
+    if (batchStatements == null || batchStatements.isEmpty()) {
+      return new long[0];
+    }
+
+    return internalExecuteBatch().getLargeUpdateCount();
   }
 
+  @Override
   public long executeLargeUpdate(String sql) throws SQLException {
-    throw Driver.notImplemented(this.getClass(), "executeLargeUpdate");
+    executeWithFlags(sql, QueryExecutor.QUERY_NO_RESULTS);
+    checkNoResultUpdate();
+    return getLargeUpdateCount();
   }
 
+  @Override
   public long executeLargeUpdate(String sql, int autoGeneratedKeys) throws SQLException {
-    throw Driver.notImplemented(this.getClass(), "executeLargeUpdate");
+    if (autoGeneratedKeys == Statement.NO_GENERATED_KEYS) {
+      return executeLargeUpdate(sql);
+    }
+
+    return executeLargeUpdate(sql, (String[]) null);
   }
 
+  @Override
   public long executeLargeUpdate(String sql, int[] columnIndexes) throws SQLException {
-    throw Driver.notImplemented(this.getClass(), "executeLargeUpdate");
+    if (columnIndexes == null || columnIndexes.length == 0) {
+      return executeLargeUpdate(sql);
+    }
+
+    throw new PSQLException(GT.tr("Returning autogenerated keys by column index is not supported."),
+        PSQLState.NOT_IMPLEMENTED);
   }
 
+  @Override
   public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
-    throw Driver.notImplemented(this.getClass(), "executeLargeUpdate");
-  }
+    if (columnNames != null && columnNames.length == 0) {
+      return executeLargeUpdate(sql);
+    }
 
-  public long executeLargeUpdate() throws SQLException {
-    throw Driver.notImplemented(this.getClass(), "executeLargeUpdate");
+    wantsGeneratedKeysOnce = true;
+    if (!executeCachedSql(sql, 0, columnNames)) {
+      // no resultset returned. What's a pity!
+    }
+    return getLargeUpdateCount();
   }
+  //#endif
 
   public boolean isClosed() throws SQLException {
     return isClosed;
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/ResultWrapper.java b/pgjdbc/src/main/java/org/postgresql/jdbc/ResultWrapper.java
index 3fb00345..df79ae78 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/ResultWrapper.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/ResultWrapper.java
@@ -21,7 +21,7 @@ public class ResultWrapper {
     this.insertOID = -1;
   }
 
-  public ResultWrapper(int updateCount, long insertOID) {
+  public ResultWrapper(long updateCount, long insertOID) {
     this.rs = null;
     this.updateCount = updateCount;
     this.insertOID = insertOID;
@@ -31,7 +31,7 @@ public class ResultWrapper {
     return rs;
   }
 
-  public int getUpdateCount() {
+  public long getUpdateCount() {
     return updateCount;
   }
 
@@ -53,7 +53,7 @@ public class ResultWrapper {
   }
 
   private final ResultSet rs;
-  private final int updateCount;
+  private final long updateCount;
   private final long insertOID;
   private ResultWrapper next;
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/TimestampUtils.java b/pgjdbc/src/main/java/org/postgresql/jdbc/TimestampUtils.java
index 4dd76e00..59341329 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/TimestampUtils.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/TimestampUtils.java
@@ -21,6 +21,7 @@ import java.sql.Time;
 import java.sql.Timestamp;
 //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
 import java.time.Duration;
+import java.time.Instant;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
@@ -56,6 +57,10 @@ public class TimestampUtils {
   private static final LocalTime MAX_TIME = LocalTime.MAX.minus(Duration.ofMillis(500));
   private static final OffsetDateTime MAX_OFFSET_DATETIME = OffsetDateTime.MAX.minus(Duration.ofMillis(500));
   private static final LocalDateTime MAX_LOCAL_DATETIME = LocalDateTime.MAX.minus(Duration.ofMillis(500));
+  // low value for dates is   4713 BC
+  private static final LocalDate MIN_LOCAL_DATE = LocalDate.of(4713, 1, 1).with(ChronoField.ERA, IsoEra.BCE.getValue());
+  private static final LocalDateTime MIN_LOCAL_DATETIME = MIN_LOCAL_DATE.atStartOfDay();
+  private static final OffsetDateTime MIN_OFFSET_DATETIME = MIN_LOCAL_DATETIME.atOffset(ZoneOffset.UTC);
   //#endif
 
   private static final Field DEFAULT_TIME_ZONE_FIELD;
@@ -469,6 +474,83 @@ public class TimestampUtils {
       return result;
     }
   }
+
+  /**
+   * Parse a string and return a LocalDateTime representing its value.
+   *
+   * @param s The ISO formated date string to parse.
+   * @return null if s is null or a LocalDateTime of the parsed string s.
+   * @throws SQLException if there is a problem parsing s.
+   */
+  public OffsetDateTime toOffsetDateTime(String s) throws SQLException {
+    if (s == null) {
+      return null;
+    }
+
+    int slen = s.length();
+
+    // convert postgres's infinity values to internal infinity magic value
+    if (slen == 8 && s.equals("infinity")) {
+      return OffsetDateTime.MAX;
+    }
+
+    if (slen == 9 && s.equals("-infinity")) {
+      return OffsetDateTime.MIN;
+    }
+
+    ParsedTimestamp ts = parseBackendTimestamp(s);
+
+    Calendar tz = ts.tz;
+    int offsetSeconds;
+    if (tz == null) {
+      offsetSeconds = 0;
+    } else {
+      offsetSeconds = tz.get(Calendar.ZONE_OFFSET) / 1000;
+    }
+    ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(offsetSeconds);
+    // Postgres is always UTC
+    OffsetDateTime result = OffsetDateTime.of(ts.year, ts.month, ts.day, ts.hour, ts.minute, ts.second, ts.nanos, zoneOffset)
+        .withOffsetSameInstant(ZoneOffset.UTC);
+    if (ts.era == GregorianCalendar.BC) {
+      return result.with(ChronoField.ERA, IsoEra.BCE.getValue());
+    } else {
+      return result;
+    }
+  }
+
+  /**
+   * Returns the offset date time object matching the given bytes with Oid#TIMETZ.
+   *
+   * @param t the time value
+   * @return the matching offset date time
+   */
+  public OffsetDateTime toOffsetDateTime(Time t) {
+    // hardcode utc because the backend does not provide us the timezone
+    // hardoce UNIX epoch, JDBC requires OffsetDateTime but doesn't describe what date should be used
+    return t.toLocalTime().atDate(LocalDate.of(1970, 1, 1)).atOffset(ZoneOffset.UTC);
+  }
+
+  /**
+   * Returns the offset date time object matching the given bytes with Oid#TIMESTAMPTZ.
+   *
+   * @param bytes The binary encoded local date time value.
+   * @return The parsed local date time object.
+   * @throws PSQLException If binary format could not be parsed.
+   */
+  public OffsetDateTime toOffsetDateTimeBin(byte[] bytes) throws PSQLException {
+    ParsedBinaryTimestamp parsedTimestamp = this.toProlepticParsedTimestampBin(bytes);
+    if (parsedTimestamp.infinity == Infinity.POSITIVE) {
+      return OffsetDateTime.MAX;
+    } else if (parsedTimestamp.infinity == Infinity.NEGATIVE) {
+      return OffsetDateTime.MIN;
+    }
+
+    // hardcode utc because the backend does not provide us the timezone
+    // Postgres is always UTC
+    Instant instant = Instant.ofEpochSecond(parsedTimestamp.millis / 1000L, parsedTimestamp.nanos);
+    return OffsetDateTime.ofInstant(instant, ZoneOffset.UTC);
+  }
+
   //#endif
 
   public synchronized Time toTime(Calendar cal, String s) throws SQLException {
@@ -758,7 +840,7 @@ public class TimestampUtils {
   public synchronized String toString(LocalDate localDate) {
     if (LocalDate.MAX.equals(localDate)) {
       return "infinity";
-    } else if (LocalDate.MIN.equals(localDate)) {
+    } else if (localDate.isBefore(MIN_LOCAL_DATE)) {
       return "-infinity";
     }
 
@@ -792,7 +874,7 @@ public class TimestampUtils {
   public synchronized String toString(OffsetDateTime offsetDateTime) {
     if (offsetDateTime.isAfter(MAX_OFFSET_DATETIME)) {
       return "infinity";
-    } else if (OffsetDateTime.MIN.equals(offsetDateTime)) {
+    } else if (offsetDateTime.isBefore(MIN_OFFSET_DATETIME)) {
       return "-infinity";
     }
 
@@ -824,7 +906,7 @@ public class TimestampUtils {
   public synchronized String toString(LocalDateTime localDateTime) {
     if (localDateTime.isAfter(MAX_LOCAL_DATETIME)) {
       return "infinity";
-    } else if (LocalDateTime.MIN.equals(localDateTime)) {
+    } else if (localDateTime.isBefore(MIN_LOCAL_DATETIME)) {
       return "-infinity";
     }
 
@@ -1056,8 +1138,8 @@ public class TimestampUtils {
     return ts;
   }
 
-  private ParsedBinaryTimestamp toParsedTimestampBin(TimeZone tz, byte[] bytes, boolean timestamptz)
-          throws PSQLException {
+  private ParsedBinaryTimestamp toParsedTimestampBinPlain(byte[] bytes)
+      throws PSQLException {
 
     if (bytes.length != 8) {
       throw new PSQLException(GT.tr("Unsupported binary encoding of {0}.", "timestamp"),
@@ -1106,6 +1188,24 @@ public class TimestampUtils {
     }
     nanos *= 1000;
 
+    long millis = secs * 1000L;
+
+    ParsedBinaryTimestamp ts = new ParsedBinaryTimestamp();
+    ts.millis = millis;
+    ts.nanos = nanos;
+    return ts;
+  }
+
+  private ParsedBinaryTimestamp toParsedTimestampBin(TimeZone tz, byte[] bytes, boolean timestamptz)
+      throws PSQLException {
+
+    ParsedBinaryTimestamp ts = toParsedTimestampBinPlain(bytes);
+    if (ts.infinity != null) {
+      return ts;
+    }
+
+    long secs = ts.millis / 1000L;
+
     secs = toJavaSecs(secs);
     long millis = secs * 1000L;
     if (!timestamptz) {
@@ -1114,9 +1214,25 @@ public class TimestampUtils {
       millis = guessTimestamp(millis, tz);
     }
 
-    ParsedBinaryTimestamp ts = new ParsedBinaryTimestamp();
     ts.millis = millis;
-    ts.nanos = nanos;
+    return ts;
+  }
+
+  private ParsedBinaryTimestamp toProlepticParsedTimestampBin(byte[] bytes)
+      throws PSQLException {
+
+    ParsedBinaryTimestamp ts = toParsedTimestampBinPlain(bytes);
+    if (ts.infinity != null) {
+      return ts;
+    }
+
+    long secs = ts.millis / 1000L;
+
+    // postgres epoc to java epoc
+    secs += 946684800L;
+    long millis = secs * 1000L;
+
+    ts.millis = millis;
     return ts;
   }
 
@@ -1124,21 +1240,22 @@ public class TimestampUtils {
   /**
    * Returns the local date time object matching the given bytes with {@link Oid#TIMESTAMP} or
    * {@link Oid#TIMESTAMPTZ}.
-   *
-   * @param tz time zone to use
    * @param bytes The binary encoded local date time value.
+   *
    * @return The parsed local date time object.
    * @throws PSQLException If binary format could not be parsed.
    */
-  public LocalDateTime toLocalDateTimeBin(TimeZone tz, byte[] bytes) throws PSQLException {
+  public LocalDateTime toLocalDateTimeBin(byte[] bytes) throws PSQLException {
 
-    ParsedBinaryTimestamp parsedTimestamp = this.toParsedTimestampBin(tz, bytes, true);
+    ParsedBinaryTimestamp parsedTimestamp = this.toProlepticParsedTimestampBin(bytes);
     if (parsedTimestamp.infinity == Infinity.POSITIVE) {
       return LocalDateTime.MAX;
     } else if (parsedTimestamp.infinity == Infinity.NEGATIVE) {
       return LocalDateTime.MIN;
     }
 
+    // hardcode utc because the backend does not provide us the timezone
+    // Postgres is always UTC
     return LocalDateTime.ofEpochSecond(parsedTimestamp.millis / 1000L, parsedTimestamp.nanos, ZoneOffset.UTC);
   }
   //#endif
@@ -1426,4 +1543,5 @@ public class TimestampUtils {
   private static long floorMod(long x, long y) {
     return x - floorDiv(x, y) * y;
   }
+
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/jre8/sasl/ScramAuthenticator.java b/pgjdbc/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
similarity index 85%
rename from pgjdbc/src/main/java/org/postgresql/jre8/sasl/ScramAuthenticator.java
rename to pgjdbc/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
index 3abe193d..2d973872 100644
--- a/pgjdbc/src/main/java/org/postgresql/jre8/sasl/ScramAuthenticator.java
+++ b/pgjdbc/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
@@ -3,7 +3,7 @@
  * See the LICENSE file in the project root for more information.
  */
 
-package org.postgresql.jre8.sasl;
+package org.postgresql.jre7.sasl;
 
 import org.postgresql.core.PGStream;
 import org.postgresql.util.GT;
@@ -36,7 +36,6 @@ public class ScramAuthenticator {
   private ScramSession.ServerFirstProcessor serverFirstProcessor;
   private ScramSession.ClientFinalProcessor clientFinalProcessor;
 
-  @FunctionalInterface
   private interface BodySender {
     void sendBody(PGStream pgStream) throws IOException;
   }
@@ -44,7 +43,7 @@ public class ScramAuthenticator {
   private void sendAuthenticationMessage(int bodyLength, BodySender bodySender)
       throws IOException {
     pgStream.sendChar('p');
-    pgStream.sendInteger4(Integer.BYTES + bodyLength);
+    pgStream.sendInteger4(Integer.SIZE / Byte.SIZE + bodyLength);
     bodySender.sendBody(pgStream);
     pgStream.flush();
   }
@@ -94,15 +93,18 @@ public class ScramAuthenticator {
     LOGGER.log(Level.FINEST, " FE=> SASLInitialResponse( {0} )", clientFirstMessage);
 
     String scramMechanismName = scramClient.getScramMechanism().getName();
-    byte[] scramMechanismNameBytes = scramMechanismName.getBytes(StandardCharsets.UTF_8);
-    byte[] clientFirstMessageBytes = clientFirstMessage.getBytes(StandardCharsets.UTF_8);
+    final byte[] scramMechanismNameBytes = scramMechanismName.getBytes(StandardCharsets.UTF_8);
+    final byte[] clientFirstMessageBytes = clientFirstMessage.getBytes(StandardCharsets.UTF_8);
     sendAuthenticationMessage(
         (scramMechanismNameBytes.length + 1) + 4 + clientFirstMessageBytes.length,
-        s -> {
-          s.send(scramMechanismNameBytes);
-          s.sendChar(0); // List terminated in '\0'
-          s.sendInteger4(clientFirstMessageBytes.length);
-          s.send(clientFirstMessageBytes);
+        new BodySender() {
+          @Override
+          public void sendBody(PGStream pgStream) throws IOException {
+            pgStream.send(scramMechanismNameBytes);
+            pgStream.sendChar(0); // List terminated in '\0'
+            pgStream.sendInteger4(clientFirstMessageBytes.length);
+            pgStream.send(clientFirstMessageBytes);
+          }
         }
     );
   }
@@ -132,10 +134,15 @@ public class ScramAuthenticator {
     String clientFinalMessage = clientFinalProcessor.clientFinalMessage();
     LOGGER.log(Level.FINEST, " FE=> SASLResponse( {0} )", clientFinalMessage);
 
-    byte[] clientFinalMessageBytes = clientFinalMessage.getBytes(StandardCharsets.UTF_8);
+    final byte[] clientFinalMessageBytes = clientFinalMessage.getBytes(StandardCharsets.UTF_8);
     sendAuthenticationMessage(
         clientFinalMessageBytes.length,
-        s -> s.send(clientFinalMessageBytes)
+        new BodySender() {
+          @Override
+          public void sendBody(PGStream pgStream) throws IOException {
+            pgStream.send(clientFinalMessageBytes);
+          }
+        }
     );
   }
 
diff --git a/pgjdbc/src/main/java/org/postgresql/util/ByteConverter.java b/pgjdbc/src/main/java/org/postgresql/util/ByteConverter.java
index 5464236c..f1188977 100644
--- a/pgjdbc/src/main/java/org/postgresql/util/ByteConverter.java
+++ b/pgjdbc/src/main/java/org/postgresql/util/ByteConverter.java
@@ -16,6 +16,25 @@ public class ByteConverter {
     // prevent instantiation of static helper class
   }
 
+  /**
+   * Convert a variable length array of bytes to an integer
+   * @param bytes array of bytes that can be decoded as an integer
+   * @return integer
+   */
+  public static int bytesToInt(byte []bytes) {
+    if ( bytes.length == 1 ) {
+      return (int)bytes[0];
+    }
+    if ( bytes.length == 2 ) {
+      return int2(bytes, 0);
+    }
+    if ( bytes.length == 4 ) {
+      return int4(bytes, 0);
+    } else {
+      throw new IllegalArgumentException("Argument bytes is empty");
+    }
+  }
+
   /**
    * Parses a long value from the byte array.
    *
diff --git a/pgjdbc/src/main/java/org/postgresql/util/PSQLState.java b/pgjdbc/src/main/java/org/postgresql/util/PSQLState.java
index 12892ad9..ca4de8ba 100644
--- a/pgjdbc/src/main/java/org/postgresql/util/PSQLState.java
+++ b/pgjdbc/src/main/java/org/postgresql/util/PSQLState.java
@@ -83,6 +83,7 @@ public enum PSQLState {
   INVALID_NAME("42602"),
   DATATYPE_MISMATCH("42804"),
   CANNOT_COERCE("42846"),
+  UNDEFINED_TABLE("42P01"),
 
   OUT_OF_MEMORY("53200"),
   OBJECT_NOT_IN_STATE("55000"),
diff --git a/pom.xml b/pom.xml
index dd9550f6..f50ae2c4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
   <artifactId>pgjdbc-aggregate</artifactId>
   <packaging>pom</packaging>
   <name>PostgreSQL JDBC Driver aggregate</name>
-  <version>42.2.7-SNAPSHOT</version>
+  <version>42.2.9-SNAPSHOT</version>
   <description>PgJDBC aggregate project</description>
   <url>https://github.com/pgjdbc/pgjdbc</url>
 


Test patch:
diff --git a/pgjdbc/src/test/java/org/postgresql/replication/ReplicationTestSuite.java b/pgjdbc/src/test/java/org/postgresql/replication/ReplicationTestSuite.java
index 27bbb0a1..ab213ce2 100644
--- a/pgjdbc/src/test/java/org/postgresql/replication/ReplicationTestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/replication/ReplicationTestSuite.java
@@ -22,12 +22,13 @@ import java.sql.Statement;
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
     CopyBothResponseTest.class,
+    LogicalReplicationStatusTest.class,
     LogicalReplicationTest.class,
     LogSequenceNumberTest.class,
     PhysicalReplicationTest.class,
-    LogicalReplicationStatusTest.class,
     ReplicationConnectionTest.class,
-    ReplicationSlotTest.class})
+    ReplicationSlotTest.class,
+})
 public class ReplicationTestSuite {
 
   @BeforeClass
diff --git a/pgjdbc/src/test/java/org/postgresql/test/TestUtil.java b/pgjdbc/src/test/java/org/postgresql/test/TestUtil.java
index d396b314..b66c0fae 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/TestUtil.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/TestUtil.java
@@ -6,7 +6,9 @@
 package org.postgresql.test;
 
 import org.postgresql.PGProperty;
+import org.postgresql.core.BaseConnection;
 import org.postgresql.core.ServerVersion;
+import org.postgresql.core.TransactionState;
 import org.postgresql.core.Version;
 import org.postgresql.jdbc.PgConnection;
 
@@ -437,6 +439,26 @@ public class TestUtil {
     }
   }
 
+  /*
+   * Helper - creates a unlogged table for use by a test.
+   * Unlogged tables works from PostgreSQL 9.1+
+   */
+  public static void createUnloggedTable(Connection con, String table, String columns)
+      throws SQLException {
+    Statement st = con.createStatement();
+    try {
+      // Drop the table
+      dropTable(con, table);
+
+      String unlogged = haveMinimumServerVersion(con, ServerVersion.v9_1) ? "UNLOGGED" : "";
+
+      // Now create the table
+      st.executeUpdate("CREATE " + unlogged + " TABLE " + table + " (" + columns + ")");
+    } finally {
+      closeQuietly(st);
+    }
+  }
+
   /**
    * Helper creates an enum type.
    *
@@ -601,6 +623,11 @@ public class TestUtil {
     }
   }
 
+  public static void assertTransactionState(String message, Connection con, TransactionState expected) {
+    TransactionState actual = TestUtil.getTransactionState(con);
+    Assert.assertEquals(message, expected, actual);
+  }
+
   /*
    * Helper - generates INSERT SQL - very simple
    */
@@ -897,6 +924,73 @@ public class TestUtil {
     }
   }
 
+  /**
+   * Execute a SQL query with a given connection and return whether any rows were
+   * returned. No column data is fetched.
+   */
+  public static boolean executeQuery(Connection conn, String sql) throws SQLException {
+    Statement stmt = conn.createStatement();
+    ResultSet rs = stmt.executeQuery(sql);
+    boolean hasNext = rs.next();
+    rs.close();
+    stmt.close();
+    return hasNext;
+  }
+
+  /**
+   * Retrieve the backend process id for a given connection.
+   */
+  public static int getBackendPid(Connection conn) throws SQLException {
+    Statement stmt = conn.createStatement();
+    ResultSet rs = stmt.executeQuery("SELECT pg_backend_pid()");
+    rs.next();
+    int pid = rs.getInt(1);
+    rs.close();
+    stmt.close();
+    return pid;
+  }
+
+  /**
+   * Executed pg_terminate_backend(...) to terminate the server process for
+   * a given process id with the given connection.
+   */
+  public static boolean terminateBackend(Connection conn, int backendPid) throws SQLException {
+    PreparedStatement stmt = conn.prepareStatement("SELECT pg_terminate_backend(?)");
+    stmt.setInt(1, backendPid);
+    ResultSet rs = stmt.executeQuery();
+    rs.next();
+    boolean wasTerminated = rs.getBoolean(1);
+    rs.close();
+    stmt.close();
+    return wasTerminated;
+  }
+
+  /**
+   * Create a new connection using the default test credentials and use it to
+   * attempt to terminate the specified backend process.
+   */
+  public static boolean terminateBackend(int backendPid) throws SQLException {
+    Connection conn = TestUtil.openPrivilegedDB();
+    try {
+      return terminateBackend(conn, backendPid);
+    } finally {
+      conn.close();
+    }
+  }
+
+  /**
+   * Retrieve the given connection backend process id, then create a new connection
+   * using the default test credentials and attempt to terminate the process.
+   */
+  public static boolean terminateBackend(Connection conn) throws SQLException {
+    int pid = getBackendPid(conn);
+    return terminateBackend(pid);
+  }
+
+  public static TransactionState getTransactionState(Connection conn) {
+    return ((BaseConnection) conn).getTransactionState();
+  }
+
   private static void waitStopReplicationSlot(Connection connection, String slotName)
       throws InterruptedException, TimeoutException, SQLException {
     long startWaitTime = System.currentTimeMillis();
@@ -915,4 +1009,16 @@ public class TestUtil {
       throw new TimeoutException("Wait stop replication slot " + timeInWait + " timeout occurs");
     }
   }
+
+  public static void execute(String sql, Connection connection) throws SQLException {
+    Statement stmt = connection.createStatement();
+    try {
+      stmt.execute(sql);
+    } finally {
+      try {
+        stmt.close();
+      } catch (SQLException e) {
+      }
+    }
+  }
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/extensions/ExtensionsTestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/extensions/ExtensionsTestSuite.java
index f0c5aed7..0da675e3 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/extensions/ExtensionsTestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/extensions/ExtensionsTestSuite.java
@@ -12,7 +12,9 @@ import org.junit.runners.Suite;
  * Executes all known tests for PostgreSQL extensions supported by JDBC driver
  */
 @RunWith(Suite.class)
-@Suite.SuiteClasses(HStoreTest.class)
+@Suite.SuiteClasses({
+    HStoreTest.class,
+})
 public class ExtensionsTestSuite {
 }
 
diff --git a/pgjdbc/src/test/java/org/postgresql/test/hostchooser/MultiHostTestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/hostchooser/MultiHostTestSuite.java
index 984577e5..2f19cd40 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/hostchooser/MultiHostTestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/hostchooser/MultiHostTestSuite.java
@@ -12,6 +12,8 @@ import org.junit.runners.Suite;
  * Executes multi host tests (aka master/slave connectivity selection).
  */
 @RunWith(Suite.class)
-@Suite.SuiteClasses(MultiHostsConnectionTest.class)
+@Suite.SuiteClasses({
+    MultiHostsConnectionTest.class,
+})
 public class MultiHostTestSuite {
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ConnectTimeoutTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ConnectTimeoutTest.java
index eafaf333..5b967391 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ConnectTimeoutTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ConnectTimeoutTest.java
@@ -10,9 +10,11 @@ import static org.junit.Assert.fail;
 
 import org.postgresql.test.TestUtil;
 
+import org.junit.Assume;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.net.NoRouteToHostException;
 import java.net.SocketTimeoutException;
 import java.sql.DriverManager;
 import java.sql.SQLException;
@@ -41,11 +43,27 @@ public class ConnectTimeoutTest {
     try {
       DriverManager.getConnection(UNREACHABLE_URL, props);
     } catch (SQLException e) {
-      assertTrue("Unexpected " + e.toString(),
-          e.getCause() instanceof SocketTimeoutException);
       final long interval = System.currentTimeMillis() - startTime;
       final long connectTimeoutMillis = CONNECT_TIMEOUT * 1000;
       final long maxDeviation = connectTimeoutMillis / 10;
+
+      /*
+       * If the platform fast-fails the unroutable address connection then this
+       * test may not time out, instead throwing
+       * java.net.NoRouteToHostException. The test has failed in that the connection
+       * attempt did not time out.
+       *
+       * We treat this as a skipped test, as the test didn't really "succeed"
+       * in testing the original behaviour, but it didn't fail either.
+       */
+      Assume.assumeTrue("Host fast-failed connection to unreachable address "
+                        + UNREACHABLE_HOST + " after " + interval + " ms, "
+                        + " before timeout should have triggered.",
+                        e.getCause() instanceof NoRouteToHostException
+                        && interval < connectTimeoutMillis );
+
+      assertTrue("Unexpected " + e.toString() + " with cause " + e.getCause(),
+          e.getCause() instanceof SocketTimeoutException);
       // check that it was not a default system timeout, an approximate value is used
       assertTrue(Math.abs(interval - connectTimeoutMillis) < maxDeviation);
       return;
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/CopyTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/CopyTest.java
index 01f9e809..798c6d7a 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/CopyTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/CopyTest.java
@@ -31,7 +31,6 @@ import java.io.OutputStream;
 import java.io.PrintStream;
 import java.io.StringReader;
 import java.sql.Connection;
-import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -353,17 +352,14 @@ public class CopyTest {
     // on the Connection object fails to deadlock.
     con.setAutoCommit(false);
 
-    Statement stmt = con.createStatement();
-    ResultSet rs = stmt.executeQuery("select pg_backend_pid()");
-    rs.next();
-    int pid = rs.getInt(1);
-    rs.close();
-    stmt.close();
+    // We get the process id before the COPY as we cannot run other commands
+    // on the connection during the COPY operation.
+    int pid = TestUtil.getBackendPid(con);
 
     CopyManager manager = con.unwrap(PGConnection.class).getCopyAPI();
     CopyIn copyIn = manager.copyIn("COPY copytest FROM STDIN with " + copyParams);
     try {
-      killConnection(pid);
+      TestUtil.terminateBackend(pid);
       byte[] bunchOfNulls = ",,\n".getBytes();
       while (true) {
         copyIn.writeToCopy(bunchOfNulls, 0, bunchOfNulls.length);
@@ -420,23 +416,6 @@ public class CopyTest {
     }
   }
 
-  private void killConnection(int pid) throws SQLException {
-    Connection killerCon;
-    try {
-      killerCon = TestUtil.openPrivilegedDB();
-    } catch (Exception e) {
-      fail("Unable to open secondary connection to terminate copy");
-      return; // persuade Java killerCon will not be used uninitialized
-    }
-    try {
-      PreparedStatement stmt = killerCon.prepareStatement("select pg_terminate_backend(?)");
-      stmt.setInt(1, pid);
-      stmt.execute();
-    } finally {
-      killerCon.close();
-    }
-  }
-
   private void acceptIOCause(SQLException e) throws SQLException {
     if (!(e.getCause() instanceof IOException)) {
       throw e;
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java
index 3f372393..01d9fbf9 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/Jdbc2TestSuite.java
@@ -32,97 +32,80 @@ import org.junit.runners.Suite;
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-        ANTTest.class,
-        JavaVersionTest.class,
-
-        DriverTest.class,
-        ConnectionTest.class,
-        DateStyleTest.class,
-        DatabaseMetaDataTest.class,
-        DatabaseMetaDataPropertiesTest.class,
-        SearchPathLookupTest.class,
-        EncodingTest.class,
-        ExpressionPropertiesTest.class,
-        ColumnSanitiserDisabledTest.class,
-        ColumnSanitiserEnabledTest.class,
-        LruCacheTest.class,
-        HostSpecTest.class,
-        ReaderInputStreamTest.class,
-        ServerVersionParseTest.class,
-        ServerVersionTest.class,
-
-        OptionsPropertyTest.class,
-
-        TypeCacheDLLStressTest.class,
-
-        ResultSetTest.class,
-        ResultSetMetaDataTest.class,
-        StringTypeUnspecifiedArrayTest.class,
-        ArrayTest.class,
-        PrimitiveArraySupportTest.class,
-        RefCursorTest.class,
-
-        DateTest.class,
-        TimeTest.class,
-        TimestampTest.class,
-        TimezoneTest.class,
-        PGTimeTest.class,
-        PGTimestampTest.class,
-        TimezoneCachingTest.class,
-        ParserTest.class,
-        ReturningParserTest.class,
-        CommandCompleteParserTest.class,
-        CommandCompleteParserNegativeTest.class,
-        ReplaceProcessingTest.class,
-
-        OidToStringTest.class,
-        OidValueOfTest.class,
-
-        PreparedStatementTest.class,
-        StatementTest.class,
-        QuotationTest.class,
-
-        ServerPreparedStmtTest.class,
-
-        BatchExecuteTest.class,
-        BatchFailureTest.class,
-
-        BatchedInsertReWriteEnabledTest.class,
-        NativeQueryBindLengthTest.class,
-        DeepBatchedInsertStatementTest.class,
-        JBuilderTest.class,
-        MiscTest.class,
-        NotifyTest.class,
-        DatabaseEncodingTest.class,
-        ClientEncodingTest.class,
-
-        BlobTest.class,
-        BlobTransactionTest.class,
-
-        UpdateableResultTest.class,
-
-        CallableStmtTest.class,
-        CursorFetchTest.class,
-        ConcurrentStatementFetch.class,
-        ServerCursorTest.class,
-
-        IntervalTest.class,
-        GeometricTest.class,
-
-        LoginTimeoutTest.class,
-        TestACL.class,
-
-        ConnectTimeoutTest.class,
-
-        PGPropertyTest.class,
-
-        V3ParameterListTests.class,
-
-        CopyTest.class,
-        CopyLargeFileTest.class,
-        ServerErrorTest.class,
-        UpsertTest.class,
-        OuterJoinSyntaxTest.class
+    ANTTest.class,
+    ArrayTest.class,
+    BatchedInsertReWriteEnabledTest.class,
+    BatchExecuteTest.class,
+    BatchFailureTest.class,
+    BlobTest.class,
+    BlobTransactionTest.class,
+    CallableStmtTest.class,
+    ClientEncodingTest.class,
+    ColumnSanitiserDisabledTest.class,
+    ColumnSanitiserEnabledTest.class,
+    CommandCompleteParserNegativeTest.class,
+    CommandCompleteParserTest.class,
+    ConcurrentStatementFetch.class,
+    ConnectionTest.class,
+    ConnectTimeoutTest.class,
+    CopyLargeFileTest.class,
+    CopyTest.class,
+    CursorFetchTest.class,
+    DatabaseEncodingTest.class,
+    DatabaseMetaDataPropertiesTest.class,
+    DatabaseMetaDataTest.class,
+    DateStyleTest.class,
+    DateTest.class,
+    DeepBatchedInsertStatementTest.class,
+    DriverTest.class,
+    EncodingTest.class,
+    ExpressionPropertiesTest.class,
+    GeometricTest.class,
+    GetXXXTest.class,
+    HostSpecTest.class,
+    IntervalTest.class,
+    JavaVersionTest.class,
+    JBuilderTest.class,
+    LoginTimeoutTest.class,
+    LruCacheTest.class,
+    MiscTest.class,
+    NativeQueryBindLengthTest.class,
+    NotifyTest.class,
+    OidToStringTest.class,
+    OidValueOfTest.class,
+    OptionsPropertyTest.class,
+    OuterJoinSyntaxTest.class,
+    ParameterStatusTest.class,
+    ParserTest.class,
+    PGPropertyTest.class,
+    PGTimestampTest.class,
+    PGTimeTest.class,
+    PreparedStatementTest.class,
+    PrimitiveArraySupportTest.class,
+    QuotationTest.class,
+    ReaderInputStreamTest.class,
+    RefCursorTest.class,
+    ReplaceProcessingTest.class,
+    ResultSetMetaDataTest.class,
+    ResultSetTest.class,
+    ReturningParserTest.class,
+    SearchPathLookupTest.class,
+    ServerCursorTest.class,
+    ServerErrorTest.class,
+    ServerPreparedStmtTest.class,
+    ServerVersionParseTest.class,
+    ServerVersionTest.class,
+    StatementTest.class,
+    StringTypeUnspecifiedArrayTest.class,
+    TestACL.class,
+    TimestampTest.class,
+    TimeTest.class,
+    TimezoneCachingTest.class,
+    TimezoneTest.class,
+    TypeCacheDLLStressTest.class,
+    UpdateableResultTest.class,
+    UpsertTest.class,
+    V3ParameterListTests.class,
 })
 public class Jdbc2TestSuite {
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ParameterStatusTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ParameterStatusTest.java
new file mode 100644
index 00000000..9951a306
--- /dev/null
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ParameterStatusTest.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc2;
+
+import org.postgresql.PGConnection;
+import org.postgresql.core.ServerVersion;
+import org.postgresql.test.TestUtil;
+
+import org.hamcrest.core.StringStartsWith;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.sql.Statement;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.logging.Logger;
+
+/**
+ * Test JDBC extension API for server reported parameter status messages.
+ *
+ * <p>This test covers client interface for server ParameterStatus messages
+ * (GUC_REPORT) parameters via PGConnection.getParameterStatuses() and
+ * PGConnection.getParameterStatus().</p>
+ */
+public class ParameterStatusTest extends BaseTest4 {
+
+  private final TimeZone tzPlus0800 = TimeZone.getTimeZone("GMT+8:00");
+  private final Logger logger = Logger.getLogger(ParameterStatusTest.class.getName());
+
+  @Override
+  public void tearDown() {
+    TimeZone.setDefault(null);
+  }
+
+  @Test
+  public void expectedInitialParameters() throws Exception {
+    TimeZone.setDefault(tzPlus0800);
+    con = TestUtil.openDB();
+
+    Map<String,String> params = ((PGConnection)con).getParameterStatuses();
+
+    // PgJDBC forces the following parameters
+    Assert.assertEquals("UTF8", params.get("client_encoding"));
+    Assert.assertNotNull(params.get("DateStyle"));
+    Assert.assertThat(params.get("DateStyle"), StringStartsWith.startsWith("ISO"));
+
+    // PgJDBC sets TimeZone via Java's TimeZone.getDefault()
+    // Pg reports POSIX timezones which are negated, so:
+    Assert.assertEquals("GMT-08:00", params.get("TimeZone"));
+
+    // Must be reported. All these exist in 8.2 or above, and we don't bother
+    // with test coverage older than that.
+    Assert.assertNotNull(params.get("integer_datetimes"));
+    Assert.assertNotNull(params.get("is_superuser"));
+    Assert.assertNotNull(params.get("server_encoding"));
+    Assert.assertNotNull(params.get("server_version"));
+    Assert.assertNotNull(params.get("session_authorization"));
+    Assert.assertNotNull(params.get("standard_conforming_strings"));
+
+    if (TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_4)) {
+      Assert.assertNotNull(params.get("IntervalStyle"));
+    } else {
+      Assert.assertNull(params.get("IntervalStyle"));
+    }
+
+    // TestUtil forces "ApplicationName=Driver Tests"
+    // if application_name is supported (9.0 or newer)
+    if (TestUtil.haveMinimumServerVersion(con, ServerVersion.v9_0)) {
+      Assert.assertEquals("Driver Tests", params.get("application_name"));
+    } else {
+      Assert.assertNull(params.get("application_name"));
+    }
+
+    // Not reported
+    Assert.assertNull(params.get("nonexistent"));
+    Assert.assertNull(params.get("enable_hashjoin"));
+
+    TestUtil.closeDB(con);
+  }
+
+  @Test
+  public void reportUpdatedParameters() throws Exception {
+    con = TestUtil.openDB();
+
+    if (!TestUtil.haveMinimumServerVersion(con, ServerVersion.v9_0)) {
+      /* This test uses application_name which was added in 9.0 */
+      return;
+    }
+
+    con.setAutoCommit(false);
+    Statement stmt = con.createStatement();
+
+    stmt.executeUpdate("SET application_name = 'pgjdbc_ParameterStatusTest2';");
+    stmt.close();
+
+    // Parameter status should be reported before the ReadyForQuery so we will
+    // have already processed it
+    Assert.assertEquals("pgjdbc_ParameterStatusTest2", ((PGConnection)con).getParameterStatus("application_name"));
+
+    TestUtil.closeDB(con);
+  }
+
+  // Run a txn-level SET then a txn-level SET LOCAL so we can make sure we keep
+  // track of the right GUC value at each point.
+  private void transactionalParametersCommon() throws Exception {
+    Statement stmt = con.createStatement();
+
+    // Initial value assigned by TestUtil
+    Assert.assertEquals("Driver Tests", ((PGConnection)con).getParameterStatus("application_name"));
+
+    // PgJDBC begins an explicit txn here due to autocommit=off so the effect
+    // should be lost on rollback but retained on commit per the docs.
+    stmt.executeUpdate("SET application_name = 'pgjdbc_ParameterStatusTestTxn';");
+    Assert.assertEquals("pgjdbc_ParameterStatusTestTxn", ((PGConnection)con).getParameterStatus("application_name"));
+
+    // SET LOCAL is always txn scoped so the effect here will always be
+    // unwound on txn end.
+    stmt.executeUpdate("SET LOCAL application_name = 'pgjdbc_ParameterStatusTestLocal';");
+    Assert.assertEquals("pgjdbc_ParameterStatusTestLocal", ((PGConnection)con).getParameterStatus("application_name"));
+
+    stmt.close();
+  }
+
+  @Test
+  public void transactionalParametersRollback() throws Exception {
+    con = TestUtil.openDB();
+
+    if (!TestUtil.haveMinimumServerVersion(con, ServerVersion.v9_0)) {
+      /* This test uses application_name which was added in 9.0 */
+      return;
+    }
+
+    con.setAutoCommit(false);
+
+    transactionalParametersCommon();
+
+    // SET unwinds on ROLLBACK
+    con.rollback();
+
+    Assert.assertEquals("Driver Tests", ((PGConnection)con).getParameterStatus("application_name"));
+
+    TestUtil.closeDB(con);
+  }
+
+  @Test
+  public void transactionalParametersCommit() throws Exception {
+    con = TestUtil.openDB();
+
+    if (!TestUtil.haveMinimumServerVersion(con, ServerVersion.v9_0)) {
+      /* This test uses application_name which was added in 9.0 */
+      return;
+    }
+
+    con.setAutoCommit(false);
+
+    transactionalParametersCommon();
+
+    // SET is retained on commit but SET LOCAL is unwound
+    con.commit();
+
+    Assert.assertEquals("pgjdbc_ParameterStatusTestTxn", ((PGConnection)con).getParameterStatus("application_name"));
+
+    TestUtil.closeDB(con);
+  }
+
+  @Test
+  public void transactionalParametersAutocommit() throws Exception {
+    con = TestUtil.openDB();
+
+    if (!TestUtil.haveMinimumServerVersion(con, ServerVersion.v9_0)) {
+      /* This test uses application_name which was added in 9.0 */
+      return;
+    }
+
+    con.setAutoCommit(true);
+    Statement stmt = con.createStatement();
+
+    // A SET LOCAL in autocommit should have no visible effect as we report the reset value too
+    Assert.assertEquals("Driver Tests", ((PGConnection)con).getParameterStatus("application_name"));
+    stmt.executeUpdate("SET LOCAL application_name = 'pgjdbc_ParameterStatusTestLocal';");
+    Assert.assertEquals("Driver Tests", ((PGConnection)con).getParameterStatus("application_name"));
+
+    stmt.close();
+    TestUtil.closeDB(con);
+  }
+
+  @Test(expected = UnsupportedOperationException.class)
+  public void parameterMapReadOnly() throws Exception {
+    try {
+      con = TestUtil.openDB();
+      Map params = ((PGConnection)con).getParameterStatuses();
+      params.put("DateStyle", "invalid");
+      Assert.fail("Attempt to write to exposed parameters map must throw");
+    } finally {
+      TestUtil.closeDB(con);
+    }
+  }
+
+  @Test
+  public void parameterMapIsView() throws Exception {
+    con = TestUtil.openDB();
+
+    if (!TestUtil.haveMinimumServerVersion(con, ServerVersion.v9_0)) {
+      /* This test uses application_name which was added in 9.0 */
+      return;
+    }
+
+    Map params = ((PGConnection)con).getParameterStatuses();
+
+    Statement stmt = con.createStatement();
+
+    Assert.assertEquals("Driver Tests", params.get("application_name"));
+    stmt.executeUpdate("SET application_name = 'pgjdbc_paramstatus_view';");
+    Assert.assertEquals("pgjdbc_paramstatus_view", params.get("application_name"));
+
+    stmt.close();
+    TestUtil.closeDB(con);
+  }
+
+}
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/StatementTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/StatementTest.java
index ef684402..7c3cf6e5 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/StatementTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/StatementTest.java
@@ -729,11 +729,21 @@ public class StatementTest {
     Statement stmt2 = con.createStatement();
     while (System.currentTimeMillis() < deadLine) {
       try {
-        stmt.execute("select 1");
+        // This usually won't time out but scheduler jitter, server load
+        // etc can cause a timeout.
+        stmt.executeQuery("select 1;");
       } catch (SQLException e) {
-        // ignore "statement cancelled"
+        // Expect "57014 query_canceled" (en-msg is "canceling statement due to statement timeout")
+        // but anything else is fatal. We can't differentiate other causes of statement cancel like
+        // "canceling statement due to user request" without error message matching though, and we
+        // don't want to do that.
+        Assert.assertEquals(
+            "Query is expected to be cancelled via st.close(), got " + e.getMessage(),
+            PSQLState.QUERY_CANCELED.getState(),
+            e.getSQLState());
       }
-      stmt2.executeQuery("select 1");
+      // Must never time out.
+      stmt2.executeQuery("select 1;");
     }
   }
 
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/BaseDataSourceFailoverUrlsTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/BaseDataSourceFailoverUrlsTest.java
new file mode 100644
index 00000000..4b0acd65
--- /dev/null
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/BaseDataSourceFailoverUrlsTest.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (c) 2004, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc2.optional;
+
+import static org.junit.Assert.assertEquals;
+
+import org.postgresql.ds.common.BaseDataSource;
+
+import org.junit.Test;
+
+import java.io.IOException;
+import javax.naming.NamingException;
+
+/**
+* tests that failover urls survive the parse/rebuild roundtrip with and without specific ports
+*/
+public class BaseDataSourceFailoverUrlsTest {
+
+  private static final String DEFAULT_PORT = "5432";
+
+  @Test
+  public void testFullDefault() throws ClassNotFoundException, NamingException, IOException {
+    roundTripFromUrl("jdbc:postgresql://server/database", "jdbc:postgresql://server:" + DEFAULT_PORT + "/database");
+  }
+
+  @Test
+  public void testTwoNoPorts() throws ClassNotFoundException, NamingException, IOException {
+    roundTripFromUrl("jdbc:postgresql://server1,server2/database", "jdbc:postgresql://server1:" + DEFAULT_PORT + ",server2:" + DEFAULT_PORT + "/database");
+  }
+
+  @Test
+  public void testTwoWithPorts() throws ClassNotFoundException, NamingException, IOException {
+    roundTripFromUrl("jdbc:postgresql://server1:1234,server2:2345/database", "jdbc:postgresql://server1:1234,server2:2345/database");
+  }
+
+  @Test
+  public void testTwoFirstPort() throws ClassNotFoundException, NamingException, IOException {
+    roundTripFromUrl("jdbc:postgresql://server1,server2:2345/database", "jdbc:postgresql://server1:" + DEFAULT_PORT + ",server2:2345/database");
+  }
+
+  @Test
+  public void testTwoLastPort() throws ClassNotFoundException, NamingException, IOException {
+    roundTripFromUrl("jdbc:postgresql://server1:2345,server2/database", "jdbc:postgresql://server1:2345,server2:" + DEFAULT_PORT + "/database");
+  }
+
+  @Test
+  public void testNullPorts() {
+    BaseDataSource bds = newDS();
+    bds.setDatabaseName("database");
+    bds.setPortNumbers(null);
+    assertUrlWithoutParamsEquals("jdbc:postgresql://localhost/database", bds.getURL());
+    assertEquals(0, bds.getPortNumber());
+    assertEquals(0, bds.getPortNumbers()[0]);
+  }
+
+  @Test
+  public void testEmptyPorts() {
+    BaseDataSource bds = newDS();
+    bds.setDatabaseName("database");
+    bds.setPortNumbers(new int[0]);
+    assertUrlWithoutParamsEquals("jdbc:postgresql://localhost/database", bds.getURL());
+    assertEquals(0, bds.getPortNumber());
+    assertEquals(0, bds.getPortNumbers()[0]);
+  }
+
+  private BaseDataSource newDS() {
+    return new BaseDataSource() {
+      @Override
+      public String getDescription() {
+        return "BaseDataSourceFailoverUrlsTest-DS";
+      }
+    };
+  }
+
+  private void roundTripFromUrl(String in, String expected) throws NamingException, ClassNotFoundException, IOException {
+    BaseDataSource bds = newDS();
+
+    bds.setUrl(in);
+    assertUrlWithoutParamsEquals(expected, bds.getURL());
+
+    bds.setFromReference(bds.getReference());
+    assertUrlWithoutParamsEquals(expected, bds.getURL());
+
+    bds.initializeFrom(bds);
+    assertUrlWithoutParamsEquals(expected, bds.getURL());
+  }
+
+  private static String jdbcUrlStripParams(String in) {
+    return in.replaceAll("\\?.*$", "");
+  }
+
+  private static void assertUrlWithoutParamsEquals(String expected, String url) {
+    assertEquals(expected, jdbcUrlStripParams(url));
+  }
+}
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java
index bcfeade4..e5edcbe7 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/ConnectionPoolTest.java
@@ -9,7 +9,6 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
-import org.postgresql.PGConnection;
 import org.postgresql.core.ServerVersion;
 import org.postgresql.ds.PGConnectionPoolDataSource;
 import org.postgresql.jdbc2.optional.ConnectionPool;
@@ -329,18 +328,9 @@ public class ConnectionPoolTest extends BaseDataSourceTest {
       Assume.assumeTrue("pg_terminate_backend requires PostgreSQL 8.4+",
           TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_4));
 
-      int pid = ((PGConnection) con).getBackendPID();
-
-      Connection adminCon = TestUtil.openPrivilegedDB();
-      try {
-        Statement statement = adminCon.createStatement();
-        statement.executeQuery("SELECT pg_terminate_backend(" + pid + ")");
-      } finally {
-        TestUtil.closeDB(adminCon);
-      }
+      TestUtil.terminateBackend(con);
       try {
-        Statement statement = con.createStatement();
-        statement.executeQuery("SELECT 1");
+        TestUtil.executeQuery(con, "SELECT 1");
         fail("The connection should not be opened anymore. An exception was expected");
       } catch (SQLException e) {
         // this is expected as the connection has been forcibly closed from backend
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java
index 00c5d1a0..b9ca7240 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/OptionalTestSuite.java
@@ -15,12 +15,15 @@ import org.junit.runners.Suite;
  * @author Aaron Mulder (ammulder@chariotsolutions.com)
  */
 @RunWith(Suite.class)
-@Suite.SuiteClasses({SimpleDataSourceTest.class,
-        SimpleDataSourceWithUrlTest.class,
-        SimpleDataSourceWithSetURLTest.class,
+@Suite.SuiteClasses({
+        BaseDataSourceFailoverUrlsTest.class,
+        CaseOptimiserDataSourceTest.class,
         ConnectionPoolTest.class,
         PoolingDataSourceTest.class,
-        CaseOptimiserDataSourceTest.class})
+        SimpleDataSourceTest.class,
+        SimpleDataSourceWithSetURLTest.class,
+        SimpleDataSourceWithUrlTest.class,
+})
 public class OptionalTestSuite {
 
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc3/CompositeTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc3/CompositeTest.java
index 32468489..3e5a120a 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc3/CompositeTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc3/CompositeTest.java
@@ -9,11 +9,16 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import org.postgresql.PGConnection;
+import org.postgresql.core.ServerVersion;
+import org.postgresql.jdbc.PreferQueryMode;
 import org.postgresql.test.TestUtil;
 import org.postgresql.util.PGobject;
 
 import org.junit.After;
+import org.junit.Assume;
 import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
 
 import java.sql.Array;
@@ -26,6 +31,16 @@ public class CompositeTest {
 
   private Connection conn;
 
+  @BeforeClass
+  public static void beforeClass() throws Exception {
+    Connection conn = TestUtil.openDB();
+    try {
+      Assume.assumeTrue("uuid requires PostgreSQL 8.3+", TestUtil.haveMinimumServerVersion(conn, ServerVersion.v8_3));
+    } finally {
+      conn.close();
+    }
+  }
+
   @Before
   public void setUp() throws Exception {
     conn = TestUtil.openDB();
@@ -74,6 +89,7 @@ public class CompositeTest {
 
   @Test
   public void testSimpleArgumentSelect() throws SQLException {
+    Assume.assumeTrue("Skip if running in simple query mode", conn.unwrap(PGConnection.class).getPreferQueryMode() != PreferQueryMode.SIMPLE);
     PreparedStatement pstmt = conn.prepareStatement("SELECT ?");
     PGobject pgo = new PGobject();
     pgo.setType("simplecompositetest");
@@ -87,6 +103,7 @@ public class CompositeTest {
 
   @Test
   public void testComplexArgumentSelect() throws SQLException {
+    Assume.assumeTrue("Skip if running in simple query mode", conn.unwrap(PGConnection.class).getPreferQueryMode() != PreferQueryMode.SIMPLE);
     PreparedStatement pstmt = conn.prepareStatement("SELECT ?");
     PGobject pgo = new PGobject();
     pgo.setType("\"Composites\".\"ComplexCompositeTest\"");
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc3/Jdbc3TestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc3/Jdbc3TestSuite.java
index a90abf63..5e41f7bd 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc3/Jdbc3TestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc3/Jdbc3TestSuite.java
@@ -13,18 +13,20 @@ import org.junit.runners.Suite;
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-    Jdbc3CallableStatementTest.class,
-    GeneratedKeysTest.class,
     CompositeQueryParseTest.class,
-    SqlCommandParseTest.class,
+    CompositeTest.class,
+    DatabaseMetaDataTest.class,
+    GeneratedKeysTest.class,
+    Jdbc3BlobTest.class,
+    Jdbc3CallableStatementTest.class,
     Jdbc3SavepointTest.class,
-    TypesTest.class,
-    ResultSetTest.class,
     ParameterMetaDataTest.class,
-    Jdbc3BlobTest.class,
-    DatabaseMetaDataTest.class,
+    ResultSetTest.class,
     SendRecvBufferSizeTest.class,
-    StringTypeParameterTest.class})
+    SqlCommandParseTest.class,
+    StringTypeParameterTest.class,
+    TypesTest.class,
+})
 public class Jdbc3TestSuite {
 
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/DatabaseMetaDataTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/DatabaseMetaDataTest.java
index 813a777f..a22f42ae 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/DatabaseMetaDataTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/DatabaseMetaDataTest.java
@@ -232,4 +232,17 @@ public class DatabaseMetaDataTest {
       stmt.execute("DROP FUNCTION getfunc_f3(int, varchar)");
     }
   }
+
+  @Test
+  public void testSortedDataTypes() throws SQLException {
+    // https://github.com/pgjdbc/pgjdbc/issues/716
+    DatabaseMetaData dbmd = conn.getMetaData();
+    ResultSet rs = dbmd.getTypeInfo();
+    int lastType = Integer.MIN_VALUE;
+    while (rs.next()) {
+      int type = rs.getInt("DATA_TYPE");
+      assertTrue(lastType <= type);
+      lastType = type;
+    }
+  }
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/IsValidTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/IsValidTest.java
index 8de8f43d..c68cc733 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/IsValidTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/IsValidTest.java
@@ -5,66 +5,62 @@
 
 package org.postgresql.test.jdbc4;
 
-import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 
-import org.postgresql.core.BaseConnection;
+import org.postgresql.core.ServerVersion;
 import org.postgresql.core.TransactionState;
 import org.postgresql.test.TestUtil;
 import org.postgresql.test.jdbc2.BaseTest4;
 
+import org.junit.Assume;
 import org.junit.Test;
 
 import java.sql.Connection;
-import java.sql.Statement;
+import java.sql.SQLException;
 
 public class IsValidTest extends BaseTest4 {
-
-  private TransactionState getTransactionState(Connection conn) {
-    return ((BaseConnection) conn).getTransactionState();
+  @Test
+  public void testIsValidShouldNotModifyTransactionStateOutsideTransaction() throws SQLException {
+    TransactionState initialTransactionState = TestUtil.getTransactionState(con);
+    assertTrue("Connection should be valid", con.isValid(0));
+    TestUtil.assertTransactionState("Transaction state should not be modified by non-transactional Connection.isValid(...)", con, initialTransactionState);
   }
 
   @Test
-  public void testIsValid() throws Exception {
-    try {
-      assertTrue(con.isValid(0));
-    } finally {
-      TestUtil.closeDB(con);
-    }
-    assertFalse(con.isValid(0));
+  public void testIsValidShouldNotModifyTransactionStateInEmptyTransaction() throws SQLException {
+    con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
+    con.setAutoCommit(false);
+    TransactionState transactionState = TestUtil.getTransactionState(con);
+    assertTrue("Connection should be valid", con.isValid(0));
+    TestUtil.assertTransactionState("Transaction state should not be modified by Connection.isValid(...) within an empty transaction", con, transactionState);
   }
 
-  /**
-   * Test that the transaction state is left unchanged.
-   */
   @Test
-  public void testTransactionState() throws Exception {
-    TransactionState transactionState = getTransactionState(con);
-    con.isValid(0);
-    assertEquals("Transaction state has been changed", transactionState,
-        getTransactionState(con));
-
+  public void testIsValidShouldNotModifyTransactionStateInNonEmptyTransaction() throws SQLException {
     con.setTransactionIsolation(Connection.TRANSACTION_READ_COMMITTED);
     con.setAutoCommit(false);
-    try {
-      transactionState = getTransactionState(con);
-      con.isValid(0);
-      assertEquals("Transaction state has been changed", transactionState,
-          getTransactionState(con));
+    TestUtil.executeQuery(con, "SELECT 1");
+    TransactionState transactionState = TestUtil.getTransactionState(con);
+    assertTrue("Connection should be valid", con.isValid(0));
+    TestUtil.assertTransactionState("Transaction state should not be modified by Connection.isValid(...) within a non-empty transaction", con, transactionState);
+  }
+
+  @Test
+  public void testIsValidRemoteClose() throws SQLException, InterruptedException {
+    Assume.assumeTrue("Unable to use pg_terminate_backend(...) before version 8.4", TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_4));
 
-      Statement stmt = con.createStatement();
-      stmt.execute("SELECT 1");
-      transactionState = getTransactionState(con);
-      con.isValid(0);
-      assertEquals("Transaction state has been changed", transactionState,
-          getTransactionState(con));
-    } finally {
-      try {
-        con.setAutoCommit(true);
-      } catch (final Exception e) {
+    boolean wasTerminated = TestUtil.terminateBackend(con);
+    assertTrue("The backend should be terminated", wasTerminated);
+
+    // Keeps checking for up to 5-seconds that the connection is marked invalid
+    for (int i = 0;i < 500;i++) {
+      if (!con.isValid(0)) {
+        break;
       }
+      // Wait a bit to give the connection a chance to gracefully handle the termination
+      Thread.sleep(10);
     }
+    assertFalse("The terminated connection should not be valid", con.isValid(0));
   }
-
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/Jdbc4TestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/Jdbc4TestSuite.java
index 405d977f..29292445 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/Jdbc4TestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/Jdbc4TestSuite.java
@@ -13,18 +13,20 @@ import org.junit.runners.Suite;
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-        DatabaseMetaDataTest.class,
-        ArrayTest.class,
-        WrapperTest.class,
-        BinaryTest.class,
-        IsValidTest.class,
-        ClientInfoTest.class,
-        PGCopyInputStreamTest.class,
-        BlobTest.class,
-        BinaryStreamTest.class,
-        CharacterStreamTest.class,
-        UUIDTest.class,
-        XmlTest.class
+    ArrayTest.class,
+    BinaryStreamTest.class,
+    BinaryTest.class,
+    BlobTest.class,
+    CharacterStreamTest.class,
+    ClientInfoTest.class,
+    DatabaseMetaDataTest.class,
+    IsValidTest.class,
+    JsonbTest.class,
+    LogTest.class,
+    PGCopyInputStreamTest.class,
+    UUIDTest.class,
+    WrapperTest.class,
+    XmlTest.class,
 })
 public class Jdbc4TestSuite {
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/GetObjectTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/GetObjectTest.java
index a452bece..bcdc541e 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/GetObjectTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/GetObjectTest.java
@@ -22,6 +22,7 @@ import org.postgresql.geometric.PGpolygon;
 import org.postgresql.test.TestUtil;
 import org.postgresql.util.PGInterval;
 import org.postgresql.util.PGmoney;
+import org.postgresql.util.PGobject;
 
 import org.junit.After;
 import org.junit.Before;
@@ -938,42 +939,48 @@ public class GetObjectTest {
     }
   }
 
-  /**
-   * Test the behavior getObject for inet columns.
-   */
-  @Test
-  public void testGetInet4Address() throws SQLException, UnknownHostException {
+  private void testInet(String inet, InetAddress expectedAddr, String expectedText) throws SQLException, UnknownHostException {
+    PGobject expectedObj = new PGobject();
+    expectedObj.setType("inet");
+    expectedObj.setValue(expectedText);
     Statement stmt = conn.createStatement();
-    String expected = "192.168.100.128";
-    stmt.executeUpdate(TestUtil.insertSQL("table1","inet_column","'" + expected + "'"));
-
-    ResultSet rs = stmt.executeQuery(TestUtil.selectSQL("table1", "inet_column"));
+    ResultSet rs = stmt.executeQuery("SELECT '" + inet + "'::inet AS inet_column");
     try {
       assertTrue(rs.next());
-      assertEquals(InetAddress.getByName(expected), rs.getObject("inet_column", InetAddress.class));
-      assertEquals(InetAddress.getByName(expected), rs.getObject(1, InetAddress.class));
+      assertEquals("The string value of the inet should match when fetched via getString(...)", expectedText, rs.getString(1));
+      assertEquals("The string value of the inet should match when fetched via getString(...)", expectedText, rs.getString("inet_column"));
+      assertEquals("The object value of the inet should match when fetched via getObject(...)", expectedObj, rs.getObject(1));
+      assertEquals("The object value of the inet should match when fetched via getObject(...)", expectedObj, rs.getObject("inet_column"));
+      assertEquals("The InetAddress value should match when fetched via getObject(..., InetAddress.class)", expectedAddr, rs.getObject("inet_column", InetAddress.class));
+      assertEquals("The InetAddress value should match when fetched via getObject(..., InetAddress.class)", expectedAddr, rs.getObject(1, InetAddress.class));
     } finally {
       rs.close();
+      stmt.close();
     }
   }
 
   /**
-   * Test the behavior getObject for inet columns.
+   * Test the behavior getObject for ipv4 inet columns.
    */
   @Test
-  public void testGetInet6Address() throws SQLException, UnknownHostException {
-    Statement stmt = conn.createStatement();
-    String expected = "2001:4f8:3:ba:2e0:81ff:fe22:d1f1";
-    stmt.executeUpdate(TestUtil.insertSQL("table1","inet_column","'" + expected + "'"));
+  public void testGetInet4Address() throws SQLException, UnknownHostException {
+    String inet = "192.168.100.128";
+    InetAddress addr = InetAddress.getByName(inet);
+    testInet(inet, addr, inet);
+    testInet(inet + "/16", addr, inet + "/16");
+    testInet(inet + "/32", addr, inet);
+  }
 
-    ResultSet rs = stmt.executeQuery(TestUtil.selectSQL("table1", "inet_column"));
-    try {
-      assertTrue(rs.next());
-      assertEquals(InetAddress.getByName(expected), rs.getObject("inet_column", InetAddress.class));
-      assertEquals(InetAddress.getByName(expected), rs.getObject(1, InetAddress.class));
-    } finally {
-      rs.close();
-    }
+  /**
+   * Test the behavior getObject for ipv6 inet columns.
+   */
+  @Test
+  public void testGetInet6Address() throws SQLException, UnknownHostException {
+    String inet = "2001:4f8:3:ba:2e0:81ff:fe22:d1f1";
+    InetAddress addr = InetAddress.getByName(inet);
+    testInet(inet, addr, inet);
+    testInet(inet + "/16", addr, inet + "/16");
+    testInet(inet + "/128", addr, inet);
   }
 
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/Jdbc41TestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/Jdbc41TestSuite.java
index 046696a0..f9ceeed9 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/Jdbc41TestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/Jdbc41TestSuite.java
@@ -13,13 +13,13 @@ import org.junit.runners.Suite;
  */
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-        SchemaTest.class,
-        AbortTest.class,
-        CloseOnCompletionTest.class,
-        SharedTimerClassLoaderLeakTest.class,
-        NetworkTimeoutTest.class,
-        GetObjectTest.class}
-    )
+    AbortTest.class,
+    CloseOnCompletionTest.class,
+    GetObjectTest.class,
+    NetworkTimeoutTest.class,
+    SchemaTest.class,
+    SharedTimerClassLoaderLeakTest.class,
+})
 public class Jdbc41TestSuite {
 
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/SchemaTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/SchemaTest.java
index 34ca983e..f843ab56 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/SchemaTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc4/jdbc41/SchemaTest.java
@@ -5,10 +5,16 @@
 
 package org.postgresql.test.jdbc4.jdbc41;
 
+import static org.hamcrest.CoreMatchers.containsString;
+import static org.hamcrest.core.IsEqual.equalTo;
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertThat;
 import static org.junit.Assert.fail;
 
+import org.postgresql.PGProperty;
 import org.postgresql.test.TestUtil;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
 
 import org.junit.After;
 import org.junit.Before;
@@ -217,6 +223,90 @@ public class SchemaTest {
     ps.close();
   }
 
+  @Test
+  public void testCurrentSchemaPropertyVisibilityTableDuringFunctionCreation() throws SQLException {
+    Properties properties = new Properties();
+    properties.setProperty(PGProperty.CURRENT_SCHEMA.getName(), "public,schema1,schema2");
+    Connection connection = TestUtil.openDB(properties);
+
+    TestUtil.execute("create table schema1.check_table (test_col text)", connection);
+    TestUtil.execute("insert into schema1.check_table (test_col) values ('test_value')", connection);
+    TestUtil.execute("create or replace function schema2.check_fun () returns text as $$"
+        + " select test_col from check_table"
+        + "$$ language sql immutable", connection);
+    connection.close();
+  }
+
+  @Test
+  public void testCurrentSchemaPropertyNotVisibilityTableDuringFunctionCreation() throws SQLException {
+    Properties properties = new Properties();
+    properties.setProperty(PGProperty.CURRENT_SCHEMA.getName(), "public,schema2");
+
+    try (Connection connection = TestUtil.openDB(properties)) {
+      TestUtil.execute("create table schema1.check_table (test_col text)", connection);
+      TestUtil.execute("insert into schema1.check_table (test_col) values ('test_value')", connection);
+      TestUtil.execute("create or replace function schema2.check_fun (txt text) returns text as $$"
+          + " select test_col from check_table"
+          + "$$ language sql immutable", connection);
+    } catch (PSQLException e) {
+      String sqlState = e.getSQLState();
+      String message = e.getMessage();
+      assertThat("Test creates function in schema 'schema2' and this function try use table \"check_table\" "
+            + "from schema 'schema1'. We expect here sql error code - "
+            + PSQLState.UNDEFINED_TABLE + ", because search_path does not contains schema 'schema1' and "
+            + "postgres does not see table \"check_table\"",
+            sqlState,
+            equalTo(PSQLState.UNDEFINED_TABLE.getState())
+      );
+      assertThat(
+          "Test creates function in schema 'schema2' and this function try use table \"check_table\" "
+              + "from schema 'schema1'. We expect here that sql error message will be contains \"check_table\", "
+              + "because search_path does not contains schema 'schema1' and postgres does not see "
+              + "table \"check_table\"",
+            message,
+            containsString("\"check_table\"")
+      );
+    }
+  }
+
+  @Test
+  public void testCurrentSchemaPropertyVisibilityFunction() throws SQLException {
+    testCurrentSchemaPropertyVisibilityTableDuringFunctionCreation();
+    Properties properties = new Properties();
+    properties.setProperty(PGProperty.CURRENT_SCHEMA.getName(), "public,schema1,schema2");
+    Connection connection = TestUtil.openDB(properties);
+
+    TestUtil.execute("select check_fun()", connection);
+    connection.close();
+  }
+
+  @Test
+  public void testCurrentSchemaPropertyNotVisibilityTableInsideFunction() throws SQLException {
+    testCurrentSchemaPropertyVisibilityTableDuringFunctionCreation();
+    Properties properties = new Properties();
+    properties.setProperty(PGProperty.CURRENT_SCHEMA.getName(), "public,schema2");
+
+    try (Connection connection = TestUtil.openDB(properties)) {
+      TestUtil.execute("select check_fun()", connection);
+    } catch (PSQLException e) {
+      String sqlState = e.getSQLState();
+      String message = e.getMessage();
+      assertThat("Test call function in schema 'schema2' and this function uses table \"check_table\" "
+            + "from schema 'schema1'. We expect here sql error code - " + PSQLState.UNDEFINED_TABLE + ", "
+            + "because search_path does not contains schema 'schema1' and postgres does not see table \"check_table\".",
+            sqlState,
+            equalTo(PSQLState.UNDEFINED_TABLE.getState())
+      );
+      assertThat(
+          "Test call function in schema 'schema2' and this function uses table \"check_table\" "
+              + "from schema 'schema1'. We expect here that sql error message will be contains \"check_table\", because "
+              + " search_path does not contains schema 'schema1' and postgres does not see table \"check_table\"",
+          message,
+          containsString("\"check_table\"")
+      );
+    }
+  }
+
   private void assertColType(PreparedStatement ps, String message, int expected) throws SQLException {
     ResultSet rs = ps.executeQuery();
     ResultSetMetaData md = rs.getMetaData();
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/GetObject310InfinityTests.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/GetObject310InfinityTests.java
index f4cc3656..04b81c84 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/GetObject310InfinityTests.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/GetObject310InfinityTests.java
@@ -30,11 +30,11 @@ import java.util.Collection;
 public class GetObject310InfinityTests extends BaseTest4 {
   private final String expression;
   private final String pgType;
-  private final Class klass;
+  private final Class<?> klass;
   private final Object expectedValue;
 
   public GetObject310InfinityTests(BinaryMode binaryMode, String expression,
-      String pgType, Class klass, Object expectedValue) {
+      String pgType, Class<?> klass, Object expectedValue) {
     setBinaryMode(binaryMode);
     this.expression = expression;
     this.pgType = pgType;
@@ -56,7 +56,7 @@ public class GetObject310InfinityTests extends BaseTest4 {
       for (String expression : Arrays.asList("-infinity", "infinity")) {
         for (String pgType : Arrays.asList("date", "timestamp",
             "timestamp with time zone")) {
-          for (Class klass : Arrays.asList(LocalDate.class, LocalDateTime.class,
+          for (Class<?> klass : Arrays.asList(LocalDate.class, LocalDateTime.class,
               OffsetDateTime.class)) {
             if (klass.equals(LocalDate.class) && !pgType.equals("date")) {
               continue;
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/GetObject310Test.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/GetObject310Test.java
index e3b048bf..b5dc6be6 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/GetObject310Test.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/GetObject310Test.java
@@ -6,10 +6,12 @@
 package org.postgresql.test.jdbc42;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assume.assumeTrue;
 
+import org.postgresql.core.ServerVersion;
 import org.postgresql.test.TestUtil;
 import org.postgresql.test.jdbc2.BaseTest4;
 import org.postgresql.util.PSQLException;
@@ -19,20 +21,28 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.Parameterized;
 
+import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.time.Duration;
 import java.time.LocalDate;
 import java.time.LocalDateTime;
 import java.time.LocalTime;
 import java.time.OffsetDateTime;
 import java.time.ZoneId;
 import java.time.ZoneOffset;
+import java.time.chrono.IsoEra;
+import java.time.temporal.ChronoField;
+import java.time.temporal.Temporal;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.List;
 import java.util.TimeZone;
+import java.util.function.UnaryOperator;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @RunWith(Parameterized.class)
 public class GetObject310Test extends BaseTest4 {
@@ -191,6 +201,9 @@ public class GetObject310Test extends BaseTest4 {
             // This is a pre-1970 date, so check if it is rounded properly
             "1950-07-20T02:00:00",
 
+            // Ensure the calendar is proleptic
+            "1582-09-30T00:00:00", "1582-10-16T00:00:00",
+
             // On 2000-10-29 03:00:00 Moscow went to regular time, thus local time became 02:00:00
             "2000-10-29T01:59:59", "2000-10-29T02:00:00", "2000-10-29T02:00:01", "2000-10-29T02:59:59",
             "2000-10-29T03:00:00", "2000-10-29T03:00:01", "2000-10-29T03:59:59", "2000-10-29T04:00:00",
@@ -262,4 +275,101 @@ public class GetObject310Test extends BaseTest4 {
     }
   }
 
+  @Test
+  public void testBcTimestamp() throws SQLException {
+
+    Statement stmt = con.createStatement();
+    ResultSet rs = stmt.executeQuery("SELECT '1582-09-30 12:34:56 BC'::timestamp");
+    try {
+      assertTrue(rs.next());
+      LocalDateTime expected = LocalDateTime.of(1582, 9, 30, 12, 34, 56)
+          .with(ChronoField.ERA, IsoEra.BCE.getValue());
+      LocalDateTime actual = rs.getObject(1, LocalDateTime.class);
+      assertEquals(expected, actual);
+      assertFalse(rs.next());
+    } finally {
+      rs.close();
+      stmt.close();
+    }
+  }
+
+  @Test
+  public void testBcTimestamptz() throws SQLException {
+
+    Statement stmt = con.createStatement();
+    ResultSet rs = stmt.executeQuery("SELECT '1582-09-30 12:34:56Z BC'::timestamp");
+    try {
+      assertTrue(rs.next());
+      OffsetDateTime expected = OffsetDateTime.of(1582, 9, 30, 12, 34, 56, 0, UTC)
+          .with(ChronoField.ERA, IsoEra.BCE.getValue());
+      OffsetDateTime actual = rs.getObject(1, OffsetDateTime.class);
+      assertEquals(expected, actual);
+      assertFalse(rs.next());
+    } finally {
+      rs.close();
+      stmt.close();
+    }
+  }
+
+  @Test
+  public void testProlepticCalendarTimestamp() throws SQLException {
+    // date time ranges and CTEs are both new with 8.4
+    assumeMinimumServerVersion(ServerVersion.v8_4);
+    LocalDateTime start = LocalDate.of(1582, 9, 30).atStartOfDay();
+    LocalDateTime end = LocalDate.of(1582, 10, 16).atStartOfDay();
+    long numberOfDays = Duration.between(start, end).toDays() + 1L;
+    List<LocalDateTime> range = Stream.iterate(start, new LocalDateTimePlusOneDay())
+        .limit(numberOfDays)
+        .collect(Collectors.toList());
+
+    runProlepticTests(LocalDateTime.class, "'1582-09-30 00:00'::timestamp, '1582-10-16 00:00'::timestamp", range);
+  }
+
+  @Test
+  public void testProlepticCalendarTimestamptz() throws SQLException {
+    // date time ranges and CTEs are both new with 8.4
+    assumeMinimumServerVersion(ServerVersion.v8_4);
+    OffsetDateTime start = LocalDate.of(1582, 9, 30).atStartOfDay().atOffset(UTC);
+    OffsetDateTime end = LocalDate.of(1582, 10, 16).atStartOfDay().atOffset(UTC);
+    long numberOfDays = Duration.between(start, end).toDays() + 1L;
+    List<OffsetDateTime> range = Stream.iterate(start, new OffsetDateTimePlusOneDay())
+        .limit(numberOfDays)
+        .collect(Collectors.toList());
+
+    runProlepticTests(OffsetDateTime.class, "'1582-09-30 00:00:00 Z'::timestamptz, '1582-10-16 00:00:00 Z'::timestamptz", range);
+  }
+
+  private <T extends Temporal> void runProlepticTests(Class<T> clazz, String selectRange, List<T> range) throws SQLException {
+    List<T> temporals = new ArrayList<>(range.size());
+
+    PreparedStatement stmt = con.prepareStatement("SELECT * FROM generate_series(" + selectRange + ", '1 day');");
+    ResultSet rs = stmt.executeQuery();
+    try {
+      while (rs.next()) {
+        T temporal = rs.getObject(1, clazz);
+        temporals.add(temporal);
+      }
+      assertEquals(range, temporals);
+    } finally {
+      rs.close();
+      stmt.close();
+    }
+  }
+
+  private static class LocalDateTimePlusOneDay implements UnaryOperator<LocalDateTime> {
+
+    @Override
+    public LocalDateTime apply(LocalDateTime x) {
+      return x.plusDays(1);
+    }
+  }
+
+  private static class OffsetDateTimePlusOneDay implements UnaryOperator<OffsetDateTime> {
+
+    @Override
+    public OffsetDateTime apply(OffsetDateTime x) {
+      return x.plusDays(1);
+    }
+  }
+
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/Jdbc42TestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/Jdbc42TestSuite.java
index d4e32c59..3d444fd0 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/Jdbc42TestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/Jdbc42TestSuite.java
@@ -11,13 +11,15 @@ import org.junit.runners.Suite.SuiteClasses;
 
 @RunWith(Suite.class)
 @SuiteClasses({
-    SimpleJdbc42Test.class,
     CustomizeDefaultFetchSizeTest.class,
+    GetObject310InfinityTests.class,
     GetObject310Test.class,
-    PreparedStatementTest.class,
     Jdbc42CallableStatementTest.class,
-    GetObject310InfinityTests.class,
-    SetObject310Test.class})
+    LargeCountJdbc42Test.class,
+    PreparedStatementTest.class,
+    SetObject310Test.class,
+    SimpleJdbc42Test.class,
+})
 public class Jdbc42TestSuite {
 
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/LargeCountJdbc42Test.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/LargeCountJdbc42Test.java
new file mode 100644
index 00000000..fef33625
--- /dev/null
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/LargeCountJdbc42Test.java
@@ -0,0 +1,412 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc42;
+
+import org.postgresql.PGProperty;
+import org.postgresql.test.TestUtil;
+import org.postgresql.test.jdbc2.BaseTest4;
+import org.postgresql.util.PSQLState;
+
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Properties;
+
+/**
+ * Test methods with small counts that return long and failure scenarios. This have two really big
+ * and slow test, they are ignored for CI but can be tested locally to check that it works.
+ */
+@RunWith(Parameterized.class)
+public class LargeCountJdbc42Test extends BaseTest4 {
+
+  private final boolean insertRewrite;
+
+  public LargeCountJdbc42Test(BinaryMode binaryMode, boolean insertRewrite) {
+    this.insertRewrite = insertRewrite;
+    setBinaryMode(binaryMode);
+  }
+
+  @Parameterized.Parameters(name = "binary = {0}, insertRewrite = {1}")
+  public static Iterable<Object[]> data() {
+    Collection<Object[]> ids = new ArrayList<>();
+    for (BinaryMode binaryMode : BinaryMode.values()) {
+      for (boolean insertRewrite : new boolean[]{false, true}) {
+        ids.add(new Object[]{binaryMode, insertRewrite});
+      }
+    }
+    return ids;
+  }
+
+  @Override
+  protected void updateProperties(Properties props) {
+    super.updateProperties(props);
+    PGProperty.REWRITE_BATCHED_INSERTS.set(props, insertRewrite);
+  }
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    TestUtil.createUnloggedTable(con, "largetable", "a boolean");
+  }
+
+  @Override
+  public void tearDown() throws SQLException {
+    TestUtil.dropTable(con, "largetable");
+    TestUtil.closeDB(con);
+  }
+
+  // ********************* EXECUTE LARGE UPDATES *********************
+  //    FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@38cccef, maxRows=0, fetchSize=0, flags=21
+  //    FINEST: FE=> Parse(stmt=null,query="insert into largetable select true from generate_series($1, $2)",oids={20,20})
+  //    FINEST: FE=> Bind(stmt=null,portal=null,$1=<1>,$2=<2147483757>)
+  //    FINEST: FE=> Describe(portal=null)
+  //    FINEST: FE=> Execute(portal=null,limit=1)
+  //    FINEST: FE=> Sync
+  //    FINEST: <=BE ParseComplete [null]
+  //    FINEST: <=BE BindComplete [unnamed]
+  //    FINEST: <=BE NoData
+  //    FINEST: <=BE CommandStatus(INSERT 0 2147483757)
+  //    FINEST: <=BE ReadyForQuery(I)
+  //    FINEST: simple execute, handler=org.postgresql.jdbc.PgStatement$StatementResultHandler@5679c6c6, maxRows=0, fetchSize=0, flags=21
+  //    FINEST: FE=> Parse(stmt=null,query="delete from largetable",oids={})
+  //    FINEST: FE=> Bind(stmt=null,portal=null)
+  //    FINEST: FE=> Describe(portal=null)
+  //    FINEST: FE=> Execute(portal=null,limit=1)
+  //    FINEST: FE=> Sync
+  //    FINEST: <=BE ParseComplete [null]
+  //    FINEST: <=BE BindComplete [unnamed]
+  //    FINEST: <=BE NoData
+  //    FINEST: <=BE CommandStatus(DELETE 2147483757)
+
+  /*
+   * Test PreparedStatement.executeLargeUpdate() and Statement.executeLargeUpdate(String sql)
+   */
+  @Ignore("This is the big and SLOW test")
+  @Test
+  public void testExecuteLargeUpdateBIG() throws Exception {
+    long expected = Integer.MAX_VALUE + 110L;
+    con.setAutoCommit(false);
+    // Test PreparedStatement.executeLargeUpdate()
+    try (PreparedStatement stmt = con.prepareStatement("insert into largetable "
+        + "select true from generate_series(?, ?)")) {
+      stmt.setLong(1, 1);
+      stmt.setLong(2, 2_147_483_757L); // Integer.MAX_VALUE + 110L
+      long count = stmt.executeLargeUpdate();
+      Assert.assertEquals("PreparedStatement 110 rows more than Integer.MAX_VALUE", expected, count);
+    }
+    // Test Statement.executeLargeUpdate(String sql)
+    try (Statement stmt = con.createStatement()) {
+      long count = stmt.executeLargeUpdate("delete from largetable");
+      Assert.assertEquals("Statement 110 rows more than Integer.MAX_VALUE", expected, count);
+    }
+    con.setAutoCommit(true);
+  }
+
+  /*
+   * Test Statement.executeLargeUpdate(String sql)
+   */
+  @Test
+  public void testExecuteLargeUpdateStatementSMALL() throws Exception {
+    try (Statement stmt = con.createStatement()) {
+      long count = stmt.executeLargeUpdate("insert into largetable "
+          + "select true from generate_series(1, 1010)");
+      long expected = 1010L;
+      Assert.assertEquals("Small long return 1010L", expected, count);
+    }
+  }
+
+  /*
+   * Test PreparedStatement.executeLargeUpdate();
+   */
+  @Test
+  public void testExecuteLargeUpdatePreparedStatementSMALL() throws Exception {
+    try (PreparedStatement stmt = con.prepareStatement("insert into largetable "
+        + "select true from generate_series(?, ?)")) {
+      stmt.setLong(1, 1);
+      stmt.setLong(2, 1010L);
+      long count = stmt.executeLargeUpdate();
+      long expected = 1010L;
+      Assert.assertEquals("Small long return 1010L", expected, count);
+    }
+  }
+
+  /*
+   * Test Statement.getLargeUpdateCount();
+   */
+  @Test
+  public void testGetLargeUpdateCountStatementSMALL() throws Exception {
+    try (Statement stmt = con.createStatement()) {
+      boolean isResult = stmt.execute("insert into largetable "
+          + "select true from generate_series(1, 1010)");
+      Assert.assertFalse("False if it is an update count or there are no results", isResult);
+      long count = stmt.getLargeUpdateCount();
+      long expected = 1010L;
+      Assert.assertEquals("Small long return 1010L", expected, count);
+    }
+  }
+
+  /*
+   * Test PreparedStatement.getLargeUpdateCount();
+   */
+  @Test
+  public void testGetLargeUpdateCountPreparedStatementSMALL() throws Exception {
+    try (PreparedStatement stmt = con.prepareStatement("insert into largetable "
+        + "select true from generate_series(?, ?)")) {
+      stmt.setInt(1, 1);
+      stmt.setInt(2, 1010);
+      boolean isResult = stmt.execute();
+      Assert.assertFalse("False if it is an update count or there are no results", isResult);
+      long count = stmt.getLargeUpdateCount();
+      long expected = 1010L;
+      Assert.assertEquals("Small long return 1010L", expected, count);
+    }
+  }
+
+  /*
+   * Test fail SELECT Statement.executeLargeUpdate(String sql)
+   */
+  @Test
+  public void testExecuteLargeUpdateStatementSELECT() throws Exception {
+    try (Statement stmt = con.createStatement()) {
+      long count = stmt.executeLargeUpdate("select true from generate_series(1, 5)");
+      Assert.fail("A result was returned when none was expected. Returned: " + count);
+    } catch (SQLException e) {
+      Assert.assertEquals(PSQLState.TOO_MANY_RESULTS.getState(), e.getSQLState());
+    }
+  }
+
+  /*
+   * Test fail SELECT PreparedStatement.executeLargeUpdate();
+   */
+  @Test
+  public void testExecuteLargeUpdatePreparedStatementSELECT() throws Exception {
+    try (PreparedStatement stmt = con.prepareStatement("select true from generate_series(?, ?)")) {
+      stmt.setLong(1, 1);
+      stmt.setLong(2, 5L);
+      long count = stmt.executeLargeUpdate();
+      Assert.fail("A result was returned when none was expected. Returned: " + count);
+    } catch (SQLException e) {
+      Assert.assertEquals(PSQLState.TOO_MANY_RESULTS.getState(), e.getSQLState());
+    }
+  }
+
+  /*
+   * Test Statement.getLargeUpdateCount();
+   */
+  @Test
+  public void testGetLargeUpdateCountStatementSELECT() throws Exception {
+    try (Statement stmt = con.createStatement()) {
+      boolean isResult = stmt.execute("select true from generate_series(1, 5)");
+      Assert.assertTrue("True since this is a SELECT", isResult);
+      long count = stmt.getLargeUpdateCount();
+      long expected = -1L;
+      Assert.assertEquals("-1 if the current result is a ResultSet object", expected, count);
+    }
+  }
+
+  /*
+   * Test PreparedStatement.getLargeUpdateCount();
+   */
+  @Test
+  public void testGetLargeUpdateCountPreparedStatementSELECT() throws Exception {
+    try (PreparedStatement stmt = con.prepareStatement("select true from generate_series(?, ?)")) {
+      stmt.setLong(1, 1);
+      stmt.setLong(2, 5L);
+      boolean isResult = stmt.execute();
+      Assert.assertTrue("True since this is a SELECT", isResult);
+      long count = stmt.getLargeUpdateCount();
+      long expected = -1L;
+      Assert.assertEquals("-1 if the current result is a ResultSet object", expected, count);
+    }
+  }
+
+  // ********************* BATCH LARGE UPDATES *********************
+  //    FINEST: batch execute 3 queries, handler=org.postgresql.jdbc.BatchResultHandler@3d04a311, maxRows=0, fetchSize=0, flags=21
+  //    FINEST: FE=> Parse(stmt=null,query="insert into largetable select true from generate_series($1, $2)",oids={23,23})
+  //    FINEST: FE=> Bind(stmt=null,portal=null,$1=<1>,$2=<200>)
+  //    FINEST: FE=> Describe(portal=null)
+  //    FINEST: FE=> Execute(portal=null,limit=1)
+  //    FINEST: FE=> Parse(stmt=null,query="insert into largetable select true from generate_series($1, $2)",oids={23,20})
+  //    FINEST: FE=> Bind(stmt=null,portal=null,$1=<1>,$2=<3000000000>)
+  //    FINEST: FE=> Describe(portal=null)
+  //    FINEST: FE=> Execute(portal=null,limit=1)
+  //    FINEST: FE=> Parse(stmt=null,query="insert into largetable select true from generate_series($1, $2)",oids={23,23})
+  //    FINEST: FE=> Bind(stmt=null,portal=null,$1=<1>,$2=<50>)
+  //    FINEST: FE=> Describe(portal=null)
+  //    FINEST: FE=> Execute(portal=null,limit=1)
+  //    FINEST: FE=> Sync
+  //    FINEST: <=BE ParseComplete [null]
+  //    FINEST: <=BE BindComplete [unnamed]
+  //    FINEST: <=BE NoData
+  //    FINEST: <=BE CommandStatus(INSERT 0 200)
+  //    FINEST: <=BE ParseComplete [null]
+  //    FINEST: <=BE BindComplete [unnamed]
+  //    FINEST: <=BE NoData
+  //    FINEST: <=BE CommandStatus(INSERT 0 3000000000)
+  //    FINEST: <=BE ParseComplete [null]
+  //    FINEST: <=BE BindComplete [unnamed]
+  //    FINEST: <=BE NoData
+  //    FINEST: <=BE CommandStatus(INSERT 0 50)
+
+  /*
+   * Test simple PreparedStatement.executeLargeBatch();
+   */
+  @Ignore("This is the big and SLOW test")
+  @Test
+  public void testExecuteLargeBatchStatementBIG() throws Exception {
+    con.setAutoCommit(false);
+    try (PreparedStatement stmt = con.prepareStatement("insert into largetable "
+        + "select true from generate_series(?, ?)")) {
+      stmt.setInt(1, 1);
+      stmt.setInt(2, 200);
+      stmt.addBatch(); // statement one
+      stmt.setInt(1, 1);
+      stmt.setLong(2, 3_000_000_000L);
+      stmt.addBatch(); // statement two
+      stmt.setInt(1, 1);
+      stmt.setInt(2, 50);
+      stmt.addBatch(); // statement three
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertArrayEquals("Large rows inserted via 3 batch", new long[]{200L, 3_000_000_000L, 50L}, actual);
+    }
+    con.setAutoCommit(true);
+  }
+
+  /*
+   * Test simple Statement.executeLargeBatch();
+   */
+  @Test
+  public void testExecuteLargeBatchStatementSMALL() throws Exception {
+    try (Statement stmt = con.createStatement()) {
+      stmt.addBatch("insert into largetable(a) select true"); // statement one
+      stmt.addBatch("insert into largetable select false"); // statement two
+      stmt.addBatch("insert into largetable(a) values(true)"); // statement three
+      stmt.addBatch("insert into largetable values(false)"); // statement four
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertArrayEquals("Rows inserted via 4 batch", new long[]{1L, 1L, 1L, 1L}, actual);
+    }
+  }
+
+  /*
+   * Test simple PreparedStatement.executeLargeBatch();
+   */
+  @Test
+  public void testExecuteLargePreparedStatementStatementSMALL() throws Exception {
+    try (PreparedStatement stmt = con.prepareStatement("insert into largetable "
+        + "select true from generate_series(?, ?)")) {
+      stmt.setInt(1, 1);
+      stmt.setInt(2, 200);
+      stmt.addBatch(); // statement one
+      stmt.setInt(1, 1);
+      stmt.setInt(2, 100);
+      stmt.addBatch(); // statement two
+      stmt.setInt(1, 1);
+      stmt.setInt(2, 50);
+      stmt.addBatch(); // statement three
+      stmt.addBatch(); // statement four, same parms as three
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertArrayEquals("Rows inserted via 4 batch", new long[]{200L, 100L, 50L, 50L}, actual);
+    }
+  }
+
+  /*
+   * Test loop PreparedStatement.executeLargeBatch();
+   */
+  @Test
+  public void testExecuteLargePreparedStatementStatementLoopSMALL() throws Exception {
+    long[] loop = {200, 100, 50, 300, 20, 60, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096};
+    try (PreparedStatement stmt = con.prepareStatement("insert into largetable "
+        + "select true from generate_series(?, ?)")) {
+      for (long i : loop) {
+        stmt.setInt(1, 1);
+        stmt.setLong(2, i);
+        stmt.addBatch();
+      }
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertArrayEquals("Rows inserted via batch", loop, actual);
+    }
+  }
+
+  /*
+   * Test loop PreparedStatement.executeLargeBatch();
+   */
+  @Test
+  public void testExecuteLargeBatchValuesInsertSMALL() throws Exception {
+    boolean[] loop = {true, false, true, false, false, false, true, true, true, true, false, true};
+    try (PreparedStatement stmt = con.prepareStatement("insert into largetable values(?)")) {
+      for (boolean i : loop) {
+        stmt.setBoolean(1, i);
+        stmt.addBatch();
+      }
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertEquals("Rows inserted via batch", loop.length, actual.length);
+      for (long i : actual) {
+        if (insertRewrite) {
+          Assert.assertEquals(Statement.SUCCESS_NO_INFO, i);
+        } else {
+          Assert.assertEquals(1, i);
+        }
+      }
+    }
+  }
+
+  /*
+   * Test null PreparedStatement.executeLargeBatch();
+   */
+  @Test
+  public void testNullExecuteLargeBatchStatement() throws Exception {
+    try (Statement stmt = con.createStatement()) {
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertArrayEquals("addBatch() not called batchStatements is null", new long[0], actual);
+    }
+  }
+
+  /*
+   * Test empty PreparedStatement.executeLargeBatch();
+   */
+  @Test
+  public void testEmptyExecuteLargeBatchStatement() throws Exception {
+    try (Statement stmt = con.createStatement()) {
+      stmt.addBatch("");
+      stmt.clearBatch();
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertArrayEquals("clearBatch() called, batchStatements.isEmpty()", new long[0], actual);
+    }
+  }
+
+  /*
+   * Test null PreparedStatement.executeLargeBatch();
+   */
+  @Test
+  public void testNullExecuteLargeBatchPreparedStatement() throws Exception {
+    try (PreparedStatement stmt = con.prepareStatement("")) {
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertArrayEquals("addBatch() not called batchStatements is null", new long[0], actual);
+    }
+  }
+
+  /*
+   * Test empty PreparedStatement.executeLargeBatch();
+   */
+  @Test
+  public void testEmptyExecuteLargeBatchPreparedStatement() throws Exception {
+    try (PreparedStatement stmt = con.prepareStatement("")) {
+      stmt.addBatch();
+      stmt.clearBatch();
+      long[] actual = stmt.executeLargeBatch();
+      Assert.assertArrayEquals("clearBatch() called, batchStatements.isEmpty()", new long[0], actual);
+    }
+  }
+
+}
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/SetObject310InfinityTests.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/SetObject310InfinityTests.java
new file mode 100644
index 00000000..86457aa2
--- /dev/null
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/SetObject310InfinityTests.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2017, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc42;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import org.postgresql.core.ServerVersion;
+import org.postgresql.test.TestUtil;
+import org.postgresql.test.jdbc2.BaseTest4;
+
+import org.junit.After;
+import org.junit.Assume;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.time.OffsetDateTime;
+import java.util.ArrayList;
+import java.util.Collection;
+
+@RunWith(Parameterized.class)
+public class SetObject310InfinityTests extends BaseTest4 {
+
+  public SetObject310InfinityTests(BinaryMode binaryMode) {
+    setBinaryMode(binaryMode);
+  }
+
+  @Parameterized.Parameters(name = "binary = {0}")
+  public static Iterable<Object[]> data() {
+    Collection<Object[]> ids = new ArrayList<>(2);
+    for (BaseTest4.BinaryMode binaryMode : BaseTest4.BinaryMode.values()) {
+      ids.add(new Object[]{binaryMode});
+    }
+    return ids;
+  }
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    Assume.assumeTrue("PostgreSQL 8.3 does not support 'infinity' for 'date'",
+        TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_4));
+    super.setUp();
+    TestUtil.createTable(con, "table1", "timestamp_without_time_zone_column timestamp without time zone,"
+            + "timestamp_with_time_zone_column timestamp with time zone,"
+            + "date_column date"
+    );
+  }
+
+  @After
+  public void tearDown() throws SQLException {
+    TestUtil.dropTable(con, "table1");
+    super.tearDown();
+  }
+
+  @Test
+  public void testTimestamptz() throws SQLException {
+    runTestforType(OffsetDateTime.MAX, OffsetDateTime.MIN, "timestamp_without_time_zone_column", null);
+  }
+
+  @Test
+  public void testTimestamp() throws SQLException {
+    runTestforType(LocalDateTime.MAX, LocalDateTime.MIN, "timestamp_without_time_zone_column", null);
+  }
+
+  @Test
+  public void testDate() throws SQLException {
+    runTestforType(LocalDate.MAX, LocalDate.MIN, "date_column", null);
+  }
+
+  private void runTestforType(Object max, Object min, String columnName, Integer type) throws SQLException {
+    insert(max, columnName, type);
+    String readback = readString(columnName);
+    assertEquals("infinity", readback);
+    delete();
+
+    insert(min, columnName, type);
+    readback = readString(columnName);
+    assertEquals("-infinity", readback);
+    delete();
+  }
+
+  private void insert(Object data, String columnName, Integer type) throws SQLException {
+    PreparedStatement ps = con.prepareStatement(TestUtil.insertSQL("table1", columnName, "?"));
+    try {
+      if (type != null) {
+        ps.setObject(1, data, type);
+      } else {
+        ps.setObject(1, data);
+      }
+      assertEquals(1, ps.executeUpdate());
+    } finally {
+      ps.close();
+    }
+  }
+
+  private String readString(String columnName) throws SQLException {
+    Statement st = con.createStatement();
+    try {
+      ResultSet rs = st.executeQuery(TestUtil.selectSQL("table1", columnName));
+      try {
+        assertNotNull(rs);
+        assertTrue(rs.next());
+        return rs.getString(1);
+      } finally {
+        rs.close();
+      }
+    } finally {
+      st.close();
+    }
+  }
+
+  private void delete() throws SQLException {
+    Statement st = con.createStatement();
+    try {
+      st.execute("DELETE FROM table1");
+    } finally {
+      st.close();
+    }
+  }
+
+}
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/SetObject310Test.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/SetObject310Test.java
index 203132ce..0b87e4e0 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc42/SetObject310Test.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc42/SetObject310Test.java
@@ -260,6 +260,9 @@ public class SetObject310Test extends BaseTest4 {
             // This is a pre-1970 date, so check if it is rounded properly
             "1950-07-20T02:00:00",
 
+            // Ensure the calendar is proleptic
+            "1582-09-30T00:00:00", "1582-10-16T00:00:00",
+
             // On 2000-10-29 03:00:00 Moscow went to regular time, thus local time became 02:00:00
             "2000-10-29T01:59:59", "2000-10-29T02:00:00", "2000-10-29T02:00:01", "2000-10-29T02:59:59",
             "2000-10-29T03:00:00", "2000-10-29T03:00:01", "2000-10-29T03:59:59", "2000-10-29T04:00:00",
@@ -311,7 +314,7 @@ public class SetObject310Test extends BaseTest4 {
               "OffsetDateTime=" + data + " (with ZoneId=" + dataZone + "), with TimeZone.default="
                   + storeZone + ", setObject(int, Object)", data.toInstant(),
               noTypeRes.toInstant());
-          String withType = rs.getString(1);
+          String withType = rs.getString(2);
           OffsetDateTime withTypeRes = OffsetDateTime.parse(withType.replace(' ', 'T') + ":00");
           assertEquals(
               "OffsetDateTime=" + data + " (with ZoneId=" + dataZone + "), with TimeZone.default="
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jre8/core/Jre8TestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/jre8/core/Jre8TestSuite.java
index 96cee652..6c971a0b 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jre8/core/Jre8TestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jre8/core/Jre8TestSuite.java
@@ -13,6 +13,8 @@ import org.junit.runners.Suite;
  *         Twitter: @codefinger
  */
 @RunWith(Suite.class)
-@Suite.SuiteClasses({SocksProxyTest.class})
+@Suite.SuiteClasses({
+    SocksProxyTest.class,
+})
 public class Jre8TestSuite {
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/osgi/OsgiTestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/osgi/OsgiTestSuite.java
index a7c909dc..7402247e 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/osgi/OsgiTestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/osgi/OsgiTestSuite.java
@@ -10,7 +10,9 @@ import org.junit.runners.Suite;
 import org.junit.runners.Suite.SuiteClasses;
 
 @RunWith(Suite.class)
-@SuiteClasses({PGDataSourceFactoryTest.class})
+@SuiteClasses({
+    PGDataSourceFactoryTest.class,
+})
 public class OsgiTestSuite {
 
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/ssl/LazyKeyManagerTest.java b/pgjdbc/src/test/java/org/postgresql/test/ssl/LazyKeyManagerTest.java
new file mode 100644
index 00000000..1c1f1f0f
--- /dev/null
+++ b/pgjdbc/src/test/java/org/postgresql/test/ssl/LazyKeyManagerTest.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (c) 2019, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.ssl;
+
+import org.postgresql.ssl.LazyKeyManager;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.File;
+import java.io.IOException;
+import java.security.PrivateKey;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.PasswordCallback;
+import javax.security.auth.callback.UnsupportedCallbackException;
+
+public class LazyKeyManagerTest {
+
+  @Test
+  public void testLoadKey() throws Exception {
+    String certdir = "../certdir/";
+    String path = new File("./").getAbsolutePath();
+    LazyKeyManager lazyKeyManager = new LazyKeyManager(certdir + "goodclient.crt",
+        certdir + "goodclient.pk8", new TestCallbackHandler("sslpwd"), true);
+    PrivateKey pk = lazyKeyManager.getPrivateKey("user");
+    Assert.assertNotNull(pk);
+  }
+
+  public static class TestCallbackHandler implements CallbackHandler {
+    char [] password;
+
+    public TestCallbackHandler(String password) {
+      if (password != null) {
+        this.password = password.toCharArray();
+      }
+    }
+
+    @Override
+    public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
+      for (Callback callback : callbacks) {
+        if (!(callback instanceof PasswordCallback)) {
+          throw new UnsupportedCallbackException(callback);
+        }
+        PasswordCallback pwdCallback = (PasswordCallback) callback;
+        if (password != null) {
+          pwdCallback.setPassword(password);
+          continue;
+        }
+        // It is used instead of cons.readPassword(prompt), because the prompt may contain '%'
+        // characters
+        //pwdCallback.setPassword(cons.readPassword("%s", pwdCallback.getPrompt()));
+      }
+    }
+  }
+}
diff --git a/pgjdbc/src/test/java/org/postgresql/test/ssl/SslTestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/ssl/SslTestSuite.java
index cf48916f..95e439c4 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/ssl/SslTestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/ssl/SslTestSuite.java
@@ -10,9 +10,10 @@ import org.junit.runners.Suite;
 
 @RunWith(Suite.class)
 @Suite.SuiteClasses({
-    LibPQFactoryHostNameTest.class,
     CommonNameVerifierTest.class,
-    SslTest.class
+    LazyKeyManagerTest.class,
+    LibPQFactoryHostNameTest.class,
+    SslTest.class,
 })
 public class SslTestSuite {
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/xa/XATestSuite.java b/pgjdbc/src/test/java/org/postgresql/test/xa/XATestSuite.java
index 787ebc7d..d4d4cb7a 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/xa/XATestSuite.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/xa/XATestSuite.java
@@ -9,6 +9,8 @@ import org.junit.runner.RunWith;
 import org.junit.runners.Suite;
 
 @RunWith(Suite.class)
-@Suite.SuiteClasses(XADataSourceTest.class)
+@Suite.SuiteClasses({
+    XADataSourceTest.class,
+})
 public class XATestSuite {
 }