diff --git a/README.md b/README.md
index 3637764018f7ddc6b0d990463b5f0a329a4a87ba..b70a1ca6de3107ffe1e70a9a28cb1ca6d6c2212b 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.12-blue.svg)][mvn-jre8]
+[![Java 8](https://img.shields.io/badge/Java_8-42.2.14-blue.svg)][mvn-jre8]
 ```xml
 <dependency>
     <groupId>org.postgresql</groupId>
     <artifactId>postgresql</artifactId>
-    <version>42.2.12</version>
+    <version>42.2.14</version>
 </dependency>
 ```
 
-[![Java 7](https://img.shields.io/badge/Java_7-42.2.12.jre7-blue.svg)][mvn-jre7]
+[![Java 7](https://img.shields.io/badge/Java_7-42.2.14.jre7-blue.svg)][mvn-jre7]
 ```xml
 <dependency>
     <groupId>org.postgresql</groupId>
     <artifactId>postgresql</artifactId>
-    <version>42.2.12.jre7</version>
+    <version>42.2.14.jre7</version>
 </dependency>
 ```
 
-[![Java 6](https://img.shields.io/badge/Java_6-42.2.12.jre6-blue.svg)][mvn-jre6]
+[![Java 6](https://img.shields.io/badge/Java_6-42.2.14.jre6-blue.svg)][mvn-jre6]
 ```xml
 <dependency>
     <groupId>org.postgresql</groupId>
     <artifactId>postgresql</artifactId>
-    <version>42.2.12.jre6</version>
+    <version>42.2.14.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.12.jre6|bundle
-[mvn-jre7]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.12.jre7|bundle
-[mvn-jre8]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.12|bundle
+[mvn-jre6]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.14.jre6|bundle
+[mvn-jre7]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.14.jre7|bundle
+[mvn-jre8]: http://search.maven.org/#artifactdetails|org.postgresql|postgresql|42.2.14|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.13-SNAPSHOT</version> <!-- Java 8 -->
-  <version>42.2.13.jre7-SNAPSHOT</version> <!-- Java 7 -->
-  <version>42.2.13.jre6-SNAPSHOT</version> <!-- Java 6 -->
+  <version>42.2.15-SNAPSHOT</version> <!-- Java 8 -->
+  <version>42.2.15.jre7-SNAPSHOT</version> <!-- Java 7 -->
+  <version>42.2.15.jre6-SNAPSHOT</version> <!-- Java 6 -->
 </dependency>
 ```
 
@@ -114,7 +114,7 @@ In addition to the standard connection parameters the driver supports a number o
 | ssl                           | Boolean | false   | Control use of SSL (true value causes SSL to be required) |
 | sslfactory                    | String  | null    | Provide a SSLSocketFactory class when using SSL. |
 | sslfactoryarg (deprecated)    | String  | null    | Argument forwarded to constructor of SSLSocketFactory class. |
-| sslmode                       | String  | null    | Parameter governing the use of SSL. |
+| sslmode                       | String  | prefer  | Controls the preference for opening using an SSL encrypted connection. |
 | sslcert                       | String  | null    | The location of the client's SSL certificate |
 | sslkey                        | String  | null    | The location of the client's PKCS#8 SSL key |
 | sslrootcert                   | String  | null    | The location of the root certificate for authenticating the server. |
@@ -150,9 +150,10 @@ In addition to the standard connection parameters the driver supports a number o
 | autosave                      | String  | never   | Specifies what the driver should do if a query fails, possible values: always, never, conservative |
 | cleanupSavepoints             | Boolean | false   | In Autosave mode the driver sets a SAVEPOINT for every query. It is possible to exhaust the server shared buffers. Setting this to true will release each SAVEPOINT at the cost of an additional round trip. |
 | preferQueryMode               | String  | extended | Specifies which mode is used to execute queries to database, possible values: extended, extendedForPrepared, extendedCacheEverything, simple |
-| reWriteBatchedInserts         | Boolean | false  | Enable optimization to rewrite and collapse compatible INSERT statements that are batched. |
-| escapeSyntaxCallMode          | String  | select   | Specifies how JDBC escape call syntax is transformed into underlying SQL (CALL/SELECT), for invoking procedures or functions (requires server version >= 11), possible values: select, callIfNoReturn, call |
+| reWriteBatchedInserts         | Boolean | false   | Enable optimization to rewrite and collapse compatible INSERT statements that are batched. |
+| escapeSyntaxCallMode          | String  | select  | Specifies how JDBC escape call syntax is transformed into underlying SQL (CALL/SELECT), for invoking procedures or functions (requires server version >= 11), possible values: select, callIfNoReturn, call |
 | maxResultBuffer               | String  | null    | Specifies size of result buffer in bytes, which can't be exceeded during reading result set. Can be specified as particular size (i.e. "100", "200M" "2G") or as percent of max heap memory (i.e. "10p", "20pct", "50percent") |
+| gssEncMode                    | String  | prefer  | Controls the preference for using GSSAPI encryption for the connection,  values are disable, allow, prefer, and require |
 
 ## Contributing
 For information on how to contribute to the project see the [Contributing Guidelines](CONTRIBUTING.md)
diff --git a/build.properties b/build.properties
index a62afc7f9c01a63dcbd5180d7e7621db1091baf6..ae2658b6d4f8cce707d9ee241c215ab4428ff4e4 100644
--- a/build.properties
+++ b/build.properties
@@ -14,6 +14,7 @@ username=test
 password=test
 privilegedUser=postgres
 privilegedPassword=
+gssEncMode=disable
 sspiusername=testsspi
 preparethreshold=5
 loggerLevel=OFF
diff --git a/pom.xml b/pom.xml
index d70c65dda6a3221ce654ba6c19a6d1efdf3d7366..3b24a13a3b076db440f38a8a72249df1955cffa5 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
     <artifactId>postgresql</artifactId>
     <packaging>jar</packaging>
     <name>PostgreSQL JDBC Driver - JDBC 4.2</name>
-    <version>42.2.14</version>
+    <version>42.2.15</version>
     <description>Java JDBC 4.2 (JRE 8+) driver for PostgreSQL database</description>
     <url>https://github.com/pgjdbc/pgjdbc</url>
 
@@ -129,4 +129,70 @@
             </plugin>
         </plugins>
     </build>
+
+    <profiles>
+        <!--
+          By default, all the dependencies are shaded, however, -DskipShadeDependencies=true
+          can be added to avoid bundling the dependencies.
+          -->
+        <profile>
+            <id>shade-dependencies</id>
+            <activation>
+                <property>
+                    <name>!skipShadeDependencies</name>
+                </property>
+            </activation>
+            <build>
+                <plugins>
+                    <plugin>
+                        <groupId>org.apache.maven.plugins</groupId>
+                        <artifactId>maven-shade-plugin</artifactId>
+                        <version>3.1.0</version>
+                        <configuration>
+                            <minimizeJar>true</minimizeJar>
+                            <artifactSet>
+                                <excludes>
+                                    <exclude>com.github.waffle:waffle-jna</exclude>
+                                    <exclude>org.slf4j:jcl-over-slf4j</exclude>
+                                </excludes>
+                            </artifactSet>
+                            <filters>
+                                <filter>
+                                    <artifact>com.ongres.scram:client</artifact>
+                                    <excludes>
+                                        <exclude>LICENSE</exclude>
+                                        <exclude>META-INF/maven/**</exclude>
+                                    </excludes>
+                                </filter>
+                                <filter>
+                                    <artifact>*:*</artifact>
+                                    <excludes>
+                                        <exclude>com/sun/jna/**</exclude>
+                                        <exclude>LICENSE</exclude>
+                                        <exclude>META-INF/maven/**</exclude>
+                                    </excludes>
+                                </filter>
+                            </filters>
+                        </configuration>
+                        <executions>
+                            <execution>
+                                <phase>package</phase>
+                                <goals>
+                                    <goal>shade</goal>
+                                </goals>
+                                <configuration>
+                                    <relocations>
+                                        <relocation>
+                                            <pattern>com.ongres</pattern>
+                                            <shadedPattern>org.postgresql.shaded.com.ongres</shadedPattern>
+                                        </relocation>
+                                    </relocations>
+                                </configuration>
+                            </execution>
+                        </executions>
+                    </plugin>
+                </plugins>
+            </build>
+        </profile>
+    </profiles>
 </project>
diff --git a/src/main/java/org/postgresql/Driver.java b/src/main/java/org/postgresql/Driver.java
index 2e82f1da5d53460d0e4c9b9dae038ef81f0cefd3..678c7a31f0d5cc1072c8d4dd22ef0d076f2ac9e9 100644
--- a/src/main/java/org/postgresql/Driver.java
+++ b/src/main/java/org/postgresql/Driver.java
@@ -5,16 +5,20 @@
 
 package org.postgresql;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.jdbc.PgConnection;
 import org.postgresql.util.DriverInfo;
 import org.postgresql.util.ExpressionProperties;
 import org.postgresql.util.GT;
 import org.postgresql.util.HostSpec;
+import org.postgresql.util.LogWriterHandler;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 import org.postgresql.util.SharedTimer;
 import org.postgresql.util.URLCoder;
-import org.postgresql.util.WriterHandler;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
 
 import java.io.IOException;
 import java.io.InputStream;
@@ -57,7 +61,7 @@ import java.util.logging.StreamHandler;
  */
 public class Driver implements java.sql.Driver {
 
-  private static Driver registeredDriver;
+  private static /* @Nullable */ Driver registeredDriver;
   private static final Logger PARENT_LOGGER = Logger.getLogger("org.postgresql");
   private static final Logger LOGGER = Logger.getLogger("org.postgresql.Driver");
   private static final SharedTimer SHARED_TIMER = new SharedTimer();
@@ -77,7 +81,7 @@ public class Driver implements java.sql.Driver {
 
   // Helper to retrieve default properties from classloader resource
   // properties files.
-  private Properties defaultProperties;
+  private /* @Nullable */ Properties defaultProperties;
 
   private synchronized Properties getDefaultProperties() throws IOException {
     if (defaultProperties != null) {
@@ -205,7 +209,7 @@ public class Driver implements java.sql.Driver {
    * @see java.sql.Driver#connect
    */
   @Override
-  public Connection connect(String url, Properties info) throws SQLException {
+  public /* @Nullable */ Connection connect(String url, /* @Nullable */ Properties info) throws SQLException {
     if (url == null) {
       throw new SQLException("url is null");
     }
@@ -285,7 +289,7 @@ public class Driver implements java.sql.Driver {
   }
 
   // Used to check if the handler file is the same
-  private static String loggerHandlerFile;
+  private static /* @Nullable */ String loggerHandlerFile;
 
   /**
    * <p>Setup java.util.logging.Logger using connection properties.</p>
@@ -335,7 +339,7 @@ public class Driver implements java.sql.Driver {
 
     if ( handler == null ) {
       if (DriverManager.getLogWriter() != null) {
-        handler = new WriterHandler(DriverManager.getLogWriter());
+        handler = new LogWriterHandler(DriverManager.getLogWriter());
       } else if ( DriverManager.getLogStream() != null) {
         handler = new StreamHandler(DriverManager.getLogStream(), formatter);
       } else {
@@ -345,7 +349,10 @@ public class Driver implements java.sql.Driver {
       handler.setFormatter(formatter);
     }
 
-    handler.setLevel(PARENT_LOGGER.getLevel());
+    Level loggerLevel = PARENT_LOGGER.getLevel();
+    if (loggerLevel != null) {
+      handler.setLevel(loggerLevel);
+    }
     PARENT_LOGGER.setUseParentHandlers(false);
     PARENT_LOGGER.addHandler(handler);
   }
@@ -440,8 +447,8 @@ public class Driver implements java.sql.Driver {
 
     private final String url;
     private final Properties props;
-    private Connection result;
-    private Throwable resultException;
+    private /* @Nullable */ Connection result;
+    private /* @Nullable */ Throwable resultException;
     private boolean abandoned;
   }
 
@@ -542,7 +549,7 @@ public class Driver implements java.sql.Driver {
    * @param defaults Default properties
    * @return Properties with elements added from the url
    */
-  public static Properties parseURL(String url, Properties defaults) {
+  public static /* @Nullable */ Properties parseURL(String url, /* @Nullable */ Properties defaults) {
     Properties urlProps = new Properties(defaults);
 
     String urlServer = url;
@@ -636,8 +643,8 @@ public class Driver implements java.sql.Driver {
    * @return the address portion of the URL
    */
   private static HostSpec[] hostSpecs(Properties props) {
-    String[] hosts = props.getProperty("PGHOST").split(",");
-    String[] ports = props.getProperty("PGPORT").split(",");
+    String[] hosts = castNonNull(props.getProperty("PGHOST")).split(",");
+    String[] ports = castNonNull(props.getProperty("PGPORT")).split(",");
     HostSpec[] hostSpecs = new HostSpec[hosts.length];
     for (int i = 0; i < hostSpecs.length; ++i) {
       hostSpecs[i] = new HostSpec(hosts[i], Integer.parseInt(ports[i]));
@@ -730,7 +737,7 @@ public class Driver implements java.sql.Driver {
    * @throws SQLException if deregistering the driver fails
    */
   public static void deregister() throws SQLException {
-    if (!isRegistered()) {
+    if (registeredDriver == null) {
       throw new IllegalStateException(
           "Driver is not registered (or it has not been registered using Driver.register() method)");
     }
diff --git a/src/main/java/org/postgresql/PGConnection.java b/src/main/java/org/postgresql/PGConnection.java
index 6f700c526ad5b0350a6734a654b68da6bf7c6fab..54a0e28a67c0d9cdd3fb0a5b5b5129495c3a93d6 100644
--- a/src/main/java/org/postgresql/PGConnection.java
+++ b/src/main/java/org/postgresql/PGConnection.java
@@ -13,6 +13,8 @@ import org.postgresql.largeobject.LargeObjectManager;
 import org.postgresql.replication.PGReplicationConnection;
 import org.postgresql.util.PGobject;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.Array;
 import java.sql.SQLException;
 import java.sql.Statement;
@@ -40,7 +42,7 @@ public interface PGConnection {
    *           If for some reason the array cannot be created.
    * @see java.sql.Connection#createArrayOf(String, Object[])
    */
-  Array createArrayOf(String typeName, Object elements) throws SQLException;
+  Array createArrayOf(String typeName, /* @Nullable */ Object elements) throws SQLException;
 
   /**
    * This method returns any notifications that have been received since the last call to this
@@ -312,5 +314,5 @@ public interface PGConnection {
    * @see #getParameterStatuses
    * @since 42.2.6
    */
-  String getParameterStatus(String parameterName);
+  /* @Nullable */ String getParameterStatus(String parameterName);
 }
diff --git a/src/main/java/org/postgresql/PGProperty.java b/src/main/java/org/postgresql/PGProperty.java
index f94b38789673d812bac03a17a8c1d356b465c2f2..053adbd77e1d3f3f4e00a1712d91c045f32b206a 100644
--- a/src/main/java/org/postgresql/PGProperty.java
+++ b/src/main/java/org/postgresql/PGProperty.java
@@ -10,6 +10,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.Connection;
 import java.sql.DriverPropertyInfo;
 import java.util.HashMap;
@@ -180,6 +182,14 @@ public enum PGProperty {
     false,
     new String[] {"select", "callIfNoReturn", "call"}),
 
+  GSS_ENC_MODE(
+      "gssEncMode",
+      "prefer",
+      "Force Encoded GSS Mode",
+      false,
+      new String[] {"disable", "prefer", "require"}
+  ),
+
   /**
    * Force one of
    * <ul>
@@ -675,21 +685,22 @@ public enum PGProperty {
   ;
 
   private final String name;
-  private final String defaultValue;
+  private final /* @Nullable */ String defaultValue;
   private final boolean required;
   private final String description;
-  private final String[] choices;
+  private final String /* @Nullable */ [] choices;
   private final boolean deprecated;
 
-  PGProperty(String name, String defaultValue, String description) {
+  PGProperty(String name, /* @Nullable */ String defaultValue, String description) {
     this(name, defaultValue, description, false);
   }
 
-  PGProperty(String name, String defaultValue, String description, boolean required) {
+  PGProperty(String name, /* @Nullable */ String defaultValue, String description, boolean required) {
     this(name, defaultValue, description, required, (String[]) null);
   }
 
-  PGProperty(String name, String defaultValue, String description, boolean required, String[] choices) {
+  PGProperty(String name, /* @Nullable */ String defaultValue, String description, boolean required,
+      String /* @Nullable */ [] choices) {
     this.name = name;
     this.defaultValue = defaultValue;
     this.required = required;
@@ -726,7 +737,7 @@ public enum PGProperty {
    *
    * @return the default value for this connection parameter or null
    */
-  public String getDefaultValue() {
+  public /* @Nullable */ String getDefaultValue() {
     return defaultValue;
   }
 
@@ -753,7 +764,7 @@ public enum PGProperty {
    *
    * @return the available values for this connection parameter or null
    */
-  public String[] getChoices() {
+  public String /* @Nullable */ [] getChoices() {
     return choices;
   }
 
@@ -773,7 +784,7 @@ public enum PGProperty {
    * @param properties properties to take actual value from
    * @return evaluated value for this connection parameter
    */
-  public String get(Properties properties) {
+  public /* @Nullable */ String get(Properties properties) {
     return properties.getProperty(name, defaultValue);
   }
 
@@ -783,7 +794,7 @@ public enum PGProperty {
    * @param properties properties in which the value should be set
    * @param value value for this connection parameter
    */
-  public void set(Properties properties, String value) {
+  public void set(Properties properties, /* @Nullable */ String value) {
     if (value == null) {
       properties.remove(name);
     } else {
@@ -798,7 +809,7 @@ public enum PGProperty {
    * @return evaluated value for this connection parameter converted to boolean
    */
   public boolean getBoolean(Properties properties) {
-    return Boolean.valueOf(get(properties));
+    return Boolean.parseBoolean(get(properties));
   }
 
   /**
@@ -809,8 +820,10 @@ public enum PGProperty {
    * @return evaluated value for this connection parameter converted to int
    * @throws NumberFormatException if it cannot be converted to int.
    */
+  @SuppressWarnings("nullness:argument.type.incompatible")
   public int getIntNoCheck(Properties properties) {
     String value = get(properties);
+    //noinspection ConstantConditions
     return Integer.parseInt(value);
   }
 
@@ -821,9 +834,11 @@ public enum PGProperty {
    * @return evaluated value for this connection parameter converted to int
    * @throws PSQLException if it cannot be converted to int.
    */
+  @SuppressWarnings("nullness:argument.type.incompatible")
   public int getInt(Properties properties) throws PSQLException {
     String value = get(properties);
     try {
+      //noinspection ConstantConditions
       return Integer.parseInt(value);
     } catch (NumberFormatException nfe) {
       throw new PSQLException(GT.tr("{0} parameter value must be an integer but was: {1}",
@@ -838,7 +853,7 @@ public enum PGProperty {
    * @return evaluated value for this connection parameter converted to Integer or null
    * @throws PSQLException if unable to parse property as integer
    */
-  public Integer getInteger(Properties properties) throws PSQLException {
+  public /* @Nullable */ Integer getInteger(Properties properties) throws PSQLException {
     String value = get(properties);
     if (value == null) {
       return null;
@@ -896,7 +911,7 @@ public enum PGProperty {
     return propertyInfo;
   }
 
-  public static PGProperty forName(String name) {
+  public static /* @Nullable */ PGProperty forName(String name) {
     return PROPS_BY_NAME.get(name);
   }
 
@@ -907,7 +922,7 @@ public enum PGProperty {
    * @param properties properties bundle
    * @return the value of a set property
    */
-  public String getSetString(Properties properties) {
+  public /* @Nullable */ String getSetString(Properties properties) {
     Object o = properties.get(name);
     if (o instanceof String) {
       return (String) o;
diff --git a/src/main/java/org/postgresql/PGRefCursorResultSet.java b/src/main/java/org/postgresql/PGRefCursorResultSet.java
index 8fc678b9f7f4c490759afac1986aa5bf702aadb0..bff52cf33841705abd9be07cbadc822607ad3eb3 100644
--- a/src/main/java/org/postgresql/PGRefCursorResultSet.java
+++ b/src/main/java/org/postgresql/PGRefCursorResultSet.java
@@ -5,6 +5,8 @@
 
 package org.postgresql;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * A ref cursor based result set.
  *
@@ -21,5 +23,5 @@ public interface PGRefCursorResultSet {
    *             was obtained from.
    */
   @Deprecated
-  String getRefCursor();
+  /* @Nullable */ String getRefCursor();
 }
diff --git a/src/main/java/org/postgresql/copy/CopyOut.java b/src/main/java/org/postgresql/copy/CopyOut.java
index f503ef36791f13d3ad9c460d638e964046224991..d661904d55bbb287cb04a378ec7dc7228655372a 100644
--- a/src/main/java/org/postgresql/copy/CopyOut.java
+++ b/src/main/java/org/postgresql/copy/CopyOut.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.copy;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 
 public interface CopyOut extends CopyOperation {
@@ -14,7 +16,7 @@ public interface CopyOut extends CopyOperation {
    * @return byte array received from server, null if server complete copy operation
    * @throws SQLException if something goes wrong for example socket timeout
    */
-  byte[] readFromCopy() throws SQLException;
+  byte /* @Nullable */ [] readFromCopy() throws SQLException;
 
   /**
    * Wait for a row of data to be received from server on an active copy operation.
@@ -25,5 +27,5 @@ public interface CopyOut extends CopyOperation {
    *         blocking mode return null
    * @throws SQLException if something goes wrong for example socket timeout
    */
-  byte[] readFromCopy(boolean block) throws SQLException;
+  byte /* @Nullable */ [] readFromCopy(boolean block) throws SQLException;
 }
diff --git a/src/main/java/org/postgresql/copy/PGCopyInputStream.java b/src/main/java/org/postgresql/copy/PGCopyInputStream.java
index 134cabea429a7571bec2b6cb1c5bb00d79d85e53..7ea9383706fd8683642fe6e0e9e39fc6b564f80e 100644
--- a/src/main/java/org/postgresql/copy/PGCopyInputStream.java
+++ b/src/main/java/org/postgresql/copy/PGCopyInputStream.java
@@ -5,21 +5,26 @@
 
 package org.postgresql.copy;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGConnection;
 import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.sql.SQLException;
+import java.util.Arrays;
 
 /**
  * InputStream for reading from a PostgreSQL COPY TO STDOUT operation.
  */
 public class PGCopyInputStream extends InputStream implements CopyOut {
-  private CopyOut op;
-  private byte[] buf;
+  private /* @Nullable */ CopyOut op;
+  private byte /* @Nullable */ [] buf;
   private int at;
   private int len;
 
@@ -43,23 +48,25 @@ public class PGCopyInputStream extends InputStream implements CopyOut {
     this.op = op;
   }
 
-  private boolean gotBuf() throws IOException {
+  private CopyOut getOp() {
+    return castNonNull(op);
+  }
+
+  private byte /* @Nullable */ [] fillBuffer() throws IOException {
     if (at >= len) {
       try {
-        buf = op.readFromCopy();
+        buf = getOp().readFromCopy();
       } catch (SQLException sqle) {
-        throw new IOException(GT.tr("Copying from database failed: {0}", sqle));
+        throw new IOException(GT.tr("Copying from database failed: {0}", sqle.getMessage()), sqle);
       }
       if (buf == null) {
         at = -1;
-        return false;
       } else {
         at = 0;
         len = buf.length;
-        return true;
       }
     }
-    return buf != null;
+    return buf;
   }
 
   private void checkClosed() throws IOException {
@@ -75,7 +82,8 @@ public class PGCopyInputStream extends InputStream implements CopyOut {
 
   public int read() throws IOException {
     checkClosed();
-    return gotBuf() ? (buf[at++] & 0xFF)  : -1;
+    byte[] buf = fillBuffer();
+    return buf != null ? (buf[at++] & 0xFF)  : -1;
   }
 
   public int read(byte[] buf) throws IOException {
@@ -85,34 +93,37 @@ public class PGCopyInputStream extends InputStream implements CopyOut {
   public int read(byte[] buf, int off, int siz) throws IOException {
     checkClosed();
     int got = 0;
-    boolean didReadSomething = false;
-    while (got < siz && (didReadSomething = gotBuf())) {
-      buf[off + got++] = this.buf[at++];
+    byte[] data = fillBuffer();
+    for (; got < siz && data != null; data = fillBuffer()) {
+      int length = Math.min(siz - got, len - at);
+      System.arraycopy(data, at, buf, off + got, length);
+      at += length;
+      got += length;
     }
-    return got == 0 && !didReadSomething ? -1 : got;
+    return got == 0 && data == null ? -1 : got;
   }
 
-  public byte[] readFromCopy() throws SQLException {
+  public byte /* @Nullable */ [] readFromCopy() throws SQLException {
     byte[] result = buf;
     try {
-      if (gotBuf()) {
+      byte[] buf = fillBuffer();
+      if (buf != null) {
         if (at > 0 || len < buf.length) {
-          byte[] ba = new byte[len - at];
-          for (int i = at; i < len; i++) {
-            ba[i - at] = buf[i];
-          }
-          result = ba;
+          result = Arrays.copyOfRange(buf, at, len);
+        } else {
+          result = buf;
         }
-        at = len; // either partly or fully returned, buffer is exhausted
+        // Mark the buffer as fully read
+        at = len;
       }
     } catch (IOException ioe) {
-      throw new PSQLException(GT.tr("Read from copy failed."), PSQLState.CONNECTION_FAILURE);
+      throw new PSQLException(GT.tr("Read from copy failed."), PSQLState.CONNECTION_FAILURE, ioe);
     }
     return result;
   }
 
   @Override
-  public byte[] readFromCopy(boolean block) throws SQLException {
+  public byte /* @Nullable */ [] readFromCopy(boolean block) throws SQLException {
     return readFromCopy();
   }
 
@@ -126,28 +137,26 @@ public class PGCopyInputStream extends InputStream implements CopyOut {
       try {
         op.cancelCopy();
       } catch (SQLException se) {
-        IOException ioe = new IOException("Failed to close copy reader.");
-        ioe.initCause(se);
-        throw ioe;
+        throw new IOException("Failed to close copy reader.", se);
       }
     }
     op = null;
   }
 
   public void cancelCopy() throws SQLException {
-    op.cancelCopy();
+    getOp().cancelCopy();
   }
 
   public int getFormat() {
-    return op.getFormat();
+    return getOp().getFormat();
   }
 
   public int getFieldFormat(int field) {
-    return op.getFieldFormat(field);
+    return getOp().getFieldFormat(field);
   }
 
   public int getFieldCount() {
-    return op.getFieldCount();
+    return getOp().getFieldCount();
   }
 
   public boolean isActive() {
@@ -155,6 +164,6 @@ public class PGCopyInputStream extends InputStream implements CopyOut {
   }
 
   public long getHandledRowCount() {
-    return op.getHandledRowCount();
+    return getOp().getHandledRowCount();
   }
 }
diff --git a/src/main/java/org/postgresql/copy/PGCopyOutputStream.java b/src/main/java/org/postgresql/copy/PGCopyOutputStream.java
index 84692ba74715f2a36384f249001c35c3f029185f..b1bed6300bc8b44d9832fc1f48302b4a508821f1 100644
--- a/src/main/java/org/postgresql/copy/PGCopyOutputStream.java
+++ b/src/main/java/org/postgresql/copy/PGCopyOutputStream.java
@@ -5,10 +5,14 @@
 
 package org.postgresql.copy;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGConnection;
 import org.postgresql.util.ByteStreamWriter;
 import org.postgresql.util.GT;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.sql.SQLException;
@@ -17,7 +21,7 @@ import java.sql.SQLException;
  * OutputStream for buffered input into a PostgreSQL COPY FROM STDIN operation.
  */
 public class PGCopyOutputStream extends OutputStream implements CopyIn {
-  private CopyIn op;
+  private /* @Nullable */ CopyIn op;
   private final byte[] copyBuffer;
   private final byte[] singleByteBuffer = new byte[1];
   private int at = 0;
@@ -66,6 +70,10 @@ public class PGCopyOutputStream extends OutputStream implements CopyIn {
     copyBuffer = new byte[bufferSize];
   }
 
+  private CopyIn getOp() {
+    return castNonNull(op);
+  }
+
   public void write(int b) throws IOException {
     checkClosed();
     if (b < 0 || b > 255) {
@@ -84,9 +92,7 @@ public class PGCopyOutputStream extends OutputStream implements CopyIn {
     try {
       writeToCopy(buf, off, siz);
     } catch (SQLException se) {
-      IOException ioe = new IOException("Write to copy failed.");
-      ioe.initCause(se);
-      throw ioe;
+      throw new IOException("Write to copy failed.", se);
     }
   }
 
@@ -102,13 +108,11 @@ public class PGCopyOutputStream extends OutputStream implements CopyIn {
       return;
     }
 
-    if (op.isActive()) {
+    if (getOp().isActive()) {
       try {
         endCopy();
       } catch (SQLException se) {
-        IOException ioe = new IOException("Ending write to copy failed.");
-        ioe.initCause(se);
-        throw ioe;
+        throw new IOException("Ending write to copy failed.", se);
       }
     }
     op = null;
@@ -117,24 +121,22 @@ public class PGCopyOutputStream extends OutputStream implements CopyIn {
   public void flush() throws IOException {
     checkClosed();
     try {
-      op.writeToCopy(copyBuffer, 0, at);
+      getOp().writeToCopy(copyBuffer, 0, at);
       at = 0;
-      op.flushCopy();
+      getOp().flushCopy();
     } catch (SQLException e) {
-      IOException ioe = new IOException("Unable to flush stream");
-      ioe.initCause(e);
-      throw ioe;
+      throw new IOException("Unable to flush stream", e);
     }
   }
 
   public void writeToCopy(byte[] buf, int off, int siz) throws SQLException {
     if (at > 0
         && siz > copyBuffer.length - at) { // would not fit into rest of our buf, so flush buf
-      op.writeToCopy(copyBuffer, 0, at);
+      getOp().writeToCopy(copyBuffer, 0, at);
       at = 0;
     }
     if (siz > copyBuffer.length) { // would still not fit into buf, so just pass it through
-      op.writeToCopy(buf, off, siz);
+      getOp().writeToCopy(buf, off, siz);
     } else { // fits into our buf, so save it there
       System.arraycopy(buf, off, copyBuffer, at, siz);
       at += siz;
@@ -142,43 +144,43 @@ public class PGCopyOutputStream extends OutputStream implements CopyIn {
   }
 
   public void writeToCopy(ByteStreamWriter from) throws SQLException {
-    op.writeToCopy(from);
+    getOp().writeToCopy(from);
   }
 
   public int getFormat() {
-    return op.getFormat();
+    return getOp().getFormat();
   }
 
   public int getFieldFormat(int field) {
-    return op.getFieldFormat(field);
+    return getOp().getFieldFormat(field);
   }
 
   public void cancelCopy() throws SQLException {
-    op.cancelCopy();
+    getOp().cancelCopy();
   }
 
   public int getFieldCount() {
-    return op.getFieldCount();
+    return getOp().getFieldCount();
   }
 
   public boolean isActive() {
-    return op != null && op.isActive();
+    return op != null && getOp().isActive();
   }
 
   public void flushCopy() throws SQLException {
-    op.flushCopy();
+    getOp().flushCopy();
   }
 
   public long endCopy() throws SQLException {
     if (at > 0) {
-      op.writeToCopy(copyBuffer, 0, at);
+      getOp().writeToCopy(copyBuffer, 0, at);
     }
-    op.endCopy();
+    getOp().endCopy();
     return getHandledRowCount();
   }
 
   public long getHandledRowCount() {
-    return op.getHandledRowCount();
+    return getOp().getHandledRowCount();
   }
 
 }
diff --git a/src/main/java/org/postgresql/core/BaseConnection.java b/src/main/java/org/postgresql/core/BaseConnection.java
index 56fb4b871d76f51373ea06210fb24a21d87b33ab..280c2b201f0c00a07377179ecc5df2a680341109 100644
--- a/src/main/java/org/postgresql/core/BaseConnection.java
+++ b/src/main/java/org/postgresql/core/BaseConnection.java
@@ -12,6 +12,10 @@ import org.postgresql.jdbc.TimestampUtils;
 import org.postgresql.util.LruCache;
 import org.postgresql.xml.PGXmlFactoryFactory;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.checker.nullness.qual.PolyNull;
+// import org.checkerframework.dataflow.qual.Pure;
+
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -73,14 +77,17 @@ public interface BaseConnection extends PGConnection, Connection {
    * <p>If no class is registered as handling the given type, then a generic
    * {@link org.postgresql.util.PGobject} instance is returned.</p>
    *
+   * <p>value or byteValue must be non-null</p>
    * @param type the backend typename
    * @param value the type-specific string representation of the value
    * @param byteValue the type-specific binary representation of the value
    * @return an appropriate object; never null.
    * @throws SQLException if something goes wrong
    */
-  Object getObject(String type, String value, byte[] byteValue) throws SQLException;
+  Object getObject(String type, /* @Nullable */ String value, byte /* @Nullable */ [] byteValue)
+      throws SQLException;
 
+  /* @Pure */
   Encoding getEncoding() throws SQLException;
 
   TypeInfo getTypeInfo();
@@ -116,7 +123,7 @@ public interface BaseConnection extends PGConnection, Connection {
    * @return an encoded representation of the string
    * @throws SQLException if something goes wrong.
    */
-  byte[] encodeString(String str) throws SQLException;
+  byte /* @PolyNull */ [] encodeString(/* @PolyNull */ String str) throws SQLException;
 
   /**
    * Escapes a string for use as string-literal within an SQL command. The method chooses the
diff --git a/src/main/java/org/postgresql/core/BaseQueryKey.java b/src/main/java/org/postgresql/core/BaseQueryKey.java
index d9d4aea95f0e725e9e339566d01c7421682ce89e..487182370a45874ff0f885c344b8471bb6bcbe6e 100644
--- a/src/main/java/org/postgresql/core/BaseQueryKey.java
+++ b/src/main/java/org/postgresql/core/BaseQueryKey.java
@@ -7,6 +7,8 @@ package org.postgresql.core;
 
 import org.postgresql.util.CanEstimateSize;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * This class is used as a cache key for simple statements that have no "returning columns".
  * Prepared statements that have no returning columns use just {@code String sql} as a key.
@@ -42,7 +44,7 @@ class BaseQueryKey implements CanEstimateSize {
   }
 
   @Override
-  public boolean equals(Object o) {
+  public boolean equals(/* @Nullable */ Object o) {
     if (this == o) {
       return true;
     }
diff --git a/src/main/java/org/postgresql/core/CachedQueryCreateAction.java b/src/main/java/org/postgresql/core/CachedQueryCreateAction.java
index d5ad5c7031570711935d12def93e025d0223af87..4881f0d8fee2f3d37685e3356221f0e0183f23bb 100644
--- a/src/main/java/org/postgresql/core/CachedQueryCreateAction.java
+++ b/src/main/java/org/postgresql/core/CachedQueryCreateAction.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.jdbc.PreferQueryMode;
 import org.postgresql.util.LruCache;
 
@@ -26,7 +28,7 @@ class CachedQueryCreateAction implements LruCache.CreateAction<Object, CachedQue
   public CachedQuery create(Object key) throws SQLException {
     assert key instanceof String || key instanceof BaseQueryKey
         : "Query key should be String or BaseQueryKey. Given " + key.getClass() + ", sql: "
-        + String.valueOf(key);
+        + key;
     BaseQueryKey queryKey;
     String parsedSql;
     if (key instanceof BaseQueryKey) {
@@ -36,7 +38,7 @@ class CachedQueryCreateAction implements LruCache.CreateAction<Object, CachedQue
       queryKey = null;
       parsedSql = (String) key;
     }
-    if (key instanceof String || queryKey.escapeProcessing) {
+    if (key instanceof String || castNonNull(queryKey).escapeProcessing) {
       parsedSql =
           Parser.replaceProcessing(parsedSql, true, queryExecutor.getStandardConformingStrings());
     }
@@ -50,7 +52,7 @@ class CachedQueryCreateAction implements LruCache.CreateAction<Object, CachedQue
     } else {
       isFunction = false;
     }
-    boolean isParameterized = key instanceof String || queryKey.isParameterized;
+    boolean isParameterized = key instanceof String || castNonNull(queryKey).isParameterized;
     boolean splitStatements = isParameterized || queryExecutor.getPreferQueryMode().compareTo(PreferQueryMode.EXTENDED) >= 0;
 
     String[] returningColumns;
diff --git a/src/main/java/org/postgresql/core/CallableQueryKey.java b/src/main/java/org/postgresql/core/CallableQueryKey.java
index d65ab181c0b5d591f050e4f90a648f23d102d83b..5a5e5d53015c7cd4f8cb7c497bedc4b14cb092ea 100644
--- a/src/main/java/org/postgresql/core/CallableQueryKey.java
+++ b/src/main/java/org/postgresql/core/CallableQueryKey.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Serves as a cache key for {@link java.sql.CallableStatement}.
  * Callable statements require some special parsing before use (due to JDBC {@code {?= call...}}
@@ -31,7 +33,7 @@ class CallableQueryKey extends BaseQueryKey {
   }
 
   @Override
-  public boolean equals(Object o) {
+  public boolean equals(/* @Nullable */ Object o) {
     // Nothing interesting here, overriding equals to make hashCode and equals paired
     return super.equals(o);
   }
diff --git a/src/main/java/org/postgresql/core/CommandCompleteParser.java b/src/main/java/org/postgresql/core/CommandCompleteParser.java
index a4e52b31b7bbe043926d3bad6af7ed196be3476a..b315f787cd1d9fae906dffc2beba6c26e2d3fd38 100644
--- a/src/main/java/org/postgresql/core/CommandCompleteParser.java
+++ b/src/main/java/org/postgresql/core/CommandCompleteParser.java
@@ -9,6 +9,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Parses {@code oid} and {@code rows} from a {@code CommandComplete (B)} message (end of Execute).
  */
@@ -83,7 +85,7 @@ public final class CommandCompleteParser {
   }
 
   @Override
-  public boolean equals(Object o) {
+  public boolean equals(/* @Nullable */ Object o) {
     if (this == o) {
       return true;
     }
diff --git a/src/main/java/org/postgresql/core/ConnectionFactory.java b/src/main/java/org/postgresql/core/ConnectionFactory.java
index 7e2399a74cd6b4bd6436c410810e32c36b345ed4..72686d66098dceff3ad7c9d20f5194d70162663c 100644
--- a/src/main/java/org/postgresql/core/ConnectionFactory.java
+++ b/src/main/java/org/postgresql/core/ConnectionFactory.java
@@ -13,6 +13,8 @@ import org.postgresql.util.HostSpec;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.sql.SQLException;
 import java.util.Properties;
@@ -81,7 +83,7 @@ public abstract class ConnectionFactory {
    *
    * @param newStream The stream to close.
    */
-  protected void closeStream(PGStream newStream) {
+  protected void closeStream(/* @Nullable */ PGStream newStream) {
     if (newStream != null) {
       try {
         newStream.close();
diff --git a/src/main/java/org/postgresql/core/Encoding.java b/src/main/java/org/postgresql/core/Encoding.java
index 5d56c5567b7c158d4ef08718a2becd55de7e5dfb..e777c24f469b1796bb8cac9066a13ebbfe29e080 100644
--- a/src/main/java/org/postgresql/core/Encoding.java
+++ b/src/main/java/org/postgresql/core/Encoding.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.PolyNull;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -219,7 +221,7 @@ public class Encoding {
    * @return a bytearray containing the encoded string
    * @throws IOException if something goes wrong
    */
-  public byte[] encode(String s) throws IOException {
+  public byte /* @PolyNull */ [] encode(/* @PolyNull */ String s) throws IOException {
     if (s == null) {
       return null;
     }
diff --git a/src/main/java/org/postgresql/core/EncodingPredictor.java b/src/main/java/org/postgresql/core/EncodingPredictor.java
index a720d88c80fc9f4f38cbb20639795d9271f407b3..608a57ecf3391a7a66a4235efff51ffa1e1abe65 100644
--- a/src/main/java/org/postgresql/core/EncodingPredictor.java
+++ b/src/main/java/org/postgresql/core/EncodingPredictor.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 
 /**
@@ -24,21 +26,22 @@ public class EncodingPredictor {
    */
   public static class DecodeResult {
     public final String result;
-    public final String encoding; // JVM name
+    public final /* @Nullable */ String encoding; // JVM name
 
-    DecodeResult(String result, String encoding) {
+    DecodeResult(String result, /* @Nullable */ String encoding) {
       this.result = result;
       this.encoding = encoding;
     }
   }
 
   static class Translation {
-    public final String fatalText;
-    private final String[] texts;
+    public final /* @Nullable */ String fatalText;
+    private final String /* @Nullable */ [] texts;
     public final String language;
     public final String[] encodings;
 
-    Translation(String fatalText, String[] texts, String language, String... encodings) {
+    Translation(/* @Nullable */ String fatalText, String /* @Nullable */ [] texts,
+        String language, String... encodings) {
       this.fatalText = fatalText;
       this.texts = texts;
       this.language = language;
@@ -58,7 +61,7 @@ public class EncodingPredictor {
               "LATIN7", "LATIN9"),
       };
 
-  public static DecodeResult decode(byte[] bytes, int offset, int length) {
+  public static /* @Nullable */ DecodeResult decode(byte[] bytes, int offset, int length) {
     Encoding defaultEncoding = Encoding.defaultEncoding();
     for (Translation tr : FATAL_TRANSLATIONS) {
       for (String encoding : tr.encodings) {
diff --git a/src/main/java/org/postgresql/core/Field.java b/src/main/java/org/postgresql/core/Field.java
index 513ea5747a45f17df93c9ccb4a1e0e84afb1f85b..4bd6c75cb0468b25d471103dc31eaea3919f32b4 100644
--- a/src/main/java/org/postgresql/core/Field.java
+++ b/src/main/java/org/postgresql/core/Field.java
@@ -7,6 +7,9 @@ package org.postgresql.core;
 
 import org.postgresql.jdbc.FieldMetadata;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.dataflow.qual.Pure;
+
 /*
  */
 public class Field {
@@ -30,7 +33,7 @@ public class Field {
 
   // Cache fields filled in by AbstractJdbc2ResultSetMetaData.fetchFieldMetaData.
   // Don't use unless that has been called.
-  private FieldMetadata metadata;
+  private /* @Nullable */ FieldMetadata metadata;
 
   private int sqlType;
   private String pgType = NOT_YET_LOADED;
@@ -83,6 +86,7 @@ public class Field {
   /**
    * @return the oid of this Field's data type
    */
+  /* @Pure */
   public int getOID() {
     return oid;
   }
@@ -133,7 +137,7 @@ public class Field {
     return positionInTable;
   }
 
-  public FieldMetadata getMetadata() {
+  public /* @Nullable */ FieldMetadata getMetadata() {
     return metadata;
   }
 
diff --git a/src/main/java/org/postgresql/core/NativeQuery.java b/src/main/java/org/postgresql/core/NativeQuery.java
index 6bb1bcef9290aeab0c5a2a2ede00382c6f86703a..f6112d2cd7e73480ca5e523a2b624554283f8b2c 100644
--- a/src/main/java/org/postgresql/core/NativeQuery.java
+++ b/src/main/java/org/postgresql/core/NativeQuery.java
@@ -6,6 +6,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Represents a query that is ready for execution by backend. The main difference from JDBC is ? are
  * replaced with $1, $2, etc.
@@ -45,7 +47,7 @@ public class NativeQuery {
    *        method, or {@code null} to leave the parameter placeholders unsubstituted.
    * @return a human-readable representation of this query
    */
-  public String toString(ParameterList parameters) {
+  public String toString(/* @Nullable */ ParameterList parameters) {
     if (bindPositions.length == 0) {
       return nativeSql;
     }
diff --git a/src/main/java/org/postgresql/core/PGStream.java b/src/main/java/org/postgresql/core/PGStream.java
index 1637bfae031c99e07e39169a1c904bc832e46d9f..b883ca9048ddd54c68fc472c94d4e5fe8e658781 100644
--- a/src/main/java/org/postgresql/core/PGStream.java
+++ b/src/main/java/org/postgresql/core/PGStream.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core;
 
+import org.postgresql.gss.GSSInputStream;
+import org.postgresql.gss.GSSOutputStream;
 import org.postgresql.util.ByteStreamWriter;
 import org.postgresql.util.GT;
 import org.postgresql.util.HostSpec;
@@ -12,6 +14,10 @@ import org.postgresql.util.PGPropertyMaxResultBufferParser;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.MessageProp;
+
 import java.io.BufferedOutputStream;
 import java.io.Closeable;
 import java.io.EOFException;
@@ -23,6 +29,7 @@ import java.io.OutputStream;
 import java.io.Writer;
 import java.net.InetSocketAddress;
 import java.net.Socket;
+import java.net.SocketException;
 import java.net.SocketTimeoutException;
 import java.sql.SQLException;
 
@@ -45,7 +52,21 @@ public class PGStream implements Closeable, Flushable {
   private Socket connection;
   private VisibleBufferedInputStream pgInput;
   private OutputStream pgOutput;
-  private byte[] streamBuffer;
+  private byte /* @Nullable */ [] streamBuffer;
+
+  public boolean isGssEncrypted() {
+    return gssEncrypted;
+  }
+
+  boolean gssEncrypted = false;
+
+  public void setSecContext(GSSContext secContext) {
+    MessageProp messageProp =  new MessageProp(0, true);
+    pgInput = new VisibleBufferedInputStream(new GSSInputStream(pgInput.getWrapped(), secContext, messageProp ), 8192);
+    pgOutput = new GSSOutputStream(pgOutput, secContext, messageProp, 16384);
+    gssEncrypted = true;
+
+  }
 
   private long nextStreamAvailableCheckTime;
   // This is a workaround for SSL sockets: sslInputStream.available() might return 0
@@ -66,20 +87,12 @@ public class PGStream implements Closeable, Flushable {
    * @param timeout timeout in milliseconds, or 0 if no timeout set
    * @throws IOException if an IOException occurs below it.
    */
+  @SuppressWarnings({"method.invocation.invalid", "initialization.fields.uninitialized"})
   public PGStream(SocketFactory socketFactory, HostSpec hostSpec, int timeout) throws IOException {
     this.socketFactory = socketFactory;
     this.hostSpec = hostSpec;
 
-    Socket socket = socketFactory.createSocket();
-    if (!socket.isConnected()) {
-      // When using a SOCKS proxy, the host might not be resolvable locally,
-      // thus we defer resolution until the traffic reaches the proxy. If there
-      // is no proxy, we must resolve the host to an IP to connect the socket.
-      InetSocketAddress address = hostSpec.shouldResolve()
-          ? new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort())
-          : InetSocketAddress.createUnresolved(hostSpec.getHost(), hostSpec.getPort());
-      socket.connect(address, timeout);
-    }
+    Socket socket = createSocket(timeout);
     changeSocket(socket);
     setEncoding(Encoding.getJVMEncoding("UTF-8"));
 
@@ -87,6 +100,48 @@ public class PGStream implements Closeable, Flushable {
     int4Buf = new byte[4];
   }
 
+  @SuppressWarnings({"method.invocation.invalid", "initialization.fields.uninitialized"})
+  public PGStream(PGStream pgStream, int timeout ) throws IOException {
+
+    /*
+    Some defaults
+     */
+    int sendBufferSize = 1024;
+    int receiveBufferSize = 1024;
+    int soTimeout = 0;
+    boolean keepAlive = false;
+
+    /*
+    Get the existing values before closing the stream
+     */
+    try {
+      sendBufferSize = pgStream.getSocket().getSendBufferSize();
+      receiveBufferSize = pgStream.getSocket().getReceiveBufferSize();
+      soTimeout = pgStream.getSocket().getSoTimeout();
+      keepAlive = pgStream.getSocket().getKeepAlive();
+
+    } catch ( SocketException ex ) {
+      // ignore it
+    }
+    //close the existing stream
+    pgStream.close();
+
+    this.socketFactory = pgStream.socketFactory;
+    this.hostSpec = pgStream.hostSpec;
+
+    Socket socket = createSocket(timeout);
+
+    // set the buffer sizes and timeout
+    socket.setReceiveBufferSize(receiveBufferSize);
+    socket.setSendBufferSize(sendBufferSize);
+    setNetworkTimeout(soTimeout);
+    socket.setKeepAlive(keepAlive);
+
+    int2Buf = new byte[2];
+    int4Buf = new byte[4];
+
+  }
+
   /**
    * Constructor: Connect to the PostgreSQL back end and return a stream connection.
    *
@@ -163,6 +218,23 @@ public class PGStream implements Closeable, Flushable {
     this.minStreamAvailableCheckDelay = delay;
   }
 
+  private Socket createSocket(int timeout) throws IOException {
+
+    Socket socket = socketFactory.createSocket();
+    if (!socket.isConnected()) {
+      // When using a SOCKS proxy, the host might not be resolvable locally,
+      // thus we defer resolution until the traffic reaches the proxy. If there
+      // is no proxy, we must resolve the host to an IP to connect the socket.
+      InetSocketAddress address = hostSpec.shouldResolve()
+          ? new InetSocketAddress(hostSpec.getHost(), hostSpec.getPort())
+          : InetSocketAddress.createUnresolved(hostSpec.getHost(), hostSpec.getPort());
+      socket.connect(address, timeout);
+    }
+    changeSocket(socket);
+    setEncoding(Encoding.getJVMEncoding("UTF-8"));
+    return socket;
+  }
+
   /**
    * Switch this stream to using a new socket. Any existing socket is <em>not</em> closed; it's
    * assumed that we are changing to a new socket that delegates to the original socket (e.g. SSL).
@@ -274,7 +346,6 @@ public class PGStream implements Closeable, Flushable {
     if (val < Short.MIN_VALUE || val > Short.MAX_VALUE) {
       throw new IOException("Tried to send an out-of-range integer as a 2-byte value: " + val);
     }
-
     int2Buf[0] = (byte) (val >>> 8);
     int2Buf[1] = (byte) val;
     pgOutput.write(int2Buf);
@@ -639,7 +710,7 @@ public class PGStream implements Closeable, Flushable {
    *              multiplier)
    * @throws PSQLException exception returned when occurred parsing problem.
    */
-  public void setMaxResultBuffer(String value) throws PSQLException {
+  public void setMaxResultBuffer(/* @Nullable */ String value) throws PSQLException {
     maxResultBuffer = PGPropertyMaxResultBufferParser.parseProperty(value);
   }
 
diff --git a/src/main/java/org/postgresql/core/ParameterList.java b/src/main/java/org/postgresql/core/ParameterList.java
index 9c25868a7fd7cb918f987e9794ac83ae88271e8b..b5fcca54839a951099b4d177bd039ec762875dbd 100644
--- a/src/main/java/org/postgresql/core/ParameterList.java
+++ b/src/main/java/org/postgresql/core/ParameterList.java
@@ -8,6 +8,10 @@ package org.postgresql.core;
 
 import org.postgresql.util.ByteStreamWriter;
 
+// import org.checkerframework.checker.index.qual.NonNegative;
+// import org.checkerframework.checker.index.qual.Positive;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.InputStream;
 import java.sql.SQLException;
 
@@ -25,7 +29,7 @@ import java.sql.SQLException;
  * @author Oliver Jowett (oliver@opencloud.com)
  */
 public interface ParameterList {
-  void registerOutParameter(int index, int sqlType) throws SQLException;
+  void registerOutParameter(/* @Positive */ int index, int sqlType) throws SQLException;
 
   /**
    * Get the number of parameters in this list. This value never changes for a particular instance,
@@ -33,21 +37,21 @@ public interface ParameterList {
    *
    * @return the number of parameters in this list.
    */
-  int getParameterCount();
+  /* @NonNegative */ int getParameterCount();
 
   /**
    * Get the number of IN parameters in this list.
    *
    * @return the number of IN parameters in this list
    */
-  int getInParameterCount();
+  /* @NonNegative */ int getInParameterCount();
 
   /**
    * Get the number of OUT parameters in this list.
    *
    * @return the number of OUT parameters in this list
    */
-  int getOutParameterCount();
+  /* @NonNegative */ int getOutParameterCount();
 
   /**
    * Return the oids of the parameters in this list. May be null for a ParameterList that does not
@@ -64,7 +68,7 @@ public interface ParameterList {
    * @param value the integer value to use.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setIntParameter(int index, int value) throws SQLException;
+  void setIntParameter(/* @Positive */ int index, int value) throws SQLException;
 
   /**
    * Binds a String value that is an unquoted literal to the server's query parser (for example, a
@@ -76,7 +80,8 @@ public interface ParameterList {
    * @param oid the type OID of the parameter, or <code>0</code> to infer the type.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setLiteralParameter(int index, String value, int oid) throws SQLException;
+  void setLiteralParameter(/* @Positive */ int index,
+      String value, int oid) throws SQLException;
 
   /**
    * Binds a String value that needs to be quoted for the server's parser to understand (for
@@ -88,7 +93,7 @@ public interface ParameterList {
    * @param oid the type OID of the parameter, or <code>0</code> to infer the type.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setStringParameter(int index, String value, int oid) throws SQLException;
+  void setStringParameter(/* @Positive */ int index, String value, int oid) throws SQLException;
 
   /**
    * Binds a binary bytea value stored as a bytearray to a parameter. The parameter's type is
@@ -101,7 +106,8 @@ public interface ParameterList {
    * @param length the number of bytes of parameter data within <code>data</code> to use.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setBytea(int index, byte[] data, int offset, int length) throws SQLException;
+  void setBytea(/* @Positive */ int index, byte[] data,
+      /* @NonNegative */ int offset, /* @NonNegative */ int length) throws SQLException;
 
   /**
    * Binds a binary bytea value stored as an InputStream. The parameter's type is implicitly set to
@@ -112,7 +118,7 @@ public interface ParameterList {
    * @param length the number of bytes of parameter data to read from <code>stream</code>.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setBytea(int index, InputStream stream, int length) throws SQLException;
+  void setBytea(/* @Positive */ int index, InputStream stream, /* @NonNegative */ int length) throws SQLException;
 
   /**
    * Binds a binary bytea value stored as an InputStream. The parameter's type is implicitly set to
@@ -122,7 +128,7 @@ public interface ParameterList {
    * @param stream a stream containing the parameter data.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setBytea(int index, InputStream stream) throws SQLException;
+  void setBytea(/* @Positive */ int index, InputStream stream) throws SQLException;
 
   /**
    * Binds a binary bytea value stored as a ByteStreamWriter. The parameter's type is implicitly set to
@@ -132,7 +138,7 @@ public interface ParameterList {
    * @param writer a writer that can write the bytes for the parameter
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setBytea(int index, ByteStreamWriter writer) throws SQLException;
+  void setBytea(/* @Positive */ int index, ByteStreamWriter writer) throws SQLException;
 
   /**
    * Binds a text value stored as an InputStream that is a valid UTF-8 byte stream.
@@ -144,7 +150,7 @@ public interface ParameterList {
    * @param stream a stream containing the parameter data.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setText(int index, InputStream stream) throws SQLException;
+  void setText(/* @Positive */ int index, InputStream stream) throws SQLException;
 
   /**
    * Binds given byte[] value to a parameter. The bytes must already be in correct format matching
@@ -155,7 +161,7 @@ public interface ParameterList {
    * @param oid the type OID of the parameter.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setBinaryParameter(int index, byte[] value, int oid) throws SQLException;
+  void setBinaryParameter(/* @Positive */ int index, byte[] value, int oid) throws SQLException;
 
   /**
    * Binds a SQL NULL value to a parameter. Associated with the parameter is a typename for the
@@ -165,7 +171,7 @@ public interface ParameterList {
    * @param oid the type OID of the parameter, or <code>0</code> to infer the type.
    * @throws SQLException on error or if <code>index</code> is out of range
    */
-  void setNull(int index, int oid) throws SQLException;
+  void setNull(/* @Positive */ int index, int oid) throws SQLException;
 
   /**
    * Perform a shallow copy of this ParameterList, returning a new instance (still suitable for
@@ -189,7 +195,7 @@ public interface ParameterList {
    * @param standardConformingStrings true if \ is not an escape character in strings literals
    * @return a string representation of the parameter.
    */
-  String toString(int index, boolean standardConformingStrings);
+  String toString(/* @Positive */ int index, boolean standardConformingStrings);
 
   /**
    * Use this operation to append more parameters to the current list.
@@ -202,5 +208,5 @@ public interface ParameterList {
    * Returns the bound parameter values.
    * @return Object array containing the parameter values.
    */
-  Object[] getValues();
+  /* @Nullable */ Object /* @Nullable */ [] getValues();
 }
diff --git a/src/main/java/org/postgresql/core/Parser.java b/src/main/java/org/postgresql/core/Parser.java
index 628e0c7f4e791eb6226d67ba388a5c241002f469..939e5d2f89c40e6ad9d53d2b90243f4560ce84ce 100644
--- a/src/main/java/org/postgresql/core/Parser.java
+++ b/src/main/java/org/postgresql/core/Parser.java
@@ -11,6 +11,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.sql.SQLException;
@@ -298,7 +300,7 @@ public class Parser {
     return nativeQueries;
   }
 
-  private static SqlCommandType parseWithCommandType(char[] aChars, int i, int keywordStart,
+  private static /* @Nullable */ SqlCommandType parseWithCommandType(char[] aChars, int i, int keywordStart,
       int wordLength) {
     // This parses `with x as (...) ...`
     // Corner case is `with select as (insert ..) select * from select
@@ -373,7 +375,7 @@ public class Parser {
    * @param list input list
    * @return output array
    */
-  private static int[] toIntArray(List<Integer> list) {
+  private static int[] toIntArray(/* @Nullable */ List<Integer> list) {
     if (list == null || list.isEmpty()) {
       return NO_BINDS;
     }
@@ -1363,7 +1365,8 @@ public class Parser {
       if (targetException instanceof SQLException) {
         throw (SQLException) targetException;
       } else {
-        throw new PSQLException(targetException.getMessage(), PSQLState.SYSTEM_ERROR);
+        String message = targetException == null ? "no message" : targetException.getMessage();
+        throw new PSQLException(message, PSQLState.SYSTEM_ERROR);
       }
     } catch (IllegalAccessException e) {
       throw new PSQLException(e.getMessage(), PSQLState.SYSTEM_ERROR);
@@ -1390,13 +1393,14 @@ public class Parser {
 
     private final char[] escapeKeyword;
     private final char[] allowedValues;
-    private final String replacementKeyword;
+    private final /* @Nullable */ String replacementKeyword;
 
     SqlParseState() {
       this("", new char[0], null);
     }
 
-    SqlParseState(String escapeKeyword, char[] allowedValues, String replacementKeyword) {
+    SqlParseState(String escapeKeyword, char[] allowedValues,
+        /* @Nullable */ String replacementKeyword) {
       this.escapeKeyword = escapeKeyword.toCharArray();
       this.allowedValues = allowedValues;
       this.replacementKeyword = replacementKeyword;
diff --git a/src/main/java/org/postgresql/core/Query.java b/src/main/java/org/postgresql/core/Query.java
index b54a034843ad5eff556c809c441e0a208dc06095..a6c8befd08b72be0d66a6c452a90eb01501e75ae 100644
--- a/src/main/java/org/postgresql/core/Query.java
+++ b/src/main/java/org/postgresql/core/Query.java
@@ -6,6 +6,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.Map;
 
 /**
@@ -37,7 +39,7 @@ public interface Query {
    *        or <code>null</code> to leave the parameter placeholders unsubstituted.
    * @return a human-readable representation of this query
    */
-  String toString(ParameterList parameters);
+  String toString(/* @Nullable */ ParameterList parameters);
 
   /**
    * Returns SQL in native for database format.
@@ -49,7 +51,7 @@ public interface Query {
    * Returns properties of the query (sql keyword, and some other parsing info).
    * @return returns properties of the query (sql keyword, and some other parsing info) or null if not applicable
    */
-  SqlCommand getSqlCommand();
+  /* @Nullable */ SqlCommand getSqlCommand();
 
   /**
    * <p>Close this query and free any server-side resources associated with it. The resources may not
@@ -74,7 +76,7 @@ public interface Query {
    *
    * @return null if the query implementation does not support this method.
    */
-  Map<String, Integer> getResultSetColumnNameIndexMap();
+  /* @Nullable */ Map<String, Integer> getResultSetColumnNameIndexMap();
 
   /**
    * Return a list of the Query objects that make up this query. If this object is already a
@@ -83,5 +85,5 @@ public interface Query {
    * @return an array of single-statement queries, or <code>null</code> if this object is already a
    *         single-statement query.
    */
-  Query[] getSubqueries();
+  Query /* @Nullable */ [] getSubqueries();
 }
diff --git a/src/main/java/org/postgresql/core/QueryExecutor.java b/src/main/java/org/postgresql/core/QueryExecutor.java
index 6df042d7c4d43a9e10543985de0110c347409e01..b1b19165ab79b7d12203e77626a89b91c57f20eb 100644
--- a/src/main/java/org/postgresql/core/QueryExecutor.java
+++ b/src/main/java/org/postgresql/core/QueryExecutor.java
@@ -15,6 +15,8 @@ import org.postgresql.jdbc.EscapeSyntaxCallMode;
 import org.postgresql.jdbc.PreferQueryMode;
 import org.postgresql.util.HostSpec;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.sql.SQLException;
 import java.sql.SQLWarning;
@@ -142,7 +144,7 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
    * @param flags a combination of QUERY_* flags indicating how to handle the query.
    * @throws SQLException if query execution fails
    */
-  void execute(Query query, ParameterList parameters, ResultHandler handler, int maxRows,
+  void execute(Query query, /* @Nullable */ ParameterList parameters, ResultHandler handler, int maxRows,
       int fetchSize, int flags) throws SQLException;
 
   /**
@@ -153,7 +155,7 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
    * @param parameterLists the parameter lists for the queries. The parameter lists correspond 1:1
    *        to the queries passed in the <code>queries</code> array. Each must be non-
    *        <code>null</code> if the corresponding query takes parameters, and must be a parameter
-   *        object returned by {@link org.postgresql.core.Query#createParameterList()} created by
+   *        object returned by {@link Query#createParameterList()} created by
    *        the corresponding query.
    * @param handler a ResultHandler responsible for handling results generated by this query
    * @param maxRows the maximum number of rows to retrieve
@@ -162,7 +164,8 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
    * @param flags a combination of QUERY_* flags indicating how to handle the query.
    * @throws SQLException if query execution fails
    */
-  void execute(Query[] queries, ParameterList[] parameterLists, BatchResultHandler handler, int maxRows,
+  void execute(Query[] queries, /* @Nullable */ ParameterList[] parameterLists,
+      BatchResultHandler handler, int maxRows,
       int fetchSize, int flags) throws SQLException;
 
   /**
@@ -190,11 +193,11 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
   boolean isReWriteBatchedInsertsEnabled();
 
   CachedQuery createQuery(String sql, boolean escapeProcessing, boolean isParameterized,
-      String... columnNames)
+      String /* @Nullable */ ... columnNames)
       throws SQLException;
 
   Object createQueryKey(String sql, boolean escapeProcessing, boolean isParameterized,
-      String... columnNames);
+      String /* @Nullable */ ... columnNames);
 
   CachedQuery createQueryByKey(Object key) throws SQLException;
 
@@ -204,7 +207,7 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
 
   CachedQuery borrowCallableQuery(String sql) throws SQLException;
 
-  CachedQuery borrowReturningQuery(String sql, String[] columnNames) throws SQLException;
+  CachedQuery borrowReturningQuery(String sql, String /* @Nullable */ [] columnNames) throws SQLException;
 
   void releaseQuery(CachedQuery cachedQuery);
 
@@ -270,7 +273,8 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
    *         and results substitutes for a fast-path function call.
    */
   @Deprecated
-  byte[] fastpathCall(int fnid, ParameterList params, boolean suppressBegin) throws SQLException;
+  byte /* @Nullable */ [] fastpathCall(int fnid, ParameterList params, boolean suppressBegin)
+      throws SQLException;
 
   /**
    * Issues a COPY FROM STDIN / COPY TO STDOUT statement and returns handler for associated
@@ -384,7 +388,7 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
    * @return the first SQLWarning in the chain; subsequent warnings can be found via
    *         SQLWarning.getNextWarning().
    */
-  SQLWarning getWarnings();
+  /* @Nullable */ SQLWarning getWarnings();
 
   /**
    * <p>Get a machine-readable server version.</p>
@@ -419,7 +423,7 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
    * Returns backend timezone in java format.
    * @return backend timezone in java format.
    */
-  TimeZone getTimeZone();
+  /* @Nullable */ TimeZone getTimeZone();
 
   /**
    * @return the current encoding in use by this connection
@@ -465,5 +469,5 @@ public interface QueryExecutor extends TypeTransferModeRegistry {
   // Expose parameter status to PGConnection
   Map<String,String> getParameterStatuses();
 
-  String getParameterStatus(String parameterName);
+  /* @Nullable */ String getParameterStatus(String parameterName);
 }
diff --git a/src/main/java/org/postgresql/core/QueryExecutorBase.java b/src/main/java/org/postgresql/core/QueryExecutorBase.java
index 82d636c4fd5818267b1aaee88d956cfca26a7ded..23bc229d18602c08cd5f3c32071e47f73e540bbc 100644
--- a/src/main/java/org/postgresql/core/QueryExecutorBase.java
+++ b/src/main/java/org/postgresql/core/QueryExecutorBase.java
@@ -16,6 +16,9 @@ import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 import org.postgresql.util.ServerErrorMessage;
 
+// import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.sql.SQLException;
 import java.sql.SQLWarning;
@@ -38,9 +41,9 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   private int cancelPid;
   private int cancelKey;
   private boolean closed = false;
-  private String serverVersion;
+  private /* @MonotonicNonNull */ String serverVersion;
   private int serverVersionNum = 0;
-  private TransactionState transactionState;
+  private TransactionState transactionState = TransactionState.IDLE;
   private final boolean reWriteBatchedInserts;
   private final boolean columnSanitiserDisabled;
   private final EscapeSyntaxCallMode escapeSyntaxCallMode;
@@ -52,7 +55,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   // default value for server versions that don't report standard_conforming_strings
   private boolean standardConformingStrings = false;
 
-  private SQLWarning warnings;
+  private /* @Nullable */ SQLWarning warnings;
   private final ArrayList<PGNotification> notifications = new ArrayList<PGNotification>();
 
   private final LruCache<Object, CachedQuery> statementCache;
@@ -62,6 +65,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   private final TreeMap<String,String> parameterStatuses
       = new TreeMap<String,String>(String.CASE_INSENSITIVE_ORDER);
 
+  @SuppressWarnings({"assignment.type.incompatible", "argument.type.incompatible"})
   protected QueryExecutorBase(PGStream pgStream, String user,
       String database, int cancelSignalTimeout, Properties info) throws SQLException {
     this.pgStream = pgStream;
@@ -76,6 +80,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
     this.preferQueryMode = PreferQueryMode.of(preferMode);
     this.autoSave = AutoSave.of(PGProperty.AUTOSAVE.get(info));
     this.logServerErrorDetail = PGProperty.LOG_SERVER_ERROR_DETAIL.getBoolean(info);
+    // assignment.type.incompatible, argument.type.incompatible
     this.cachedQueryCreateAction = new CachedQueryCreateAction(this);
     statementCache = new LruCache<Object, CachedQuery>(
         Math.max(0, PGProperty.PREPARED_STATEMENT_CACHE_QUERIES.getInt(info)),
@@ -220,7 +225,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   }
 
   @Override
-  public synchronized SQLWarning getWarnings() {
+  public synchronized /* @Nullable */ SQLWarning getWarnings() {
     SQLWarning chain = warnings;
     warnings = null;
     return chain;
@@ -228,6 +233,10 @@ public abstract class QueryExecutorBase implements QueryExecutor {
 
   @Override
   public String getServerVersion() {
+    String serverVersion = this.serverVersion;
+    if (serverVersion == null) {
+      throw new IllegalStateException("serverVersion must not be null");
+    }
     return serverVersion;
   }
 
@@ -236,7 +245,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
     if (serverVersionNum != 0) {
       return serverVersionNum;
     }
-    return serverVersionNum = Utils.parseServerVersionStr(serverVersion);
+    return serverVersionNum = Utils.parseServerVersionStr(getServerVersion());
   }
 
   public void setServerVersion(String serverVersion) {
@@ -290,7 +299,8 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   }
 
   @Override
-  public final CachedQuery borrowReturningQuery(String sql, String[] columnNames) throws SQLException {
+  public final CachedQuery borrowReturningQuery(String sql, String /* @Nullable */ [] columnNames)
+      throws SQLException {
     return statementCache.borrow(new QueryWithReturningColumnsKey(sql, true, true,
         columnNames
     ));
@@ -308,7 +318,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
 
   @Override
   public final Object createQueryKey(String sql, boolean escapeProcessing,
-      boolean isParameterized, String... columnNames) {
+      boolean isParameterized, String /* @Nullable */ ... columnNames) {
     Object key;
     if (columnNames == null || columnNames.length != 0) {
       // Null means "return whatever sensible columns are" (e.g. primary key, or serial, or something like that)
@@ -329,7 +339,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
 
   @Override
   public final CachedQuery createQuery(String sql, boolean escapeProcessing,
-      boolean isParameterized, String... columnNames)
+      boolean isParameterized, String /* @Nullable */ ... columnNames)
       throws SQLException {
     Object key = createQueryKey(sql, escapeProcessing, isParameterized, columnNames);
     // Note: cache is not reused here for two reasons:
@@ -385,7 +395,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
       return false;
     }
     // "cached plan must not change result type"
-    String routine = pe.getServerErrorMessage().getRoutine();
+    String routine = serverErrorMessage.getRoutine();
     return "RevalidateCachedQuery".equals(routine) // 9.2+
         || "RevalidateCachedPlan".equals(routine); // <= 9.1
   }
@@ -418,7 +428,7 @@ public abstract class QueryExecutorBase implements QueryExecutor {
   }
 
   @Override
-  public final String getParameterStatus(String parameterName) {
+  public final /* @Nullable */ String getParameterStatus(String parameterName) {
     return parameterStatuses.get(parameterName);
   }
 
diff --git a/src/main/java/org/postgresql/core/QueryWithReturningColumnsKey.java b/src/main/java/org/postgresql/core/QueryWithReturningColumnsKey.java
index 04bcb56e7cd67b437d4b9e2657d6a38bcdcfeaae..c383b84e259b033b38d6696c0ec6b9570d4fefa2 100644
--- a/src/main/java/org/postgresql/core/QueryWithReturningColumnsKey.java
+++ b/src/main/java/org/postgresql/core/QueryWithReturningColumnsKey.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.Arrays;
 
 /**
@@ -19,7 +21,7 @@ class QueryWithReturningColumnsKey extends BaseQueryKey {
   private int size; // query length cannot exceed MAX_INT
 
   QueryWithReturningColumnsKey(String sql, boolean isParameterized, boolean escapeProcessing,
-      String[] columnNames) {
+      String /* @Nullable */ [] columnNames) {
     super(sql, isParameterized, escapeProcessing);
     if (columnNames == null) {
       // TODO: teach parser to fetch key columns somehow when no column names were given
@@ -56,7 +58,7 @@ class QueryWithReturningColumnsKey extends BaseQueryKey {
   }
 
   @Override
-  public boolean equals(Object o) {
+  public boolean equals(/* @Nullable */ Object o) {
     if (this == o) {
       return true;
     }
diff --git a/src/main/java/org/postgresql/core/ResultHandler.java b/src/main/java/org/postgresql/core/ResultHandler.java
index 121898eda4ccb31930c8c49594fffc65eba8efa6..e9a7b9389dd41c6747fea9605cce048e1ad77ecd 100644
--- a/src/main/java/org/postgresql/core/ResultHandler.java
+++ b/src/main/java/org/postgresql/core/ResultHandler.java
@@ -6,6 +6,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.util.List;
@@ -36,7 +38,8 @@ public interface ResultHandler {
    * @param cursor a cursor to use to fetch additional data; <code>null</code> if no further results
    *        are present.
    */
-  void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples, ResultCursor cursor);
+  void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
+      /* @Nullable */ ResultCursor cursor);
 
   /**
    * Called when a query that did not return a resultset completes.
@@ -84,11 +87,11 @@ public interface ResultHandler {
    * Returns the first encountered exception. The rest are chained via {@link SQLException#setNextException(SQLException)}
    * @return the first encountered exception
    */
-  SQLException getException();
+  /* @Nullable */ SQLException getException();
 
   /**
    * Returns the first encountered warning. The rest are chained via {@link SQLException#setNextException(SQLException)}
    * @return the first encountered warning
    */
-  SQLWarning getWarning();
+  /* @Nullable */ SQLWarning getWarning();
 }
diff --git a/src/main/java/org/postgresql/core/ResultHandlerBase.java b/src/main/java/org/postgresql/core/ResultHandlerBase.java
index 85dab434a850829a692579f8c166c93c1619d26e..28c1de0fed49d9ad015342506239b11d03791f95 100644
--- a/src/main/java/org/postgresql/core/ResultHandlerBase.java
+++ b/src/main/java/org/postgresql/core/ResultHandlerBase.java
@@ -6,6 +6,10 @@
 
 package org.postgresql.core;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.util.List;
@@ -18,15 +22,15 @@ import java.util.List;
 public class ResultHandlerBase implements ResultHandler {
   // Last exception is tracked to avoid O(N) SQLException#setNextException just in case there
   // will be lots of exceptions (e.g. all batch rows fail with constraint violation or so)
-  private SQLException firstException;
-  private SQLException lastException;
+  private /* @Nullable */ SQLException firstException;
+  private /* @Nullable */ SQLException lastException;
 
-  private SQLWarning firstWarning;
-  private SQLWarning lastWarning;
+  private /* @Nullable */ SQLWarning firstWarning;
+  private /* @Nullable */ SQLWarning lastWarning;
 
   @Override
   public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
-      ResultCursor cursor) {
+      /* @Nullable */ ResultCursor cursor) {
   }
 
   @Override
@@ -43,8 +47,9 @@ public class ResultHandlerBase implements ResultHandler {
       firstWarning = lastWarning = warning;
       return;
     }
+    SQLWarning lastWarning = castNonNull(this.lastWarning);
     lastWarning.setNextException(warning);
-    lastWarning = warning;
+    this.lastWarning = warning;
   }
 
   @Override
@@ -53,24 +58,25 @@ public class ResultHandlerBase implements ResultHandler {
       firstException = lastException = error;
       return;
     }
-    lastException.setNextException(error);
-    lastException = error;
+    castNonNull(lastException).setNextException(error);
+    this.lastException = error;
   }
 
   @Override
   public void handleCompletion() throws SQLException {
+    SQLException firstException = this.firstException;
     if (firstException != null) {
       throw firstException;
     }
   }
 
   @Override
-  public SQLException getException() {
+  public /* @Nullable */ SQLException getException() {
     return firstException;
   }
 
   @Override
-  public SQLWarning getWarning() {
+  public /* @Nullable */ SQLWarning getWarning() {
     return firstWarning;
   }
 }
diff --git a/src/main/java/org/postgresql/core/ResultHandlerDelegate.java b/src/main/java/org/postgresql/core/ResultHandlerDelegate.java
index 456ce1e71c515858a7950fecda6f2d6006744de4..18f82b1e456bfd661dcfab1e17d237624674063d 100644
--- a/src/main/java/org/postgresql/core/ResultHandlerDelegate.java
+++ b/src/main/java/org/postgresql/core/ResultHandlerDelegate.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.util.List;
@@ -16,15 +18,15 @@ import java.util.List;
  * for the interface methods</p>
  */
 public class ResultHandlerDelegate implements ResultHandler {
-  private final ResultHandler delegate;
+  private final /* @Nullable */ ResultHandler delegate;
 
-  public ResultHandlerDelegate(ResultHandler delegate) {
+  public ResultHandlerDelegate(/* @Nullable */ ResultHandler delegate) {
     this.delegate = delegate;
   }
 
   @Override
   public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
-      ResultCursor cursor) {
+      /* @Nullable */ ResultCursor cursor) {
     if (delegate != null) {
       delegate.handleResultRows(fromQuery, fields, tuples, cursor);
     }
@@ -66,7 +68,7 @@ public class ResultHandlerDelegate implements ResultHandler {
   }
 
   @Override
-  public SQLException getException() {
+  public /* @Nullable */ SQLException getException() {
     if (delegate != null) {
       return delegate.getException();
     }
@@ -74,7 +76,7 @@ public class ResultHandlerDelegate implements ResultHandler {
   }
 
   @Override
-  public SQLWarning getWarning() {
+  public /* @Nullable */ SQLWarning getWarning() {
     if (delegate != null) {
       return delegate.getWarning();
     }
diff --git a/src/main/java/org/postgresql/core/ServerVersion.java b/src/main/java/org/postgresql/core/ServerVersion.java
index 9348ba1321191fbce161e16a090144e52f92bb73..1d6ee3dc9973b9c1d854c1f7db3c9d84142e5df3 100644
--- a/src/main/java/org/postgresql/core/ServerVersion.java
+++ b/src/main/java/org/postgresql/core/ServerVersion.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.text.NumberFormat;
 import java.text.ParsePosition;
 
@@ -54,7 +56,7 @@ public enum ServerVersion implements Version {
    * @param version version in numeric XXYYZZ form, e.g. "090401" for 9.4.1
    * @return a {@link Version} representing the specified version string.
    */
-  public static Version from(String version) {
+  public static Version from(/* @Nullable */ String version) {
     final int versionNum = parseServerVersionStr(version);
     return new Version() {
       @Override
@@ -63,7 +65,7 @@ public enum ServerVersion implements Version {
       }
 
       @Override
-      public boolean equals(Object obj) {
+      public boolean equals(/* @Nullable */ Object obj) {
         if (obj instanceof Version) {
           return this.getVersionNum() == ((Version) obj).getVersionNum();
         }
@@ -99,15 +101,15 @@ public enum ServerVersion implements Version {
    * @param serverVersion server vertion in a XXYYZZ form
    * @return server version in number form
    */
-  static int parseServerVersionStr(String serverVersion) throws NumberFormatException {
-    NumberFormat numformat = NumberFormat.getIntegerInstance();
-    numformat.setGroupingUsed(false);
-    ParsePosition parsepos = new ParsePosition(0);
-
+  static int parseServerVersionStr(/* @Nullable */ String serverVersion) throws NumberFormatException {
     if (serverVersion == null) {
       return 0;
     }
 
+    NumberFormat numformat = NumberFormat.getIntegerInstance();
+    numformat.setGroupingUsed(false);
+    ParsePosition parsepos = new ParsePosition(0);
+
     int[] parts = new int[3];
     int versionParts;
     for (versionParts = 0; versionParts < 3; versionParts++) {
diff --git a/src/main/java/org/postgresql/core/SetupQueryRunner.java b/src/main/java/org/postgresql/core/SetupQueryRunner.java
index fcf9d830d9afa7daeb54d9b9df9200acc9119fbb..6ba55449a5815ed468192ffade7f523ea9997010 100644
--- a/src/main/java/org/postgresql/core/SetupQueryRunner.java
+++ b/src/main/java/org/postgresql/core/SetupQueryRunner.java
@@ -10,6 +10,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 import java.sql.SQLWarning;
 import java.util.List;
@@ -21,14 +23,14 @@ import java.util.List;
 public class SetupQueryRunner {
 
   private static class SimpleResultHandler extends ResultHandlerBase {
-    private List<Tuple> tuples;
+    private /* @Nullable */ List<Tuple> tuples;
 
-    List<Tuple> getResults() {
+    /* @Nullable */ List<Tuple> getResults() {
       return tuples;
     }
 
     public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
-        ResultCursor cursor) {
+        /* @Nullable */ ResultCursor cursor) {
       this.tuples = tuples;
     }
 
@@ -38,7 +40,7 @@ public class SetupQueryRunner {
     }
   }
 
-  public static Tuple run(QueryExecutor executor, String queryString,
+  public static /* @Nullable */ Tuple run(QueryExecutor executor, String queryString,
       boolean wantResults) throws SQLException {
     Query query = executor.createSimpleQuery(queryString);
     SimpleResultHandler handler = new SimpleResultHandler();
diff --git a/src/main/java/org/postgresql/core/Tuple.java b/src/main/java/org/postgresql/core/Tuple.java
index 8e076ce53492ff4c61af35839feb535967394e32..98b12ead113d62339a2104114a72e05df052108a 100644
--- a/src/main/java/org/postgresql/core/Tuple.java
+++ b/src/main/java/org/postgresql/core/Tuple.java
@@ -5,12 +5,16 @@
 
 package org.postgresql.core;
 
+// import org.checkerframework.checker.index.qual.NonNegative;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.dataflow.qual.Pure;
+
 /**
  * Class representing a row in a {@link java.sql.ResultSet}.
  */
 public class Tuple {
   private final boolean forUpdate;
-  final byte[][] data;
+  final byte[] /* @Nullable */ [] data;
 
   /**
    * Construct an empty tuple. Used in updatable result sets.
@@ -24,11 +28,11 @@ public class Tuple {
    * Construct a populated tuple. Used when returning results.
    * @param data the tuple data
    */
-  public Tuple(byte[][] data) {
+  public Tuple(byte[] /* @Nullable */ [] data) {
     this(data, false);
   }
 
-  private Tuple(byte[][] data, boolean forUpdate) {
+  private Tuple(byte[] /* @Nullable */ [] data, boolean forUpdate) {
     this.data = data;
     this.forUpdate = forUpdate;
   }
@@ -37,7 +41,7 @@ public class Tuple {
    * Number of fields in the tuple
    * @return number of fields
    */
-  public int fieldCount() {
+  public /* @NonNegative */ int fieldCount() {
     return data.length;
   }
 
@@ -45,7 +49,7 @@ public class Tuple {
    * Total length in bytes of the tuple data.
    * @return the number of bytes in this tuple
    */
-  public int length() {
+  public /* @NonNegative */ int length() {
     int length = 0;
     for (byte[] field : data) {
       if (field != null) {
@@ -60,7 +64,8 @@ public class Tuple {
    * @param index 0-based field position in the tuple
    * @return byte array of the data
    */
-  public byte[] get(int index) {
+  /* @Pure */
+  public byte /* @Nullable */ [] get(/* @NonNegative */ int index) {
     return data[index];
   }
 
@@ -91,7 +96,7 @@ public class Tuple {
    * @param index 0-based field position
    * @param fieldData the data to set
    */
-  public void set(int index, byte[] fieldData) {
+  public void set(/* @NonNegative */ int index, byte /* @Nullable */ [] fieldData) {
     if (!forUpdate) {
       throw new IllegalArgumentException("Attempted to write to readonly tuple");
     }
diff --git a/src/main/java/org/postgresql/core/TypeInfo.java b/src/main/java/org/postgresql/core/TypeInfo.java
index 504cde72d9991e4b9cf550fdb773825e89bc05ac..994d4a19380a17d8d44d580caa10d29b91b83ecf 100644
--- a/src/main/java/org/postgresql/core/TypeInfo.java
+++ b/src/main/java/org/postgresql/core/TypeInfo.java
@@ -7,6 +7,8 @@ package org.postgresql.core;
 
 import org.postgresql.util.PGobject;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 import java.util.Iterator;
 
@@ -52,7 +54,7 @@ public interface TypeInfo {
    * @return the server type name for that OID or null if unknown
    * @throws SQLException if an error occurs when retrieving PG type
    */
-  String getPGType(int oid) throws SQLException;
+  /* @Nullable */ String getPGType(int oid) throws SQLException;
 
   /**
    * Look up the oid of an array's base type given the array's type oid.
@@ -83,7 +85,7 @@ public interface TypeInfo {
 
   Iterator<String> getPGTypeNamesWithSQLTypes();
 
-  Class<? extends PGobject> getPGobject(String type);
+  /* @Nullable */ Class<? extends PGobject> getPGobject(String type);
 
   String getJavaClass(int oid) throws SQLException;
 
diff --git a/src/main/java/org/postgresql/core/Utils.java b/src/main/java/org/postgresql/core/Utils.java
index 8b9c018b4273367b0ae616ede42a7ec2f263a5c0..2f761afce38247fa5f8c5b5d3cba0c17bddd52d9 100644
--- a/src/main/java/org/postgresql/core/Utils.java
+++ b/src/main/java/org/postgresql/core/Utils.java
@@ -10,6 +10,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.nio.charset.Charset;
 import java.sql.SQLException;
@@ -65,7 +67,7 @@ public class Utils {
    * @return the sbuf argument; or a new string builder for sbuf == null
    * @throws SQLException if the string contains a {@code \0} character
    */
-  public static StringBuilder escapeLiteral(StringBuilder sbuf, String value,
+  public static StringBuilder escapeLiteral(/* @Nullable */ StringBuilder sbuf, String value,
       boolean standardConformingStrings) throws SQLException {
     if (sbuf == null) {
       sbuf = new StringBuilder((value.length() + 10) / 10 * 11); // Add 10% for escaping.
@@ -133,7 +135,7 @@ public class Utils {
    * @return the sbuf argument; or a new string builder for sbuf == null
    * @throws SQLException if the string contains a {@code \0} character
    */
-  public static StringBuilder escapeIdentifier(StringBuilder sbuf, String value)
+  public static StringBuilder escapeIdentifier(/* @Nullable */ StringBuilder sbuf, String value)
       throws SQLException {
     if (sbuf == null) {
       sbuf = new StringBuilder(2 + (value.length() + 10) / 10 * 11); // Add 10% for escaping.
@@ -191,7 +193,7 @@ public class Utils {
    * @deprecated use specific {@link Version} instance
    */
   @Deprecated
-  public static int parseServerVersionStr(String serverVersion) throws NumberFormatException {
+  public static int parseServerVersionStr(/* @Nullable */ String serverVersion) throws NumberFormatException {
     return ServerVersion.parseServerVersionStr(serverVersion);
   }
 }
diff --git a/src/main/java/org/postgresql/core/VisibleBufferedInputStream.java b/src/main/java/org/postgresql/core/VisibleBufferedInputStream.java
index b0cd984bba7c9c3caf97903d0f0faa6e5531343b..874e480dd6e95bbbd6e96b218705ef14eb415ac9 100644
--- a/src/main/java/org/postgresql/core/VisibleBufferedInputStream.java
+++ b/src/main/java/org/postgresql/core/VisibleBufferedInputStream.java
@@ -337,4 +337,12 @@ public class VisibleBufferedInputStream extends InputStream {
   public void setTimeoutRequested(boolean timeoutRequested) {
     this.timeoutRequested = timeoutRequested;
   }
+
+  /**
+   *
+   * @return the wrapped stream
+   */
+  public InputStream getWrapped() {
+    return wrapped;
+  }
 }
diff --git a/src/main/java/org/postgresql/core/v3/BatchedQuery.java b/src/main/java/org/postgresql/core/v3/BatchedQuery.java
index dc24a751bc631f38afcd135c123398b415160fd5..79d7faedd8fe9001ccbcf5a7bc3473822969d064 100644
--- a/src/main/java/org/postgresql/core/v3/BatchedQuery.java
+++ b/src/main/java/org/postgresql/core/v3/BatchedQuery.java
@@ -8,6 +8,8 @@ package org.postgresql.core.v3;
 import org.postgresql.core.NativeQuery;
 import org.postgresql.core.ParameterList;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Purpose of this object is to support batched query re write behaviour. Responsibility for
  * tracking the batch size and implement the clean up of the query fragments after the batch execute
@@ -20,11 +22,11 @@ import org.postgresql.core.ParameterList;
  */
 public class BatchedQuery extends SimpleQuery {
 
-  private String sql;
+  private /* @Nullable */ String sql;
   private final int valuesBraceOpenPosition;
   private final int valuesBraceClosePosition;
   private final int batchSize;
-  private BatchedQuery[] blocks;
+  private BatchedQuery /* @Nullable */ [] blocks;
 
   public BatchedQuery(NativeQuery query, TypeTransferModeRegistry transferModeRegistry,
       int valuesBraceOpenPosition,
@@ -84,7 +86,7 @@ public class BatchedQuery extends SimpleQuery {
     return sql;
   }
 
-  private String buildNativeSql(ParameterList params) {
+  private String buildNativeSql(/* @Nullable */ ParameterList params) {
     String sql = null;
     // dynamically build sql with parameters for batches
     String nativeSql = super.getNativeSql();
@@ -172,7 +174,7 @@ public class BatchedQuery extends SimpleQuery {
   }
 
   @Override
-  public String toString(ParameterList params) {
+  public String toString(/* @Nullable */ ParameterList params) {
     if (getBatchSize() < 2) {
       return super.toString(params);
     }
diff --git a/src/main/java/org/postgresql/core/v3/CompositeParameterList.java b/src/main/java/org/postgresql/core/v3/CompositeParameterList.java
index 0fabdc305f3958ef658b5d2e1138ce33f317b267..98992f5a372ebd1762988a6e7a62534fabc6c590 100644
--- a/src/main/java/org/postgresql/core/v3/CompositeParameterList.java
+++ b/src/main/java/org/postgresql/core/v3/CompositeParameterList.java
@@ -12,6 +12,10 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.index.qual.NonNegative;
+// import org.checkerframework.checker.index.qual.Positive;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.InputStream;
 import java.sql.SQLException;
 
@@ -28,7 +32,7 @@ class CompositeParameterList implements V3ParameterList {
     this.total = offsets[offsets.length - 1] + subparams[offsets.length - 1].getInParameterCount();
   }
 
-  private int findSubParam(int index) throws SQLException {
+  private int findSubParam(/* @Positive */ int index) throws SQLException {
     if (index < 1 || index > total) {
       throw new PSQLException(
           GT.tr("The column index is out of range: {0}, number of columns: {1}.", index, total),
@@ -44,7 +48,7 @@ class CompositeParameterList implements V3ParameterList {
     throw new IllegalArgumentException("I am confused; can't find a subparam for index " + index);
   }
 
-  public void registerOutParameter(int index, int sqlType) {
+  public void registerOutParameter(/* @Positive */ int index, int sqlType) {
 
   }
 
@@ -52,15 +56,15 @@ class CompositeParameterList implements V3ParameterList {
     return 0;
   }
 
-  public int getParameterCount() {
+  public /* @NonNegative */ int getParameterCount() {
     return total;
   }
 
-  public int getInParameterCount() {
+  public /* @NonNegative */ int getInParameterCount() {
     return total;
   }
 
-  public int getOutParameterCount() {
+  public /* @NonNegative */ int getOutParameterCount() {
     return 0;
   }
 
@@ -73,57 +77,57 @@ class CompositeParameterList implements V3ParameterList {
     return oids;
   }
 
-  public void setIntParameter(int index, int value) throws SQLException {
+  public void setIntParameter(/* @Positive */ int index, int value) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setIntParameter(index - offsets[sub], value);
   }
 
-  public void setLiteralParameter(int index, String value, int oid) throws SQLException {
+  public void setLiteralParameter(/* @Positive */ int index, String value, int oid) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setStringParameter(index - offsets[sub], value, oid);
   }
 
-  public void setStringParameter(int index, String value, int oid) throws SQLException {
+  public void setStringParameter(/* @Positive */ int index, String value, int oid) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setStringParameter(index - offsets[sub], value, oid);
   }
 
-  public void setBinaryParameter(int index, byte[] value, int oid) throws SQLException {
+  public void setBinaryParameter(/* @Positive */ int index, byte[] value, int oid) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setBinaryParameter(index - offsets[sub], value, oid);
   }
 
-  public void setBytea(int index, byte[] data, int offset, int length) throws SQLException {
+  public void setBytea(/* @Positive */ int index, byte[] data, int offset, /* @NonNegative */ int length) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setBytea(index - offsets[sub], data, offset, length);
   }
 
-  public void setBytea(int index, InputStream stream, int length) throws SQLException {
+  public void setBytea(/* @Positive */ int index, InputStream stream, /* @NonNegative */ int length) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setBytea(index - offsets[sub], stream, length);
   }
 
-  public void setBytea(int index, InputStream stream) throws SQLException {
+  public void setBytea(/* @Positive */ int index, InputStream stream) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setBytea(index - offsets[sub], stream);
   }
 
-  public void setBytea(int index, ByteStreamWriter writer) throws SQLException {
+  public void setBytea(/* @Positive */ int index, ByteStreamWriter writer) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setBytea(index - offsets[sub], writer);
   }
 
-  public void setText(int index, InputStream stream) throws SQLException {
+  public void setText(/* @Positive */ int index, InputStream stream) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setText(index - offsets[sub], stream);
   }
 
-  public void setNull(int index, int oid) throws SQLException {
+  public void setNull(/* @Positive */ int index, int oid) throws SQLException {
     int sub = findSubParam(index);
     subparams[sub].setNull(index - offsets[sub], oid);
   }
 
-  public String toString(int index, boolean standardConformingStrings) {
+  public String toString(/* @Positive */ int index, boolean standardConformingStrings) {
     try {
       int sub = findSubParam(index);
       return subparams[sub].toString(index - offsets[sub], standardConformingStrings);
@@ -147,7 +151,7 @@ class CompositeParameterList implements V3ParameterList {
     }
   }
 
-  public SimpleParameterList[] getSubparams() {
+  public SimpleParameterList /* @Nullable */ [] getSubparams() {
     return subparams;
   }
 
@@ -157,19 +161,19 @@ class CompositeParameterList implements V3ParameterList {
     }
   }
 
-  public byte[][] getEncoding() {
+  public byte /* @Nullable */ [][] getEncoding() {
     return null; // unsupported
   }
 
-  public byte[] getFlags() {
+  public byte /* @Nullable */ [] getFlags() {
     return null; // unsupported
   }
 
-  public int[] getParamTypes() {
+  public int /* @Nullable */ [] getParamTypes() {
     return null; // unsupported
   }
 
-  public Object[] getValues() {
+  public /* @Nullable */ Object /* @Nullable */ [] getValues() {
     return null; // unsupported
   }
 
@@ -183,7 +187,7 @@ class CompositeParameterList implements V3ParameterList {
     }
   }
 
-  private final int total;
+  private final /* @Positive */ int total;
   private final SimpleParameterList[] subparams;
   private final int[] offsets;
 }
diff --git a/src/main/java/org/postgresql/core/v3/CompositeQuery.java b/src/main/java/org/postgresql/core/v3/CompositeQuery.java
index 789979b657f0a2d06ef3f6660a8e7d8ef5daac47..74604545d083c70e6b9a3c15005e5916364deaa5 100644
--- a/src/main/java/org/postgresql/core/v3/CompositeQuery.java
+++ b/src/main/java/org/postgresql/core/v3/CompositeQuery.java
@@ -10,6 +10,8 @@ import org.postgresql.core.ParameterList;
 import org.postgresql.core.Query;
 import org.postgresql.core.SqlCommand;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.Map;
 
 /**
@@ -33,7 +35,7 @@ class CompositeQuery implements Query {
     return new CompositeParameterList(subparams, offsets);
   }
 
-  public String toString(ParameterList parameters) {
+  public String toString(/* @Nullable */ ParameterList parameters) {
     StringBuilder sbuf = new StringBuilder(subqueries[0].toString());
     for (int i = 1; i < subqueries.length; ++i) {
       sbuf.append(';');
@@ -53,7 +55,7 @@ class CompositeQuery implements Query {
   }
 
   @Override
-  public SqlCommand getSqlCommand() {
+  public /* @Nullable */ SqlCommand getSqlCommand() {
     return null;
   }
 
@@ -94,7 +96,7 @@ class CompositeQuery implements Query {
   }
 
   @Override
-  public Map<String, Integer> getResultSetColumnNameIndexMap() {
+  public /* @Nullable */ Map<String, Integer> getResultSetColumnNameIndexMap() {
     return null; // unsupported
   }
 
diff --git a/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java b/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
index 9737a82eab64cf8b43e09c374097d6b909ad7dd1..07cd2073112fd2fe5d49a9a3ac3ee3194ef1e201 100644
--- a/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
+++ b/src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java
@@ -6,6 +6,8 @@
 
 package org.postgresql.core.v3;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGProperty;
 import org.postgresql.core.ConnectionFactory;
 import org.postgresql.core.PGStream;
@@ -22,6 +24,7 @@ import org.postgresql.hostchooser.HostChooser;
 import org.postgresql.hostchooser.HostChooserFactory;
 import org.postgresql.hostchooser.HostRequirement;
 import org.postgresql.hostchooser.HostStatus;
+import org.postgresql.jdbc.GSSEncMode;
 import org.postgresql.jdbc.SslMode;
 import org.postgresql.sspi.ISSPIClient;
 import org.postgresql.util.GT;
@@ -31,6 +34,8 @@ import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 import org.postgresql.util.ServerErrorMessage;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.net.ConnectException;
 import java.sql.SQLException;
@@ -70,7 +75,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
   private static final int AUTH_REQ_SASL_FINAL = 12;
 
   private ISSPIClient createSSPI(PGStream pgStream,
-      String spnServiceClass,
+      /* @Nullable */ String spnServiceClass,
       boolean enableNegotiate) {
     try {
       @SuppressWarnings("unchecked")
@@ -86,7 +91,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
 
   private PGStream tryConnect(String user, String database,
       Properties info, SocketFactory socketFactory, HostSpec hostSpec,
-      SslMode sslMode)
+      SslMode sslMode, GSSEncMode gssEncMode)
       throws SQLException, IOException {
     int connectTimeout = PGProperty.CONNECT_TIMEOUT.getInt(info) * 1000;
 
@@ -95,7 +100,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
     // Set the socket timeout if the "socketTimeout" property has been set.
     int socketTimeout = PGProperty.SOCKET_TIMEOUT.getInt(info);
     if (socketTimeout > 0) {
-      newStream.getSocket().setSoTimeout(socketTimeout * 1000);
+      newStream.setNetworkTimeout(socketTimeout * 1000);
     }
 
     String maxResultBuffer = PGProperty.MAX_RESULT_BUFFER.get(info);
@@ -136,8 +141,18 @@ 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);
+    newStream = enableGSSEncrypted(newStream, gssEncMode, hostSpec.getHost(), user, info, connectTimeout);
+
+    // if we have a security context then gss negotiation succeeded. Do not attempt SSL negotiation
+    if (!newStream.isGssEncrypted()) {
+      // Construct and send an ssl startup packet if requested.
+      newStream = enableSSL(newStream, sslMode, info, connectTimeout);
+    }
+
+    // Make sure to set network timeout again, in case the stream changed due to GSS or SSL
+    if (socketTimeout > 0) {
+      newStream.setNetworkTimeout(socketTimeout * 1000);
+    }
 
     List<String[]> paramList = getParametersForStartup(user, database, info);
     sendStartupPacket(newStream, paramList);
@@ -152,9 +167,10 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
   public QueryExecutor openConnectionImpl(HostSpec[] hostSpecs, String user, String database,
       Properties info) throws SQLException {
     SslMode sslMode = SslMode.of(info);
+    GSSEncMode gssEncMode = GSSEncMode.of(info);
 
     HostRequirement targetServerType;
-    String targetServerTypeStr = PGProperty.TARGET_SERVER_TYPE.get(info);
+    String targetServerTypeStr = castNonNull(PGProperty.TARGET_SERVER_TYPE.get(info));
     try {
       targetServerType = HostRequirement.getTargetServerType(targetServerTypeStr);
     } catch (IllegalArgumentException ex) {
@@ -194,7 +210,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
       PGStream newStream = null;
       try {
         try {
-          newStream = tryConnect(user, database, info, socketFactory, hostSpec, sslMode);
+          newStream = tryConnect(user, database, info, socketFactory, hostSpec, sslMode, gssEncMode);
         } catch (SQLException e) {
           if (sslMode == SslMode.PREFER
               && PSQLState.INVALID_AUTHORIZATION_SPECIFICATION.getState().equals(e.getSQLState())) {
@@ -203,7 +219,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
             Throwable ex = null;
             try {
               newStream =
-                  tryConnect(user, database, info, socketFactory, hostSpec, SslMode.DISABLE);
+                  tryConnect(user, database, info, socketFactory, hostSpec, SslMode.DISABLE,gssEncMode);
               LOGGER.log(Level.FINE, "Downgraded to non-encrypted connection for host {0}",
                   hostSpec);
             } catch (SQLException ee) {
@@ -226,7 +242,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
             Throwable ex = null;
             try {
               newStream =
-                  tryConnect(user, database, info, socketFactory, hostSpec, SslMode.REQUIRE);
+                  tryConnect(user, database, info, socketFactory, hostSpec, SslMode.REQUIRE, gssEncMode);
               LOGGER.log(Level.FINE, "Upgraded to encrypted connection for host {0}",
                   hostSpec);
             } catch (SQLException ee) {
@@ -251,6 +267,8 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
 
         int cancelSignalTimeout = PGProperty.CANCEL_SIGNAL_TIMEOUT.getInt(info) * 1000;
 
+        // CheckerFramework can't infer newStream is non-nullable
+        castNonNull(newStream);
         // Do final startup.
         QueryExecutor queryExecutor = new QueryExecutorImpl(newStream, user, database,
             cancelSignalTimeout, info);
@@ -393,6 +411,77 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
     return start + tz.substring(4);
   }
 
+  private PGStream enableGSSEncrypted(PGStream pgStream, GSSEncMode gssEncMode, String host, String user, Properties info,
+                                    int connectTimeout)
+      throws IOException, PSQLException {
+
+    if ( gssEncMode == GSSEncMode.DISABLE ) {
+      return pgStream;
+    }
+
+    if (gssEncMode == GSSEncMode.ALLOW ) {
+      // start with plain text and let the server request it
+      return pgStream;
+    }
+
+    String password = PGProperty.PASSWORD.get(info);
+    LOGGER.log(Level.FINEST, " FE=> GSSENCRequest");
+
+    // Send SSL request packet
+    pgStream.sendInteger4(8);
+    pgStream.sendInteger2(1234);
+    pgStream.sendInteger2(5680);
+    pgStream.flush();
+    // Now get the response from the backend, one of N, E, S.
+    int beresp = pgStream.receiveChar();
+    switch (beresp) {
+      case 'E':
+        LOGGER.log(Level.FINEST, " <=BE GSSEncrypted Error");
+
+        // Server doesn't even know about the SSL handshake protocol
+        if (gssEncMode.requireEncryption()) {
+          throw new PSQLException(GT.tr("The server does not support GSS Encoding."),
+              PSQLState.CONNECTION_REJECTED);
+        }
+
+        // We have to reconnect to continue.
+        pgStream.close();
+        return new PGStream(pgStream.getSocketFactory(), pgStream.getHostSpec(), connectTimeout);
+
+      case 'N':
+        LOGGER.log(Level.FINEST, " <=BE GSSEncrypted Refused");
+
+        // Server does not support gss encryption
+        if (gssEncMode.requireEncryption()) {
+          throw new PSQLException(GT.tr("The server does not support GSS Encryption."),
+              PSQLState.CONNECTION_REJECTED);
+        }
+
+        return pgStream;
+
+      case 'G':
+        LOGGER.log(Level.FINEST, " <=BE GSSEncryptedOk");
+        try {
+          org.postgresql.gss.MakeGSS.authenticate(true, pgStream, host, user, password,
+              PGProperty.JAAS_APPLICATION_NAME.get(info),
+              PGProperty.KERBEROS_SERVER_NAME.get(info), false, // TODO: fix this
+              PGProperty.JAAS_LOGIN.getBoolean(info),
+              PGProperty.LOG_SERVER_ERROR_DETAIL.getBoolean(info));
+          return pgStream;
+        } catch (PSQLException ex) {
+          // allow the connection to proceed
+          if ( gssEncMode == GSSEncMode.PREFER) {
+            // we have to reconnect to continue
+            return new PGStream(pgStream, connectTimeout);
+          }
+        }
+
+      default:
+        throw new PSQLException(GT.tr("An error occurred while setting up the GSS Encoded connection."),
+            PSQLState.PROTOCOL_VIOLATION);
+    }
+  }
+
   private PGStream enableSSL(PGStream pgStream, SslMode sslMode, Properties info,
       int connectTimeout)
       throws IOException, PSQLException {
@@ -425,8 +514,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
         }
 
         // We have to reconnect to continue.
-        pgStream.close();
-        return new PGStream(pgStream.getSocketFactory(), pgStream.getHostSpec(), connectTimeout);
+        return new PGStream(pgStream, connectTimeout);
 
       case 'N':
         LOGGER.log(Level.FINEST, " <=BE SSLRefused");
@@ -614,9 +702,9 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
                  * it's forced. Otherwise use gssapi. If the user has specified a Kerberos server
                  * name we'll always use JSSE GSSAPI.
                  */
-                if (gsslib.equals("gssapi")) {
+                if ("gssapi".equals(gsslib)) {
                   LOGGER.log(Level.FINE, "Using JSSE GSSAPI, param gsslib=gssapi");
-                } else if (areq == AUTH_REQ_GSS && !gsslib.equals("sspi")) {
+                } else if (areq == AUTH_REQ_GSS && !"sspi".equals(gsslib)) {
                   LOGGER.log(Level.FINE,
                       "Using JSSE GSSAPI, gssapi requested by server and gsslib=sspi not forced");
                 } else {
@@ -632,7 +720,7 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
                     /* No need to dispose() if no SSPI used */
                     sspiClient = null;
 
-                    if (gsslib.equals("sspi")) {
+                    if ("sspi".equals(gsslib)) {
                       throw new PSQLException(
                           "SSPI forced with gsslib=sspi, but SSPI not available; set loglevel=2 for details",
                           PSQLState.CONNECTION_UNABLE_TO_CONNECT);
@@ -646,10 +734,10 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
 
                 if (useSSPI) {
                   /* SSPI requested and detected as available */
-                  sspiClient.startSSPI();
+                  castNonNull(sspiClient).startSSPI();
                 } else {
                   /* Use JGSS's GSSAPI for this request */
-                  org.postgresql.gss.MakeGSS.authenticate(pgStream, host, user, password,
+                  org.postgresql.gss.MakeGSS.authenticate(false, pgStream, host, user, password,
                       PGProperty.JAAS_APPLICATION_NAME.get(info),
                       PGProperty.KERBEROS_SERVER_NAME.get(info), usespnego,
                       PGProperty.JAAS_LOGIN.getBoolean(info),
@@ -661,14 +749,14 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
                 /*
                  * Only called for SSPI, as GSS is handled by an inner loop in MakeGSS.
                  */
-                sspiClient.continueSSPI(msgLen - 8);
+                castNonNull(sspiClient).continueSSPI(msgLen - 8);
                 break;
 
               case AUTH_REQ_SASL:
                 LOGGER.log(Level.FINEST, " <=BE AuthenticationSASL");
 
                 //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
-                scramAuthenticator = new org.postgresql.jre7.sasl.ScramAuthenticator(user, password, pgStream);
+                scramAuthenticator = new org.postgresql.jre7.sasl.ScramAuthenticator(user, castNonNull(password), pgStream);
                 scramAuthenticator.processServerMechanismsAndInit();
                 scramAuthenticator.sendScramClientFirstMessage();
                 // This works as follows:
@@ -687,11 +775,11 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
 
               //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
               case AUTH_REQ_SASL_CONTINUE:
-                scramAuthenticator.processServerFirstMessage(msgLen - 4 - 4);
+                castNonNull(scramAuthenticator).processServerFirstMessage(msgLen - 4 - 4);
                 break;
 
               case AUTH_REQ_SASL_FINAL:
-                scramAuthenticator.verifyServerSignature(msgLen - 4 - 4);
+                castNonNull(scramAuthenticator).verifyServerSignature(msgLen - 4 - 4);
                 break;
               //#endif
 
@@ -755,7 +843,8 @@ public class ConnectionFactoryImpl extends ConnectionFactory {
 
   private boolean isPrimary(QueryExecutor queryExecutor) throws SQLException, IOException {
     Tuple results = SetupQueryRunner.run(queryExecutor, "show transaction_read_only", true);
-    String value = queryExecutor.getEncoding().decode(results.get(0));
+    Tuple nonNullResults = castNonNull(results);
+    String value = queryExecutor.getEncoding().decode(castNonNull(nonNullResults.get(0)));
     return value.equalsIgnoreCase("off");
   }
 }
diff --git a/src/main/java/org/postgresql/core/v3/CopyDualImpl.java b/src/main/java/org/postgresql/core/v3/CopyDualImpl.java
index 62ebda9be0c6235c1745588bf896bc5ef30fb05a..621d6cfe2bfb35dc226a80b0ad3baddaeb140906 100644
--- a/src/main/java/org/postgresql/core/v3/CopyDualImpl.java
+++ b/src/main/java/org/postgresql/core/v3/CopyDualImpl.java
@@ -9,37 +9,39 @@ import org.postgresql.copy.CopyDual;
 import org.postgresql.util.ByteStreamWriter;
 import org.postgresql.util.PSQLException;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
-import java.util.LinkedList;
+import java.util.ArrayDeque;
 import java.util.Queue;
 
 public class CopyDualImpl extends CopyOperationImpl implements CopyDual {
-  private Queue<byte[]> received = new LinkedList<byte[]>();
+  private final Queue<byte[]> received = new ArrayDeque<byte[]>();
 
   public void writeToCopy(byte[] data, int off, int siz) throws SQLException {
-    queryExecutor.writeToCopy(this, data, off, siz);
+    getQueryExecutor().writeToCopy(this, data, off, siz);
   }
 
   public void writeToCopy(ByteStreamWriter from) throws SQLException {
-    queryExecutor.writeToCopy(this, from);
+    getQueryExecutor().writeToCopy(this, from);
   }
 
   public void flushCopy() throws SQLException {
-    queryExecutor.flushCopy(this);
+    getQueryExecutor().flushCopy(this);
   }
 
   public long endCopy() throws SQLException {
-    return queryExecutor.endCopy(this);
+    return getQueryExecutor().endCopy(this);
   }
 
-  public byte[] readFromCopy() throws SQLException {
+  public byte /* @Nullable */ [] readFromCopy() throws SQLException {
     return readFromCopy(true);
   }
 
   @Override
-  public byte[] readFromCopy(boolean block) throws SQLException {
+  public byte /* @Nullable */ [] readFromCopy(boolean block) throws SQLException {
     if (received.isEmpty()) {
-      queryExecutor.readFromCopy(this, block);
+      getQueryExecutor().readFromCopy(this, block);
     }
 
     return received.poll();
diff --git a/src/main/java/org/postgresql/core/v3/CopyInImpl.java b/src/main/java/org/postgresql/core/v3/CopyInImpl.java
index 0da2c1e18deb5849283f775314f294c249798129..5d7b2419ecd40eb530a1ec0e72f0fc799ea57be6 100644
--- a/src/main/java/org/postgresql/core/v3/CopyInImpl.java
+++ b/src/main/java/org/postgresql/core/v3/CopyInImpl.java
@@ -33,21 +33,20 @@ import java.sql.SQLException;
  * CopyInImpl.getUpdatedRowCount()</p>
  */
 public class CopyInImpl extends CopyOperationImpl implements CopyIn {
-
   public void writeToCopy(byte[] data, int off, int siz) throws SQLException {
-    queryExecutor.writeToCopy(this, data, off, siz);
+    getQueryExecutor().writeToCopy(this, data, off, siz);
   }
 
   public void writeToCopy(ByteStreamWriter from) throws SQLException {
-    queryExecutor.writeToCopy(this, from);
+    getQueryExecutor().writeToCopy(this, from);
   }
 
   public void flushCopy() throws SQLException {
-    queryExecutor.flushCopy(this);
+    getQueryExecutor().flushCopy(this);
   }
 
   public long endCopy() throws SQLException {
-    return queryExecutor.endCopy(this);
+    return getQueryExecutor().endCopy(this);
   }
 
   protected void handleCopydata(byte[] data) throws PSQLException {
diff --git a/src/main/java/org/postgresql/core/v3/CopyOperationImpl.java b/src/main/java/org/postgresql/core/v3/CopyOperationImpl.java
index a7bb1e8d2c1da6cc2d9844f0afc53f8dfe16d930..4f680babfc358745d079ec506f4754bedbe7d0ef 100644
--- a/src/main/java/org/postgresql/core/v3/CopyOperationImpl.java
+++ b/src/main/java/org/postgresql/core/v3/CopyOperationImpl.java
@@ -5,17 +5,21 @@
 
 package org.postgresql.core.v3;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.copy.CopyOperation;
 import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 
 public abstract class CopyOperationImpl implements CopyOperation {
-  QueryExecutorImpl queryExecutor;
+  /* @Nullable */ QueryExecutorImpl queryExecutor;
   int rowFormat;
-  int[] fieldFormats;
+  int /* @Nullable */ [] fieldFormats;
   long handledRowCount = -1;
 
   void init(QueryExecutorImpl q, int fmt, int[] fmts) {
@@ -24,16 +28,20 @@ public abstract class CopyOperationImpl implements CopyOperation {
     fieldFormats = fmts;
   }
 
+  protected QueryExecutorImpl getQueryExecutor() {
+    return castNonNull(queryExecutor);
+  }
+
   public void cancelCopy() throws SQLException {
-    queryExecutor.cancelCopy(this);
+    castNonNull(queryExecutor).cancelCopy(this);
   }
 
   public int getFieldCount() {
-    return fieldFormats.length;
+    return castNonNull(fieldFormats).length;
   }
 
   public int getFieldFormat(int field) {
-    return fieldFormats[field];
+    return castNonNull(fieldFormats)[field];
   }
 
   public int getFormat() {
@@ -41,7 +49,7 @@ public abstract class CopyOperationImpl implements CopyOperation {
   }
 
   public boolean isActive() {
-    synchronized (queryExecutor) {
+    synchronized (castNonNull(queryExecutor)) {
       return queryExecutor.hasLock(this);
     }
   }
diff --git a/src/main/java/org/postgresql/core/v3/CopyOutImpl.java b/src/main/java/org/postgresql/core/v3/CopyOutImpl.java
index f3fffb5e83d6cbc05c19289ac26099f86dc37cad..43ae2caf5a3e61ddcc93397e39210a192b94b7e9 100644
--- a/src/main/java/org/postgresql/core/v3/CopyOutImpl.java
+++ b/src/main/java/org/postgresql/core/v3/CopyOutImpl.java
@@ -7,6 +7,8 @@ package org.postgresql.core.v3;
 
 import org.postgresql.copy.CopyOut;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 
 /**
@@ -24,16 +26,16 @@ import java.sql.SQLException;
  * &lt;-returned: byte array of data received from server or null at end.</p>
  */
 public class CopyOutImpl extends CopyOperationImpl implements CopyOut {
-  private byte[] currentDataRow;
+  private byte /* @Nullable */ [] currentDataRow;
 
-  public byte[] readFromCopy() throws SQLException {
+  public byte /* @Nullable */ [] readFromCopy() throws SQLException {
     return readFromCopy(true);
   }
 
   @Override
-  public byte[] readFromCopy(boolean block) throws SQLException {
+  public byte /* @Nullable */ [] readFromCopy(boolean block) throws SQLException {
     currentDataRow = null;
-    queryExecutor.readFromCopy(this, block);
+    getQueryExecutor().readFromCopy(this, block);
     return currentDataRow;
   }
 
diff --git a/src/main/java/org/postgresql/core/v3/DescribeRequest.java b/src/main/java/org/postgresql/core/v3/DescribeRequest.java
index 05cc7bea309e083c3442d4aaf842c9d2317f8d4f..dcf35acb9724a5471d293b10a01e44ba4f17c53b 100644
--- a/src/main/java/org/postgresql/core/v3/DescribeRequest.java
+++ b/src/main/java/org/postgresql/core/v3/DescribeRequest.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core.v3;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Information for "pending describe queue".
  *
@@ -14,10 +16,10 @@ class DescribeRequest {
   public final SimpleQuery query;
   public final SimpleParameterList parameterList;
   public final boolean describeOnly;
-  public final String statementName;
+  public final /* @Nullable */ String statementName;
 
   DescribeRequest(SimpleQuery query, SimpleParameterList parameterList,
-      boolean describeOnly, String statementName) {
+      boolean describeOnly, /* @Nullable */ String statementName) {
     this.query = query;
     this.parameterList = parameterList;
     this.describeOnly = describeOnly;
diff --git a/src/main/java/org/postgresql/core/v3/ExecuteRequest.java b/src/main/java/org/postgresql/core/v3/ExecuteRequest.java
index cf6b2a86ac6ed3751c33d0fca4ebf131ecf4e0af..1be784963cfdaeff2e29c1c363379e5af371a864 100644
--- a/src/main/java/org/postgresql/core/v3/ExecuteRequest.java
+++ b/src/main/java/org/postgresql/core/v3/ExecuteRequest.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core.v3;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Information for "pending execute queue".
  *
@@ -12,10 +14,10 @@ package org.postgresql.core.v3;
  */
 class ExecuteRequest {
   public final SimpleQuery query;
-  public final Portal portal;
+  public final /* @Nullable */ Portal portal;
   public final boolean asSimple;
 
-  ExecuteRequest(SimpleQuery query, Portal portal, boolean asSimple) {
+  ExecuteRequest(SimpleQuery query, /* @Nullable */ Portal portal, boolean asSimple) {
     this.query = query;
     this.portal = portal;
     this.asSimple = asSimple;
diff --git a/src/main/java/org/postgresql/core/v3/Portal.java b/src/main/java/org/postgresql/core/v3/Portal.java
index 8a93abc4827f3cd8caba3a78de7ac654cffb8b12..c6a3f437e04ee8ec88bd6354831a03bf91b44c63 100644
--- a/src/main/java/org/postgresql/core/v3/Portal.java
+++ b/src/main/java/org/postgresql/core/v3/Portal.java
@@ -9,6 +9,8 @@ package org.postgresql.core.v3;
 import org.postgresql.core.ResultCursor;
 import org.postgresql.core.Utils;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.ref.PhantomReference;
 
 /**
@@ -18,7 +20,7 @@ import java.lang.ref.PhantomReference;
  * @author Oliver Jowett (oliver@opencloud.com)
  */
 class Portal implements ResultCursor {
-  Portal(SimpleQuery query, String portalName) {
+  Portal(/* @Nullable */ SimpleQuery query, String portalName) {
     this.query = query;
     this.portalName = portalName;
     this.encodedName = Utils.encodeUTF8(portalName);
@@ -40,7 +42,7 @@ class Portal implements ResultCursor {
     return encodedName;
   }
 
-  SimpleQuery getQuery() {
+  /* @Nullable */ SimpleQuery getQuery() {
     return query;
   }
 
@@ -58,8 +60,8 @@ class Portal implements ResultCursor {
   // be closed while the portal is open (the backend closes
   // all open portals when the statement is closed)
 
-  private final SimpleQuery query;
+  private final /* @Nullable */ SimpleQuery query;
   private final String portalName;
   private final byte[] encodedName;
-  private PhantomReference<?> cleanupRef;
+  private /* @Nullable */ PhantomReference<?> cleanupRef;
 }
diff --git a/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java b/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
index 15d340747a443e298975e363d713ae8bd3ba9c12..2ccdb553ae49adb0373471186ea37d3e27a29120 100644
--- a/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
+++ b/src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java
@@ -6,6 +6,8 @@
 
 package org.postgresql.core.v3;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGProperty;
 import org.postgresql.copy.CopyIn;
 import org.postgresql.copy.CopyOperation;
@@ -44,6 +46,8 @@ import org.postgresql.util.PSQLState;
 import org.postgresql.util.PSQLWarning;
 import org.postgresql.util.ServerErrorMessage;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.lang.ref.PhantomReference;
 import java.lang.ref.Reference;
@@ -75,15 +79,17 @@ public class QueryExecutorImpl extends QueryExecutorBase {
 
   private static final Logger LOGGER = Logger.getLogger(QueryExecutorImpl.class.getName());
 
+  private static final Field[] NO_FIELDS = new Field[0];
+
   /**
    * TimeZone of the current connection (TimeZone backend parameter).
    */
-  private TimeZone timeZone;
+  private /* @Nullable */ TimeZone timeZone;
 
   /**
    * application_name connection property.
    */
-  private String applicationName;
+  private /* @Nullable */ String applicationName;
 
   /**
    * True if server uses integers for date and time fields. False if server uses double.
@@ -104,6 +110,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    * This is a fake query object so processResults can distinguish "ReadyForQuery" messages
    * from Sync messages vs from simple execute (aka 'Q').
    */
+  @SuppressWarnings("method.invocation.invalid")
   private final SimpleQuery sync = (SimpleQuery) createQuery("SYNC", false, true).query;
 
   private short deallocateEpoch;
@@ -113,12 +120,12 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    * statement cache can be skipped if using repeated calls for the same {@code set search_path}
    * value.
    */
-  private String lastSetSearchPathQuery;
+  private /* @Nullable */ String lastSetSearchPathQuery;
 
   /**
    * The exception that caused the last transaction to fail.
    */
-  private SQLException transactionFailCause;
+  private /* @Nullable */ SQLException transactionFailCause;
 
   private final ReplicationProtocol replicationProtocol;
 
@@ -127,12 +134,15 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    */
   private final CommandCompleteParser commandCompleteParser = new CommandCompleteParser();
 
+  @SuppressWarnings({"assignment.type.incompatible", "argument.type.incompatible",
+      "method.invocation.invalid"})
   public QueryExecutorImpl(PGStream pgStream, String user, String database,
       int cancelSignalTimeout, Properties info) throws SQLException, IOException {
     super(pgStream, user, database, cancelSignalTimeout, info);
 
     this.allowEncodingChanges = PGProperty.ALLOW_ENCODING_CHANGES.getBoolean(info);
     this.cleanupSavePoints = PGProperty.CLEANUP_SAVEPOINTS.getBoolean(info);
+    // assignment.type.incompatible, argument.type.incompatible
     this.replicationProtocol = new V3ReplicationProtocol(this, pgStream);
     readStartupMessages();
   }
@@ -154,7 +164,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    *
    * <p>See notes on related methods as well as currentCopy() below.</p>
    */
-  private Object lockedFor = null;
+  private /* @Nullable */ Object lockedFor;
 
   /**
    * Obtain lock over this connection for given object, blocking to wait if necessary.
@@ -208,7 +218,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    * @param holder object assumed to hold the lock
    * @return whether given object actually holds the lock
    */
-  boolean hasLock(Object holder) {
+  boolean hasLock(/* @Nullable */ Object holder) {
     return lockedFor == holder;
   }
 
@@ -273,7 +283,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     }
   }
 
-  public synchronized void execute(Query query, ParameterList parameters, ResultHandler handler,
+  public synchronized void execute(Query query, /* @Nullable */ ParameterList parameters,
+      ResultHandler handler,
       int maxRows, int fetchSize, int flags) throws SQLException {
     waitOnLock();
     if (LOGGER.isLoggable(Level.FINEST)) {
@@ -459,7 +470,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
   private static final int MAX_BUFFERED_RECV_BYTES = 64000;
   private static final int NODATA_QUERY_RESPONSE_SIZE_BYTES = 250;
 
-  public synchronized void execute(Query[] queries, ParameterList[] parameterLists,
+  public synchronized void execute(Query[] queries, /* @Nullable */ ParameterList[] parameterLists,
       BatchResultHandler batchHandler, int maxRows, int fetchSize, int flags) throws SQLException {
     waitOnLock();
     if (LOGGER.isLoggable(Level.FINEST)) {
@@ -557,7 +568,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
       private boolean sawBegin = false;
 
       public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
-          ResultCursor cursor) {
+          /* @Nullable */ ResultCursor cursor) {
         if (sawBegin) {
           super.handleResultRows(fromQuery, fields, tuples, cursor);
         }
@@ -582,7 +593,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
   // Fastpath
   //
 
-  public synchronized byte[] fastpathCall(int fnid, ParameterList parameters, boolean suppressBegin)
+  public synchronized byte /* @Nullable */ [] fastpathCall(int fnid, ParameterList parameters,
+      boolean suppressBegin)
       throws SQLException {
     waitOnLock();
     if (!suppressBegin) {
@@ -789,7 +801,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     }
   }
 
-  private byte[] receiveFastpathResult() throws IOException, SQLException {
+  private byte /* @Nullable */ [] receiveFastpathResult() throws IOException, SQLException {
     boolean endQuery = false;
     SQLException error = null;
     byte[] returnValue = null;
@@ -879,7 +891,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
       pgStream.sendChar(0);
       pgStream.flush();
 
-      return processCopyResults(null, true);
+      return castNonNull(processCopyResults(null, true));
       // expect a CopyInResponse or CopyOutResponse to our query above
     } catch (IOException ioe) {
       throw new PSQLException(GT.tr("Database connection failed when starting copy"),
@@ -1116,20 +1128,20 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    * @throws SQLException in case of misuse
    * @throws IOException from the underlying connection
    */
-  CopyOperationImpl processCopyResults(CopyOperationImpl op, boolean block)
+  /* @Nullable */ CopyOperationImpl processCopyResults(/* @Nullable */ CopyOperationImpl op, boolean block)
       throws SQLException, IOException {
 
     /*
     * fixes issue #1592 where one thread closes the stream and another is reading it
      */
     if (pgStream.isClosed()) {
-      throw new PSQLException(GT.tr("PGStream is closed",
-        op.getClass().getName()), PSQLState.CONNECTION_DOES_NOT_EXIST);
+      throw new PSQLException(GT.tr("PGStream is closed"),
+          PSQLState.CONNECTION_DOES_NOT_EXIST);
     }
     /*
     *  This is a hack as we should not end up here, but sometimes do with large copy operations.
      */
-    if ( processingCopyResults.compareAndSet(false,true) == false ) {
+    if (!processingCopyResults.compareAndSet(false, true)) {
       LOGGER.log(Level.INFO, "Ignoring request to process copy results, already processing");
       return null;
     }
@@ -1297,7 +1309,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
           case 'Z': // ReadyForQuery: After FE:CopyDone => BE:CommandComplete
 
             receiveRFQ();
-            if (hasLock(op)) {
+            if (op != null && hasLock(op)) {
               unlock(op);
             }
             op = null;
@@ -1354,7 +1366,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    */
   private void flushIfDeadlockRisk(Query query, boolean disallowBatching,
       ResultHandler resultHandler,
-      BatchResultHandler batchHandler,
+      /* @Nullable */ BatchResultHandler batchHandler,
       final int flags) throws IOException {
     // Assume all statements need at least this much reply buffer space,
     // plus params
@@ -1403,7 +1415,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
    */
   private void sendQuery(Query query, V3ParameterList parameters, int maxRows, int fetchSize,
       int flags, ResultHandler resultHandler,
-      BatchResultHandler batchHandler) throws IOException, SQLException {
+      /* @Nullable */ BatchResultHandler batchHandler) throws IOException, SQLException {
     // Now the query itself.
     Query[] subqueries = query.getSubqueries();
     SimpleParameterList[] subparams = parameters.getSubparams();
@@ -1540,7 +1552,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     pendingParseQueue.add(query);
   }
 
-  private void sendBind(SimpleQuery query, SimpleParameterList params, Portal portal,
+  private void sendBind(SimpleQuery query, SimpleParameterList params, /* @Nullable */ Portal portal,
       boolean noBinaryTransfer) throws IOException {
     //
     // Send Bind.
@@ -1576,7 +1588,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     }
 
     Field[] fields = query.getFields();
-    if (!noBinaryTransfer && query.needUpdateFieldFormats()) {
+    if (!noBinaryTransfer && query.needUpdateFieldFormats() && fields != null) {
       for (Field field : fields) {
         if (useBinary(field)) {
           field.setFormat(Field.BINARY_FORMAT);
@@ -1586,7 +1598,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     }
     // If text-only results are required (e.g. updateable resultset), and the query has binary columns,
     // flip to text format.
-    if (noBinaryTransfer && query.hasBinaryFields()) {
+    if (noBinaryTransfer && query.hasBinaryFields() && fields != null) {
       for (Field field : fields) {
         if (field.getFormat() != Field.TEXT_FORMAT) {
           field.setFormat(Field.TEXT_FORMAT);
@@ -1599,7 +1611,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     // This is not the number of binary fields, but the total number
     // of fields if any of them are binary or zero if all of them
     // are text.
-    int numBinaryFields = !noBinaryTransfer && query.hasBinaryFields() ? fields.length : 0;
+    int numBinaryFields = !noBinaryTransfer && query.hasBinaryFields() && fields != null
+        ? fields.length : 0;
 
     encodedSize = 4
         + (encodedPortalName == null ? 0 : encodedPortalName.length) + 1
@@ -1661,7 +1674,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     }
 
     pgStream.sendInteger2(numBinaryFields); // # of result format codes
-    for (int i = 0; i < numBinaryFields; ++i) {
+    for (int i = 0; fields != null && i < numBinaryFields; ++i) {
       pgStream.sendInteger2(fields[i].getFormat());
     }
 
@@ -1684,7 +1697,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     return useBinaryForReceive(oid);
   }
 
-  private void sendDescribePortal(SimpleQuery query, Portal portal) throws IOException {
+  private void sendDescribePortal(SimpleQuery query, /* @Nullable */ Portal portal) throws IOException {
     //
     // Send Describe.
     //
@@ -1736,7 +1749,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     query.setPortalDescribed(true);
   }
 
-  private void sendExecute(SimpleQuery query, Portal portal, int limit) throws IOException {
+  private void sendExecute(SimpleQuery query, /* @Nullable */ Portal portal, int limit)
+      throws IOException {
     //
     // Send Execute.
     //
@@ -1867,7 +1881,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
         || (!oneShot && paramsHasUnknown && queryHasUnknown && !query.isStatementDescribed());
 
     if (!describeStatement && paramsHasUnknown && !queryHasUnknown) {
-      int[] queryOIDs = query.getPrepareTypes();
+      int[] queryOIDs = castNonNull(query.getPrepareTypes());
       int[] paramOIDs = params.getTypeOIDs();
       for (int i = 0; i < paramOIDs.length; i++) {
         // Only supply type information when there isn't any
@@ -1981,7 +1995,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
   private void processDeadParsedQueries() throws IOException {
     Reference<? extends SimpleQuery> deadQuery;
     while ((deadQuery = parsedQueryCleanupQueue.poll()) != null) {
-      String statementName = parsedQueryMap.remove(deadQuery);
+      String statementName = castNonNull(parsedQueryMap.remove(deadQuery));
       sendCloseStatement(statementName);
       deadQuery.clear();
     }
@@ -2017,7 +2031,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
   private void processDeadPortals() throws IOException {
     Reference<? extends Portal> deadPortal;
     while ((deadPortal = openPortalCleanupQueue.poll()) != null) {
-      String portalName = openPortalMap.remove(deadPortal);
+      String portalName = castNonNull(openPortalMap.remove(deadPortal));
       sendClosePortal(portalName);
       deadPortal.clear();
     }
@@ -2146,7 +2160,9 @@ public class QueryExecutorImpl extends QueryExecutorBase {
             tuples = noResults ? Collections.<Tuple>emptyList() : new ArrayList<Tuple>();
           }
 
-          handler.handleResultRows(currentQuery, fields, tuples, currentPortal);
+          if (fields != null && tuples != null) {
+            handler.handleResultRows(currentQuery, fields, tuples, currentPortal);
+          }
           tuples = null;
           break;
         }
@@ -2161,7 +2177,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
 
           doneAfterRowDescNoData = false;
 
-          ExecuteRequest executeData = pendingExecuteQueue.peekFirst();
+          ExecuteRequest executeData = castNonNull(pendingExecuteQueue.peekFirst());
           SimpleQuery currentQuery = executeData.query;
           Portal currentPortal = executeData.portal;
 
@@ -2205,7 +2221,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
                 "Received resultset tuples, but no field structure for them");
           }
 
-          if (fields != null || tuples != null) {
+          if (fields != null && tuples != null) {
             // There was a resultset.
             handler.handleResultRows(currentQuery, fields, tuples, null);
             tuples = null;
@@ -2247,7 +2263,9 @@ public class QueryExecutorImpl extends QueryExecutorBase {
             if (tuples == null) {
               tuples = new ArrayList<Tuple>();
             }
-            tuples.add(tuple);
+            if (tuple != null) {
+              tuples.add(tuple);
+            }
           }
 
           if (LOGGER.isLoggable(Level.FINEST)) {
@@ -2311,8 +2329,9 @@ public class QueryExecutorImpl extends QueryExecutorBase {
           Field[] fields = receiveFields();
           tuples = new ArrayList<Tuple>();
 
-          SimpleQuery query = pendingDescribePortalQueue.peekFirst();
-          if (!pendingExecuteQueue.isEmpty() && !pendingExecuteQueue.peekFirst().asSimple) {
+          SimpleQuery query = castNonNull(pendingDescribePortalQueue.peekFirst());
+          if (!pendingExecuteQueue.isEmpty()
+              && !castNonNull(pendingExecuteQueue.peekFirst()).asSimple) {
             pendingDescribePortalQueue.removeFirst();
           }
           query.setFields(fields);
@@ -2329,7 +2348,8 @@ public class QueryExecutorImpl extends QueryExecutorBase {
 
         case 'Z': // Ready For Query (eventual response to Sync)
           receiveRFQ();
-          if (!pendingExecuteQueue.isEmpty() && pendingExecuteQueue.peekFirst().asSimple) {
+          if (!pendingExecuteQueue.isEmpty()
+              && castNonNull(pendingExecuteQueue.peekFirst()).asSimple) {
             tuples = null;
             pgStream.clearResultBufferCount();
 
@@ -2441,10 +2461,11 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     // Insert a ResultHandler that turns bare command statuses into empty datasets
     // (if the fetch returns no rows, we see just a CommandStatus..)
     final ResultHandler delegateHandler = handler;
+    final SimpleQuery query = castNonNull(portal.getQuery());
     handler = new ResultHandlerDelegate(delegateHandler) {
       @Override
       public void handleCommandStatus(String status, long updateCount, long insertOID) {
-        handleResultRows(portal.getQuery(), null, new ArrayList<Tuple>(), null);
+        handleResultRows(query, NO_FIELDS, new ArrayList<Tuple>(), null);
       }
     };
 
@@ -2454,7 +2475,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
       processDeadParsedQueries();
       processDeadPortals();
 
-      sendExecute(portal.getQuery(), portal, fetchSize);
+      sendExecute(query, portal, fetchSize);
       sendSync();
 
       processResults(handler, 0);
@@ -2745,7 +2766,7 @@ public class QueryExecutorImpl extends QueryExecutorBase {
     this.timeZone = timeZone;
   }
 
-  public TimeZone getTimeZone() {
+  public /* @Nullable */ TimeZone getTimeZone() {
     return timeZone;
   }
 
diff --git a/src/main/java/org/postgresql/core/v3/SimpleParameterList.java b/src/main/java/org/postgresql/core/v3/SimpleParameterList.java
index 1ce49996c718d5ee596da7d333b4fc8ba01d8096..c6a8986da7040ab497b82bae0079de2c9573a712 100644
--- a/src/main/java/org/postgresql/core/v3/SimpleParameterList.java
+++ b/src/main/java/org/postgresql/core/v3/SimpleParameterList.java
@@ -20,6 +20,10 @@ import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 import org.postgresql.util.StreamWrapper;
 
+// import org.checkerframework.checker.index.qual.NonNegative;
+// import org.checkerframework.checker.index.qual.Positive;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.sql.SQLException;
@@ -39,7 +43,7 @@ class SimpleParameterList implements V3ParameterList {
   private static final byte TEXT = 0;
   private static final byte BINARY = 4;
 
-  SimpleParameterList(int paramCount, TypeTransferModeRegistry transferModeRegistry) {
+  SimpleParameterList(int paramCount, /* @Nullable */ TypeTransferModeRegistry transferModeRegistry) {
     this.paramValues = new Object[paramCount];
     this.paramTypes = new int[paramCount];
     this.encoded = new byte[paramCount][];
@@ -85,11 +89,11 @@ class SimpleParameterList implements V3ParameterList {
     pos = index + 1;
   }
 
-  public int getParameterCount() {
+  public /* @NonNegative */ int getParameterCount() {
     return paramValues.length;
   }
 
-  public int getOutParameterCount() {
+  public /* @NonNegative */ int getOutParameterCount() {
     int count = 0;
     for (int i = 0; i < paramTypes.length; i++) {
       if ((direction(i) & OUT) == OUT) {
@@ -104,7 +108,7 @@ class SimpleParameterList implements V3ParameterList {
 
   }
 
-  public int getInParameterCount() {
+  public /* @NonNegative */ int getInParameterCount() {
     int count = 0;
     for (int i = 0; i < paramTypes.length; i++) {
       if (direction(i) != OUT) {
@@ -114,92 +118,93 @@ class SimpleParameterList implements V3ParameterList {
     return count;
   }
 
-  public void setIntParameter(int index, int value) throws SQLException {
+  public void setIntParameter(/* @Positive */ int index, int value) throws SQLException {
     byte[] data = new byte[4];
     ByteConverter.int4(data, 0, value);
     bind(index, data, Oid.INT4, BINARY);
   }
 
-  public void setLiteralParameter(int index, String value, int oid) throws SQLException {
+  public void setLiteralParameter(/* @Positive */ int index, String value, int oid) throws SQLException {
     bind(index, value, oid, TEXT);
   }
 
-  public void setStringParameter(int index, String value, int oid) throws SQLException {
+  public void setStringParameter(/* @Positive */ int index, String value, int oid) throws SQLException {
     bind(index, value, oid, TEXT);
   }
 
-  public void setBinaryParameter(int index, byte[] value, int oid) throws SQLException {
+  public void setBinaryParameter(/* @Positive */ int index, byte[] value, int oid) throws SQLException {
     bind(index, value, oid, BINARY);
   }
 
   @Override
-  public void setBytea(int index, byte[] data, int offset, int length) throws SQLException {
+  public void setBytea(/* @Positive */ int index, byte[] data, int offset, /* @NonNegative */ int length) throws SQLException {
     bind(index, new StreamWrapper(data, offset, length), Oid.BYTEA, BINARY);
   }
 
   @Override
-  public void setBytea(int index, InputStream stream, int length) throws SQLException {
+  public void setBytea(/* @Positive */ int index, InputStream stream, /* @NonNegative */ int length) throws SQLException {
     bind(index, new StreamWrapper(stream, length), Oid.BYTEA, BINARY);
   }
 
   @Override
-  public void setBytea(int index, InputStream stream) throws SQLException {
+  public void setBytea(/* @Positive */ int index, InputStream stream) throws SQLException {
     bind(index, new StreamWrapper(stream), Oid.BYTEA, BINARY);
   }
 
   @Override
-  public void setBytea(int index, ByteStreamWriter writer) throws SQLException {
+  public void setBytea(/* @Positive */ int index, ByteStreamWriter writer) throws SQLException {
     bind(index, writer, Oid.BYTEA, BINARY);
   }
 
   @Override
-  public void setText(int index, InputStream stream) throws SQLException {
+  public void setText(/* @Positive */ int index, InputStream stream) throws SQLException {
     bind(index, new StreamWrapper(stream), Oid.TEXT, TEXT);
   }
 
   @Override
-  public void setNull(int index, int oid) throws SQLException {
+  public void setNull(/* @Positive */ int index, int oid) throws SQLException {
 
     byte binaryTransfer = TEXT;
 
-    if (transferModeRegistry.useBinaryForReceive(oid)) {
+    if (transferModeRegistry != null && transferModeRegistry.useBinaryForReceive(oid)) {
       binaryTransfer = BINARY;
     }
     bind(index, NULL_OBJECT, oid, binaryTransfer);
   }
 
   @Override
-  public String toString(int index, boolean standardConformingStrings) {
+  public String toString(/* @Positive */ int index, boolean standardConformingStrings) {
     --index;
-    if (paramValues[index] == null) {
+    Object paramValue = paramValues[index];
+    if (paramValue == null) {
       return "?";
-    } else if (paramValues[index] == NULL_OBJECT) {
+    } else if (paramValue == NULL_OBJECT) {
       return "NULL";
     } else if ((flags[index] & BINARY) == BINARY) {
       // handle some of the numeric types
 
       switch (paramTypes[index]) {
         case Oid.INT2:
-          short s = ByteConverter.int2((byte[]) paramValues[index], 0);
+          short s = ByteConverter.int2((byte[]) paramValue, 0);
           return Short.toString(s);
 
         case Oid.INT4:
-          int i = ByteConverter.int4((byte[]) paramValues[index], 0);
+          int i = ByteConverter.int4((byte[]) paramValue, 0);
           return Integer.toString(i);
 
         case Oid.INT8:
-          long l = ByteConverter.int8((byte[]) paramValues[index], 0);
+          long l = ByteConverter.int8((byte[]) paramValue, 0);
           return Long.toString(l);
 
         case Oid.FLOAT4:
-          float f = ByteConverter.float4((byte[]) paramValues[index], 0);
+          float f = ByteConverter.float4((byte[]) paramValue, 0);
           if (Float.isNaN(f)) {
             return "'NaN'::real";
           }
           return Float.toString(f);
 
         case Oid.FLOAT8:
-          double d = ByteConverter.float8((byte[]) paramValues[index], 0);
+          double d = ByteConverter.float8((byte[]) paramValue, 0);
           if (Double.isNaN(d)) {
             return "'NaN'::double precision";
           }
@@ -207,22 +212,22 @@ class SimpleParameterList implements V3ParameterList {
 
         case Oid.UUID:
           String uuid =
-              new UUIDArrayAssistant().buildElement((byte[]) paramValues[index], 0, 16).toString();
+              new UUIDArrayAssistant().buildElement((byte[]) paramValue, 0, 16).toString();
           return "'" + uuid + "'::uuid";
 
         case Oid.POINT:
           PGpoint pgPoint = new PGpoint();
-          pgPoint.setByteValue((byte[]) paramValues[index], 0);
+          pgPoint.setByteValue((byte[]) paramValue, 0);
           return "'" + pgPoint.toString() + "'::point";
 
         case Oid.BOX:
           PGbox pgBox = new PGbox();
-          pgBox.setByteValue((byte[]) paramValues[index], 0);
+          pgBox.setByteValue((byte[]) paramValue, 0);
           return "'" + pgBox.toString() + "'::box";
       }
       return "?";
     } else {
-      String param = paramValues[index].toString();
+      String param = paramValue.toString();
 
       // add room for quotes + potential escaping.
       StringBuilder p = new StringBuilder(3 + (param.length() + 10) / 10 * 11);
@@ -313,7 +318,7 @@ class SimpleParameterList implements V3ParameterList {
   // Package-private V3 accessors
   //
 
-  int getTypeOID(int index) {
+  int getTypeOID(/* @Positive */ int index) {
     return paramTypes[index - 1];
   }
 
@@ -326,7 +331,7 @@ class SimpleParameterList implements V3ParameterList {
     return false;
   }
 
-  void setResolvedType(int index, int oid) {
+  void setResolvedType(/* @Positive */ int index, int oid) {
     // only allow overwriting an unknown value
     if (paramTypes[index - 1] == Oid.UNSPECIFIED) {
       paramTypes[index - 1] = oid;
@@ -336,79 +341,82 @@ class SimpleParameterList implements V3ParameterList {
     }
   }
 
-  boolean isNull(int index) {
+  boolean isNull(/* @Positive */ int index) {
     return (paramValues[index - 1] == NULL_OBJECT);
   }
 
-  boolean isBinary(int index) {
+  boolean isBinary(/* @Positive */ int index) {
     return (flags[index - 1] & BINARY) != 0;
   }
 
-  private byte direction(int index) {
+  private byte direction(/* @Positive */ int index) {
     return (byte) (flags[index] & INOUT);
   }
 
-  int getV3Length(int index) {
+  int getV3Length(/* @Positive */ int index) {
     --index;
 
     // Null?
-    if (paramValues[index] == NULL_OBJECT) {
+    Object value = paramValues[index];
+    if (value == null || value == NULL_OBJECT) {
       throw new IllegalArgumentException("can't getV3Length() on a null parameter");
     }
 
     // Directly encoded?
-    if (paramValues[index] instanceof byte[]) {
-      return ((byte[]) paramValues[index]).length;
+    if (value instanceof byte[]) {
+      return ((byte[]) value).length;
     }
 
     // Binary-format bytea?
-    if (paramValues[index] instanceof StreamWrapper) {
-      return ((StreamWrapper) paramValues[index]).getLength();
+    if (value instanceof StreamWrapper) {
+      return ((StreamWrapper) value).getLength();
     }
 
     // Binary-format bytea?
-    if (paramValues[index] instanceof ByteStreamWriter) {
-      return ((ByteStreamWriter) paramValues[index]).getLength();
+    if (value instanceof ByteStreamWriter) {
+      return ((ByteStreamWriter) value).getLength();
     }
 
     // Already encoded?
-    if (encoded[index] == null) {
+    byte[] encoded = this.encoded[index];
+    if (encoded == null) {
       // Encode value and compute actual length using UTF-8.
-      encoded[index] = Utils.encodeUTF8(paramValues[index].toString());
+      this.encoded[index] = encoded = Utils.encodeUTF8(value.toString());
     }
 
-    return encoded[index].length;
+    return encoded.length;
   }
 
-  void writeV3Value(int index, PGStream pgStream) throws IOException {
+  void writeV3Value(/* @Positive */ int index, PGStream pgStream) throws IOException {
     --index;
 
     // Null?
-    if (paramValues[index] == NULL_OBJECT) {
+    Object paramValue = paramValues[index];
+    if (paramValue == null || paramValue == NULL_OBJECT) {
       throw new IllegalArgumentException("can't writeV3Value() on a null parameter");
     }
 
     // Directly encoded?
-    if (paramValues[index] instanceof byte[]) {
-      pgStream.send((byte[]) paramValues[index]);
+    if (paramValue instanceof byte[]) {
+      pgStream.send((byte[]) paramValue);
       return;
     }
 
     // Binary-format bytea?
-    if (paramValues[index] instanceof StreamWrapper) {
-      streamBytea(pgStream, (StreamWrapper) paramValues[index]);
+    if (paramValue instanceof StreamWrapper) {
+      streamBytea(pgStream, (StreamWrapper) paramValue);
       return;
     }
 
     // Streamed bytea?
-    if (paramValues[index] instanceof ByteStreamWriter) {
-      streamBytea(pgStream, (ByteStreamWriter) paramValues[index]);
+    if (paramValue instanceof ByteStreamWriter) {
+      streamBytea(pgStream, (ByteStreamWriter) paramValue);
       return;
     }
 
     // Encoded string.
     if (encoded[index] == null) {
-      encoded[index] = Utils.encodeUTF8((String) paramValues[index]);
+      encoded[index] = Utils.encodeUTF8((String) paramValue);
     }
     pgStream.send(encoded[index]);
   }
@@ -430,11 +438,11 @@ class SimpleParameterList implements V3ParameterList {
     pos = 0;
   }
 
-  public SimpleParameterList[] getSubparams() {
+  public SimpleParameterList /* @Nullable */ [] getSubparams() {
     return null;
   }
 
-  public Object[] getValues() {
+  public /* @Nullable */ Object[] getValues() {
     return paramValues;
   }
 
@@ -446,7 +454,7 @@ class SimpleParameterList implements V3ParameterList {
     return flags;
   }
 
-  public byte[][] getEncoding() {
+  public byte[] /* @Nullable */ [] getEncoding() {
     return encoded;
   }
 
@@ -488,11 +496,11 @@ class SimpleParameterList implements V3ParameterList {
     return ts.toString();
   }
 
-  private final Object[] paramValues;
+  private final /* @Nullable */ Object[] paramValues;
   private final int[] paramTypes;
   private final byte[] flags;
-  private final byte[][] encoded;
-  private final TypeTransferModeRegistry transferModeRegistry;
+  private final byte[] /* @Nullable */ [] encoded;
+  private final /* @Nullable */ TypeTransferModeRegistry transferModeRegistry;
 
   /**
    * Marker object representing NULL; this distinguishes "parameter never set" from "parameter set
diff --git a/src/main/java/org/postgresql/core/v3/SimpleQuery.java b/src/main/java/org/postgresql/core/v3/SimpleQuery.java
index ad013dca1314b02851305fb171635ca9710a686e..9e0fa22228d35fe71820958a615ef00587e986ad 100644
--- a/src/main/java/org/postgresql/core/v3/SimpleQuery.java
+++ b/src/main/java/org/postgresql/core/v3/SimpleQuery.java
@@ -15,6 +15,8 @@ import org.postgresql.core.SqlCommand;
 import org.postgresql.core.Utils;
 import org.postgresql.jdbc.PgResultSet;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.ref.PhantomReference;
 import java.util.BitSet;
 import java.util.Map;
@@ -35,7 +37,7 @@ class SimpleQuery implements Query {
     this(src.nativeQuery, src.transferModeRegistry, src.sanitiserDisabled);
   }
 
-  SimpleQuery(NativeQuery query, TypeTransferModeRegistry transferModeRegistry,
+  SimpleQuery(NativeQuery query, /* @Nullable */ TypeTransferModeRegistry transferModeRegistry,
       boolean sanitiserDisabled) {
     this.nativeQuery = query;
     this.transferModeRegistry = transferModeRegistry;
@@ -50,7 +52,7 @@ class SimpleQuery implements Query {
     return new SimpleParameterList(getBindCount(), transferModeRegistry);
   }
 
-  public String toString(ParameterList parameters) {
+  public String toString(/* @Nullable */ ParameterList parameters) {
     return nativeQuery.toString(parameters);
   }
 
@@ -62,7 +64,7 @@ class SimpleQuery implements Query {
     unprepare();
   }
 
-  public SimpleQuery[] getSubqueries() {
+  public SimpleQuery /* @Nullable */ [] getSubqueries() {
     return null;
   }
 
@@ -140,11 +142,11 @@ class SimpleQuery implements Query {
     System.arraycopy(paramTypes, 0, this.preparedTypes, 0, paramTypes.length);
   }
 
-  int[] getPrepareTypes() {
+  int /* @Nullable */ [] getPrepareTypes() {
     return preparedTypes;
   }
 
-  String getStatementName() {
+  /* @Nullable */ String getStatementName() {
     return statementName;
   }
 
@@ -204,7 +206,7 @@ class SimpleQuery implements Query {
     return this.unspecifiedParams != null && !this.unspecifiedParams.isEmpty();
   }
 
-  byte[] getEncodedStatementName() {
+  byte /* @Nullable */ [] getEncodedStatementName() {
     return encodedStatementName;
   }
 
@@ -213,7 +215,7 @@ class SimpleQuery implements Query {
    *
    * @param fields The fields that this query will return.
    */
-  void setFields(Field[] fields) {
+  void setFields(Field /* @Nullable */ [] fields) {
     this.fields = fields;
     this.resultSetColumnNameIndexMap = null;
     this.cachedMaxResultRowSize = null;
@@ -227,7 +229,7 @@ class SimpleQuery implements Query {
    *
    * @return the fields that this query will return.
    */
-  Field[] getFields() {
+  Field /* @Nullable */ [] getFields() {
     return fields;
   }
 
@@ -323,10 +325,10 @@ class SimpleQuery implements Query {
     return nativeQuery.bindPositions.length * getBatchSize();
   }
 
-  private Map<String, Integer> resultSetColumnNameIndexMap;
+  private /* @Nullable */ Map<String, Integer> resultSetColumnNameIndexMap;
 
   @Override
-  public Map<String, Integer> getResultSetColumnNameIndexMap() {
+  public /* @Nullable */ Map<String, Integer> getResultSetColumnNameIndexMap() {
     Map<String, Integer> columnPositions = this.resultSetColumnNameIndexMap;
     if (columnPositions == null && fields != null) {
       columnPositions =
@@ -346,25 +348,25 @@ class SimpleQuery implements Query {
 
   private final NativeQuery nativeQuery;
 
-  private final TypeTransferModeRegistry transferModeRegistry;
-  private String statementName;
-  private byte[] encodedStatementName;
+  private final /* @Nullable */ TypeTransferModeRegistry transferModeRegistry;
+  private /* @Nullable */ String statementName;
+  private byte /* @Nullable */ [] encodedStatementName;
   /**
    * The stored fields from previous execution or describe of a prepared statement. Always null for
    * non-prepared statements.
    */
-  private Field[] fields;
+  private Field /* @Nullable */ [] fields;
   private boolean needUpdateFieldFormats;
   private boolean hasBinaryFields;
   private boolean portalDescribed;
   private boolean statementDescribed;
   private final boolean sanitiserDisabled;
-  private PhantomReference<?> cleanupRef;
-  private int[] preparedTypes;
-  private BitSet unspecifiedParams;
+  private /* @Nullable */ PhantomReference<?> cleanupRef;
+  private int /* @Nullable */ [] preparedTypes;
+  private /* @Nullable */ BitSet unspecifiedParams;
   private short deallocateEpoch;
 
-  private Integer cachedMaxResultRowSize;
+  private /* @Nullable */ Integer cachedMaxResultRowSize;
 
   static final SimpleParameterList NO_PARAMETERS = new SimpleParameterList(0, null);
 }
diff --git a/src/main/java/org/postgresql/core/v3/V3ParameterList.java b/src/main/java/org/postgresql/core/v3/V3ParameterList.java
index 028a32585b4315fc38db38b6267ccf7bb745e2f2..42d1a77972641fe42757a801d3fc40c281385709 100644
--- a/src/main/java/org/postgresql/core/v3/V3ParameterList.java
+++ b/src/main/java/org/postgresql/core/v3/V3ParameterList.java
@@ -8,6 +8,8 @@ package org.postgresql.core.v3;
 
 import org.postgresql.core.ParameterList;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 
 /**
@@ -38,23 +40,23 @@ interface V3ParameterList extends ParameterList {
    * @return an array of single-statement parameter lists, or <code>null</code> if this object is
    *         already a single-statement parameter list.
    */
-  SimpleParameterList[] getSubparams();
+  SimpleParameterList /* @Nullable */ [] getSubparams();
 
   /**
    * Return the parameter type information.
    * @return an array of {@link org.postgresql.core.Oid} type information
    */
-  int[] getParamTypes();
+  int /* @Nullable */ [] getParamTypes();
 
   /**
    * Return the flags for each parameter.
    * @return an array of bytes used to store flags.
    */
-  byte[] getFlags();
+  byte /* @Nullable */ [] getFlags();
 
   /**
    * Return the encoding for each parameter.
    * @return nested byte array of bytes with encoding information.
    */
-  byte[][] getEncoding();
+  byte /* @Nullable */ [] /* @Nullable */ [] getEncoding();
 }
diff --git a/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java b/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
index 66fd2f5e5a4f994fa48e247f221eba15a1855c3a..27e751546afb008867fcc7c1c6f7d00ae3fff8b8 100644
--- a/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
+++ b/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
@@ -13,6 +13,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.net.SocketTimeoutException;
 import java.nio.ByteBuffer;
 import java.sql.SQLException;
@@ -64,7 +66,7 @@ public class V3PGReplicationStream implements PGReplicationStream {
   }
 
   @Override
-  public ByteBuffer read() throws SQLException {
+  public /* @Nullable */ ByteBuffer read() throws SQLException {
     checkClose();
 
     ByteBuffer payload = null;
@@ -75,7 +77,7 @@ public class V3PGReplicationStream implements PGReplicationStream {
     return payload;
   }
 
-  public ByteBuffer readPending() throws SQLException {
+  public /* @Nullable */ ByteBuffer readPending() throws SQLException {
     checkClose();
     return readInternal(false);
   }
@@ -116,7 +118,7 @@ public class V3PGReplicationStream implements PGReplicationStream {
     return closeFlag || !copyDual.isActive();
   }
 
-  private ByteBuffer readInternal(boolean block) throws SQLException {
+  private /* @Nullable */ ByteBuffer readInternal(boolean block) throws SQLException {
     boolean updateStatusRequired = false;
     while (copyDual.isActive()) {
 
@@ -153,7 +155,7 @@ public class V3PGReplicationStream implements PGReplicationStream {
     return null;
   }
 
-  private ByteBuffer receiveNextData(boolean block) throws SQLException {
+  private /* @Nullable */ ByteBuffer receiveNextData(boolean block) throws SQLException {
     try {
       byte[] message = copyDual.readFromCopy(block);
       if (message != null) {
diff --git a/src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java b/src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java
index ac229f5daa379d7315d54ea9dccac0f049d33df0..a0800e7b2a3a660122c14d75f0a7bec52341aca9 100644
--- a/src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java
+++ b/src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.core.v3.replication;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.copy.CopyDual;
 import org.postgresql.core.PGStream;
 import org.postgresql.core.QueryExecutor;
@@ -58,7 +60,7 @@ public class V3ReplicationProtocol implements ReplicationProtocol {
     CopyDual copyDual = (CopyDual) queryExecutor.startCopy(query, true);
 
     return new V3PGReplicationStream(
-        copyDual,
+        castNonNull(copyDual),
         options.getStartLSNPosition(),
         options.getStatusInterval(),
         replicationType
diff --git a/src/main/java/org/postgresql/ds/PGPooledConnection.java b/src/main/java/org/postgresql/ds/PGPooledConnection.java
index cef89f335eab06545f6cf1ea92a16fc97f58bceb..b7916f5672dc9b75d1da8f034a99af269a079300 100644
--- a/src/main/java/org/postgresql/ds/PGPooledConnection.java
+++ b/src/main/java/org/postgresql/ds/PGPooledConnection.java
@@ -5,11 +5,15 @@
 
 package org.postgresql.ds;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGConnection;
 import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -37,8 +41,8 @@ import javax.sql.StatementEventListener;
  */
 public class PGPooledConnection implements PooledConnection {
   private final List<ConnectionEventListener> listeners = new LinkedList<ConnectionEventListener>();
-  private Connection con;
-  private ConnectionHandler last;
+  private /* @Nullable */ Connection con;
+  private /* @Nullable */ ConnectionHandler last;
   private final boolean autoCommit;
   private final boolean isXA;
 
@@ -83,7 +87,7 @@ public class PGPooledConnection implements PooledConnection {
   public void close() throws SQLException {
     if (last != null) {
       last.close();
-      if (!con.isClosed()) {
+      if (con != null && !con.isClosed()) {
         if (!con.getAutoCommit()) {
           try {
             con.rollback();
@@ -92,6 +96,9 @@ public class PGPooledConnection implements PooledConnection {
         }
       }
     }
+    if (con == null) {
+      return;
+    }
     try {
       con.close();
     } finally {
@@ -128,31 +135,33 @@ public class PGPooledConnection implements PooledConnection {
       // Package spec section 6.2.3
       if (last != null) {
         last.close();
-        if (!con.getAutoCommit()) {
-          try {
-            con.rollback();
-          } catch (SQLException ignored) {
+        if (con != null) {
+          if (!con.getAutoCommit()) {
+            try {
+              con.rollback();
+            } catch (SQLException ignored) {
+            }
           }
+          con.clearWarnings();
         }
-        con.clearWarnings();
       }
       /*
        * In XA-mode, autocommit is handled in PGXAConnection, because it depends on whether an
        * XA-transaction is open or not
        */
-      if (!isXA) {
+      if (!isXA && con != null) {
         con.setAutoCommit(autoCommit);
       }
     } catch (SQLException sqlException) {
       fireConnectionFatalError(sqlException);
       throw (SQLException) sqlException.fillInStackTrace();
     }
-    ConnectionHandler handler = new ConnectionHandler(con);
+    ConnectionHandler handler = new ConnectionHandler(castNonNull(con));
     last = handler;
 
     Connection proxyCon = (Connection) Proxy.newProxyInstance(getClass().getClassLoader(),
         new Class[]{Connection.class, PGConnection.class}, handler);
-    last.setProxy(proxyCon);
+    handler.setProxy(proxyCon);
     return proxyCon;
   }
 
@@ -188,12 +197,12 @@ public class PGPooledConnection implements PooledConnection {
     }
   }
 
-  protected ConnectionEvent createConnectionEvent(SQLException e) {
-    return new ConnectionEvent(this, e);
+  protected ConnectionEvent createConnectionEvent(/* @Nullable */ SQLException e) {
+    return e == null ? new ConnectionEvent(this) : new ConnectionEvent(this, e);
   }
 
   // Classes we consider fatal.
-  private static String[] fatalClasses = {
+  private static final String[] fatalClasses = {
       "08", // connection error
       "53", // insufficient resources
 
@@ -209,7 +218,7 @@ public class PGPooledConnection implements PooledConnection {
       "XX", // internal error (backend)
   };
 
-  private static boolean isFatalState(String state) {
+  private static boolean isFatalState(/* @Nullable */ String state) {
     if (state == null) {
       // no info, assume fatal
       return true;
@@ -248,8 +257,8 @@ public class PGPooledConnection implements PooledConnection {
    * package.
    */
   private class ConnectionHandler implements InvocationHandler {
-    private Connection con;
-    private Connection proxy; // the Connection the client is currently using, which is a proxy
+    private /* @Nullable */ Connection con;
+    private /* @Nullable */ Connection proxy; // the Connection the client is currently using, which is a proxy
     private boolean automatic = false;
 
     ConnectionHandler(Connection con) {
@@ -257,7 +266,8 @@ public class PGPooledConnection implements PooledConnection {
     }
 
     @Override
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+    @SuppressWarnings("throwing.nullable")
+    public /* @Nullable */ Object invoke(Object proxy, Method method, /* @Nullable */ Object[] args) throws Throwable {
       final String methodName = method.getName();
       // From Object
       if (method.getDeclaringClass() == Object.class) {
@@ -273,6 +283,7 @@ public class PGPooledConnection implements PooledConnection {
         try {
           return method.invoke(con, args);
         } catch (InvocationTargetException e) {
+          // throwing.nullable
           throw e.getTargetException();
         }
       }
@@ -319,17 +330,17 @@ public class PGPooledConnection implements PooledConnection {
       // and check if they're fatal before rethrowing.
       try {
         if (methodName.equals("createStatement")) {
-          Statement st = (Statement) method.invoke(con, args);
+          Statement st = castNonNull((Statement) method.invoke(con, args));
           return Proxy.newProxyInstance(getClass().getClassLoader(),
               new Class[]{Statement.class, org.postgresql.PGStatement.class},
               new StatementHandler(this, st));
         } else if (methodName.equals("prepareCall")) {
-          Statement st = (Statement) method.invoke(con, args);
+          Statement st = castNonNull((Statement) method.invoke(con, args));
           return Proxy.newProxyInstance(getClass().getClassLoader(),
               new Class[]{CallableStatement.class, org.postgresql.PGStatement.class},
               new StatementHandler(this, st));
         } else if (methodName.equals("prepareStatement")) {
-          Statement st = (Statement) method.invoke(con, args);
+          Statement st = castNonNull((Statement) method.invoke(con, args));
           return Proxy.newProxyInstance(getClass().getClassLoader(),
               new Class[]{PreparedStatement.class, org.postgresql.PGStatement.class},
               new StatementHandler(this, st));
@@ -346,7 +357,7 @@ public class PGPooledConnection implements PooledConnection {
     }
 
     Connection getProxy() {
-      return proxy;
+      return castNonNull(proxy);
     }
 
     void setProxy(Connection proxy) {
@@ -377,8 +388,8 @@ public class PGPooledConnection implements PooledConnection {
    * getConnection method.</p>
    */
   private class StatementHandler implements InvocationHandler {
-    private ConnectionHandler con;
-    private Statement st;
+    private /* @Nullable */ ConnectionHandler con;
+    private /* @Nullable */ Statement st;
 
     StatementHandler(ConnectionHandler con, Statement st) {
       this.con = con;
@@ -386,7 +397,9 @@ public class PGPooledConnection implements PooledConnection {
     }
 
     @Override
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+    @SuppressWarnings("throwing.nullable")
+    public /* @Nullable */ Object invoke(Object proxy, Method method, /* @Nullable */ Object[] args)
+        throws Throwable {
       final String methodName = method.getName();
       // From Object
       if (method.getDeclaringClass() == Object.class) {
@@ -420,7 +433,7 @@ public class PGPooledConnection implements PooledConnection {
         throw new PSQLException(GT.tr("Statement has been closed."), PSQLState.OBJECT_NOT_IN_STATE);
       }
       if (methodName.equals("getConnection")) {
-        return con.getProxy(); // the proxied connection, not a physical connection
+        return castNonNull(con).getProxy(); // the proxied connection, not a physical connection
       }
 
       // Delegate the call to the proxied Statement.
diff --git a/src/main/java/org/postgresql/ds/PGPoolingDataSource.java b/src/main/java/org/postgresql/ds/PGPoolingDataSource.java
index 31d8d416a60076a8309dbbf164b12387d4469541..67ab74bcde6255f7726c951f44a739fbbf933cb3 100644
--- a/src/main/java/org/postgresql/ds/PGPoolingDataSource.java
+++ b/src/main/java/org/postgresql/ds/PGPoolingDataSource.java
@@ -5,11 +5,15 @@
 
 package org.postgresql.ds;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.ds.common.BaseDataSource;
 import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.util.Stack;
@@ -62,20 +66,21 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
   protected static ConcurrentMap<String, PGPoolingDataSource> dataSources =
       new ConcurrentHashMap<String, PGPoolingDataSource>();
 
-  public static PGPoolingDataSource getDataSource(String name) {
+  public static /* @Nullable */ PGPoolingDataSource getDataSource(String name) {
     return dataSources.get(name);
   }
 
   // Additional Data Source properties
-  protected String dataSourceName; // Must be protected for subclasses to sync updates to it
+  protected /* @Nullable */ String dataSourceName; // Must be protected for subclasses to sync updates to it
   private int initialConnections = 0;
   private int maxConnections = 0;
   // State variables
   private boolean initialized = false;
-  private Stack<PooledConnection> available = new Stack<PooledConnection>();
-  private Stack<PooledConnection> used = new Stack<PooledConnection>();
-  private Object lock = new Object();
-  private PGConnectionPoolDataSource source;
+  private final Stack<PooledConnection> available = new Stack<PooledConnection>();
+  private final Stack<PooledConnection> used = new Stack<PooledConnection>();
+  private boolean isClosed;
+  private final Object lock = new Object();
+  private /* @Nullable */ PGConnectionPoolDataSource source;
 
   /**
    * Gets a description of this DataSource.
@@ -104,7 +109,7 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
    * @throws IllegalStateException The Database Name cannot be changed after the DataSource has been
    *         used.
    */
-  public void setDatabaseName(String databaseName) {
+  public void setDatabaseName(/* @Nullable */ String databaseName) {
     if (initialized) {
       throw new IllegalStateException(
           "Cannot set Data Source properties after DataSource has been used");
@@ -117,7 +122,7 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
    *
    * @throws IllegalStateException The User cannot be changed after the DataSource has been used.
    */
-  public void setUser(String user) {
+  public void setUser(/* @Nullable */ String user) {
     if (initialized) {
       throw new IllegalStateException(
           "Cannot set Data Source properties after DataSource has been used");
@@ -131,7 +136,7 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
    * @throws IllegalStateException The Password cannot be changed after the DataSource has been
    *         used.
    */
-  public void setPassword(String password) {
+  public void setPassword(/* @Nullable */ String password) {
     if (initialized) {
       throw new IllegalStateException(
           "Cannot set Data Source properties after DataSource has been used");
@@ -217,7 +222,7 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
    *
    * @return name of this DataSource
    */
-  public String getDataSourceName() {
+  public /* @Nullable */ String getDataSourceName() {
     return dataSourceName;
   }
 
@@ -262,7 +267,8 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
    */
   public void initialize() throws SQLException {
     synchronized (lock) {
-      source = createConnectionPool();
+      PGConnectionPoolDataSource source = createConnectionPool();
+      this.source = source;
       try {
         source.initializeFrom(this);
       } catch (Exception e) {
@@ -299,7 +305,8 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
    * @throws SQLException Occurs when no pooled connection is available, and a new physical
    *         connection cannot be created.
    */
-  public Connection getConnection(String user, String password) throws SQLException {
+  public Connection getConnection(/* @Nullable */ String user, /* @Nullable */ String password)
+      throws SQLException {
     // If this is for the default user/password, use a pooled connection
     if (user == null || (user.equals(getUser()) && ((password == null && getPassword() == null)
         || (password != null && password.equals(getPassword()))))) {
@@ -331,29 +338,28 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
    */
   public void close() {
     synchronized (lock) {
+      isClosed = true;
       while (!available.isEmpty()) {
         PooledConnection pci = available.pop();
         try {
           pci.close();
-        } catch (SQLException e) {
+        } catch (SQLException ignored) {
         }
       }
-      available = null;
       while (!used.isEmpty()) {
         PooledConnection pci = used.pop();
         pci.removeConnectionEventListener(connectionEventListener);
         try {
           pci.close();
-        } catch (SQLException e) {
+        } catch (SQLException ignored) {
         }
       }
-      used = null;
     }
     removeStoredDataSource();
   }
 
   protected void removeStoredDataSource() {
-    dataSources.remove(dataSourceName);
+    dataSources.remove(castNonNull(dataSourceName));
   }
 
   protected void addDataSource(String dataSourceName) {
@@ -367,7 +373,7 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
   private Connection getPooledConnection() throws SQLException {
     PooledConnection pc = null;
     synchronized (lock) {
-      if (available == null) {
+      if (isClosed) {
         throw new PSQLException(GT.tr("DataSource has been closed."),
             PSQLState.CONNECTION_DOES_NOT_EXIST);
       }
@@ -378,14 +384,14 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
           break;
         }
         if (maxConnections == 0 || used.size() < maxConnections) {
-          pc = source.getPooledConnection();
+          pc = castNonNull(source).getPooledConnection();
           used.push(pc);
           break;
         } else {
           try {
             // Wake up every second at a minimum
             lock.wait(1000L);
-          } catch (InterruptedException e) {
+          } catch (InterruptedException ignored) {
           }
         }
       }
@@ -398,11 +404,11 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
    * Notified when a pooled connection is closed, or a fatal error occurs on a pooled connection.
    * This is the only way connections are marked as unused.
    */
-  private ConnectionEventListener connectionEventListener = new ConnectionEventListener() {
+  private final ConnectionEventListener connectionEventListener = new ConnectionEventListener() {
     public void connectionClosed(ConnectionEvent event) {
       ((PooledConnection) event.getSource()).removeConnectionEventListener(this);
       synchronized (lock) {
-        if (available == null) {
+        if (isClosed) {
           return; // DataSource has been closed
         }
         boolean removed = used.remove(event.getSource());
@@ -423,7 +429,7 @@ public class PGPoolingDataSource extends BaseDataSource implements DataSource {
     public void connectionErrorOccurred(ConnectionEvent event) {
       ((PooledConnection) event.getSource()).removeConnectionEventListener(this);
       synchronized (lock) {
-        if (available == null) {
+        if (isClosed) {
           return; // DataSource has been closed
         }
         used.remove(event.getSource());
diff --git a/src/main/java/org/postgresql/ds/common/BaseDataSource.java b/src/main/java/org/postgresql/ds/common/BaseDataSource.java
index e4e2bb324bf6f4fa3f9979a0948e1a653f18ff7d..70e601a54554e587d087c7ef93b6f9e056ab0cd1 100644
--- a/src/main/java/org/postgresql/ds/common/BaseDataSource.java
+++ b/src/main/java/org/postgresql/ds/common/BaseDataSource.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.ds.common;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGProperty;
 import org.postgresql.jdbc.AutoSave;
 import org.postgresql.jdbc.PreferQueryMode;
@@ -14,6 +16,8 @@ import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 import org.postgresql.util.URLCoder;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
@@ -46,9 +50,9 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
 
   // Standard properties, defined in the JDBC 2.0 Optional Package spec
   private String[] serverNames = new String[] {"localhost"};
-  private String databaseName = "";
-  private String user;
-  private String password;
+  private /* @Nullable */ String databaseName = "";
+  private /* @Nullable */ String user;
+  private /* @Nullable */ String password;
   private int[] portNumbers = new int[] {0};
 
   // Map for all other properties
@@ -93,7 +97,8 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return A valid database connection.
    * @throws SQLException Occurs when the database connection cannot be established.
    */
-  public Connection getConnection(String user, String password) throws SQLException {
+  public Connection getConnection(/* @Nullable */ String user, /* @Nullable */ String password)
+      throws SQLException {
     try {
       Connection con = DriverManager.getConnection(getUrl(), user, password);
       if (LOGGER.isLoggable(Level.FINE)) {
@@ -112,7 +117,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * This implementation don't use a LogWriter.
    */
   @Override
-  public PrintWriter getLogWriter() {
+  public /* @Nullable */ PrintWriter getLogWriter() {
     return null;
   }
 
@@ -122,7 +127,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param printWriter Not used
    */
   @Override
-  public void setLogWriter(PrintWriter printWriter) {
+  public void setLogWriter(/* @Nullable */ PrintWriter printWriter) {
     // NOOP
   }
 
@@ -164,13 +169,15 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @param serverNames name of the host(s) the PostgreSQL database is running on
    */
-  public void setServerNames(String[] serverNames) {
+  @SuppressWarnings("nullness")
+  public void setServerNames(/* @Nullable */ String /* @Nullable */ [] serverNames) {
     if (serverNames == null || serverNames.length == 0) {
       this.serverNames = new String[] {"localhost"};
     } else {
-      serverNames = Arrays.copyOf(serverNames, serverNames.length);
+      serverNames = serverNames.clone();
       for (int i = 0; i < serverNames.length; i++) {
-        if (serverNames[i] == null || serverNames[i].equals("")) {
+        String serverName = serverNames[i];
+        if (serverName == null || serverName.equals("")) {
           serverNames[i] = "localhost";
         }
       }
@@ -184,7 +191,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @return name of the PostgreSQL database
    */
-  public String getDatabaseName() {
+  public /* @Nullable */ String getDatabaseName() {
     return databaseName;
   }
 
@@ -194,7 +201,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @param databaseName name of the PostgreSQL database
    */
-  public void setDatabaseName(String databaseName) {
+  public void setDatabaseName(/* @Nullable */ String databaseName) {
     this.databaseName = databaseName;
   }
 
@@ -211,7 +218,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @return user to connect as by default
    */
-  public String getUser() {
+  public /* @Nullable */ String getUser() {
     return user;
   }
 
@@ -222,7 +229,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @param user user to connect as by default
    */
-  public void setUser(String user) {
+  public void setUser(/* @Nullable */ String user) {
     this.user = user;
   }
 
@@ -232,7 +239,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @return password to connect with by default
    */
-  public String getPassword() {
+  public /* @Nullable */ String getPassword() {
     return password;
   }
 
@@ -243,7 +250,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @param password password to connect with by default
    */
-  public void setPassword(String password) {
+  public void setPassword(/* @Nullable */ String password) {
     this.password = password;
   }
 
@@ -290,7 +297,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @param portNumbers port(s) which the PostgreSQL server is listening on for TCP/IP
    */
-  public void setPortNumbers(int[] portNumbers) {
+  public void setPortNumbers(int /* @Nullable */ [] portNumbers) {
     if (portNumbers == null || portNumbers.length == 0) {
       portNumbers = new int[] { 0 };
     }
@@ -300,7 +307,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
   /**
    * @return command line options for this connection
    */
-  public String getOptions() {
+  public /* @Nullable */ String getOptions() {
     return PGProperty.OPTIONS.get(properties);
   }
 
@@ -309,7 +316,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    *
    * @param options string to set options to
    */
-  public void setOptions(String options) {
+  public void setOptions(/* @Nullable */ String options) {
     PGProperty.OPTIONS.set(properties, options);
   }
 
@@ -580,7 +587,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return SSL factory class name
    * @see PGProperty#SSL_FACTORY
    */
-  public String getSslfactory() {
+  public /* @Nullable */ String getSslfactory() {
     return PGProperty.SSL_FACTORY.get(properties);
   }
 
@@ -588,7 +595,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return SSL mode
    * @see PGProperty#SSL_MODE
    */
-  public String getSslMode() {
+  public /* @Nullable */ String getSslMode() {
     return PGProperty.SSL_MODE.get(properties);
   }
 
@@ -596,7 +603,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param mode SSL mode
    * @see PGProperty#SSL_MODE
    */
-  public void setSslMode(String mode) {
+  public void setSslMode(/* @Nullable */ String mode) {
     PGProperty.SSL_MODE.set(properties, mode);
   }
 
@@ -604,7 +611,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return SSL mode
    * @see PGProperty#SSL_FACTORY_ARG
    */
-  public String getSslFactoryArg() {
+  public /* @Nullable */ String getSslFactoryArg() {
     return PGProperty.SSL_FACTORY_ARG.get(properties);
   }
 
@@ -612,7 +619,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param arg argument forwarded to SSL factory
    * @see PGProperty#SSL_FACTORY_ARG
    */
-  public void setSslFactoryArg(String arg) {
+  public void setSslFactoryArg(/* @Nullable */ String arg) {
     PGProperty.SSL_FACTORY_ARG.set(properties, arg);
   }
 
@@ -620,7 +627,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return argument forwarded to SSL factory
    * @see PGProperty#SSL_HOSTNAME_VERIFIER
    */
-  public String getSslHostnameVerifier() {
+  public /* @Nullable */ String getSslHostnameVerifier() {
     return PGProperty.SSL_HOSTNAME_VERIFIER.get(properties);
   }
 
@@ -628,7 +635,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param className SSL hostname verifier
    * @see PGProperty#SSL_HOSTNAME_VERIFIER
    */
-  public void setSslHostnameVerifier(String className) {
+  public void setSslHostnameVerifier(/* @Nullable */ String className) {
     PGProperty.SSL_HOSTNAME_VERIFIER.set(properties, className);
   }
 
@@ -636,7 +643,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return className SSL hostname verifier
    * @see PGProperty#SSL_CERT
    */
-  public String getSslCert() {
+  public /* @Nullable */ String getSslCert() {
     return PGProperty.SSL_CERT.get(properties);
   }
 
@@ -644,7 +651,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param file SSL certificate
    * @see PGProperty#SSL_CERT
    */
-  public void setSslCert(String file) {
+  public void setSslCert(/* @Nullable */ String file) {
     PGProperty.SSL_CERT.set(properties, file);
   }
 
@@ -652,7 +659,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return SSL certificate
    * @see PGProperty#SSL_KEY
    */
-  public String getSslKey() {
+  public /* @Nullable */ String getSslKey() {
     return PGProperty.SSL_KEY.get(properties);
   }
 
@@ -660,7 +667,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param file SSL key
    * @see PGProperty#SSL_KEY
    */
-  public void setSslKey(String file) {
+  public void setSslKey(/* @Nullable */ String file) {
     PGProperty.SSL_KEY.set(properties, file);
   }
 
@@ -668,7 +675,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return SSL root certificate
    * @see PGProperty#SSL_ROOT_CERT
    */
-  public String getSslRootCert() {
+  public /* @Nullable */ String getSslRootCert() {
     return PGProperty.SSL_ROOT_CERT.get(properties);
   }
 
@@ -676,7 +683,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param file SSL root certificate
    * @see PGProperty#SSL_ROOT_CERT
    */
-  public void setSslRootCert(String file) {
+  public void setSslRootCert(/* @Nullable */ String file) {
     PGProperty.SSL_ROOT_CERT.set(properties, file);
   }
 
@@ -684,7 +691,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return SSL password
    * @see PGProperty#SSL_PASSWORD
    */
-  public String getSslPassword() {
+  public /* @Nullable */ String getSslPassword() {
     return PGProperty.SSL_PASSWORD.get(properties);
   }
 
@@ -692,7 +699,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param password SSL password
    * @see PGProperty#SSL_PASSWORD
    */
-  public void setSslPassword(String password) {
+  public void setSslPassword(/* @Nullable */ String password) {
     PGProperty.SSL_PASSWORD.set(properties, password);
   }
 
@@ -700,7 +707,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return SSL password callback
    * @see PGProperty#SSL_PASSWORD_CALLBACK
    */
-  public String getSslPasswordCallback() {
+  public /* @Nullable */ String getSslPasswordCallback() {
     return PGProperty.SSL_PASSWORD_CALLBACK.get(properties);
   }
 
@@ -708,7 +715,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param className SSL password callback class name
    * @see PGProperty#SSL_PASSWORD_CALLBACK
    */
-  public void setSslPasswordCallback(String className) {
+  public void setSslPasswordCallback(/* @Nullable */ String className) {
     PGProperty.SSL_PASSWORD_CALLBACK.set(properties, className);
   }
 
@@ -716,7 +723,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param applicationName application name
    * @see PGProperty#APPLICATION_NAME
    */
-  public void setApplicationName(String applicationName) {
+  public void setApplicationName(/* @Nullable */ String applicationName) {
     PGProperty.APPLICATION_NAME.set(properties, applicationName);
   }
 
@@ -725,14 +732,14 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @see PGProperty#APPLICATION_NAME
    */
   public String getApplicationName() {
-    return PGProperty.APPLICATION_NAME.get(properties);
+    return castNonNull(PGProperty.APPLICATION_NAME.get(properties));
   }
 
   /**
    * @param targetServerType target server type
    * @see PGProperty#TARGET_SERVER_TYPE
    */
-  public void setTargetServerType(String targetServerType) {
+  public void setTargetServerType(/* @Nullable */ String targetServerType) {
     PGProperty.TARGET_SERVER_TYPE.set(properties, targetServerType);
   }
 
@@ -741,7 +748,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @see PGProperty#TARGET_SERVER_TYPE
    */
   public String getTargetServerType() {
-    return PGProperty.TARGET_SERVER_TYPE.get(properties);
+    return castNonNull(PGProperty.TARGET_SERVER_TYPE.get(properties));
   }
 
   /**
@@ -812,7 +819,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param oidList list of OIDs that are allowed to use binary transfer
    * @see PGProperty#BINARY_TRANSFER_ENABLE
    */
-  public void setBinaryTransferEnable(String oidList) {
+  public void setBinaryTransferEnable(/* @Nullable */ String oidList) {
     PGProperty.BINARY_TRANSFER_ENABLE.set(properties, oidList);
   }
 
@@ -821,14 +828,14 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @see PGProperty#BINARY_TRANSFER_ENABLE
    */
   public String getBinaryTransferEnable() {
-    return PGProperty.BINARY_TRANSFER_ENABLE.get(properties);
+    return castNonNull(PGProperty.BINARY_TRANSFER_ENABLE.get(properties));
   }
 
   /**
    * @param oidList list of OIDs that are not allowed to use binary transfer
    * @see PGProperty#BINARY_TRANSFER_DISABLE
    */
-  public void setBinaryTransferDisable(String oidList) {
+  public void setBinaryTransferDisable(/* @Nullable */ String oidList) {
     PGProperty.BINARY_TRANSFER_DISABLE.set(properties, oidList);
   }
 
@@ -837,14 +844,14 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @see PGProperty#BINARY_TRANSFER_DISABLE
    */
   public String getBinaryTransferDisable() {
-    return PGProperty.BINARY_TRANSFER_DISABLE.get(properties);
+    return castNonNull(PGProperty.BINARY_TRANSFER_DISABLE.get(properties));
   }
 
   /**
    * @return string type
    * @see PGProperty#STRING_TYPE
    */
-  public String getStringType() {
+  public /* @Nullable */ String getStringType() {
     return PGProperty.STRING_TYPE.get(properties);
   }
 
@@ -852,7 +859,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param stringType string type
    * @see PGProperty#STRING_TYPE
    */
-  public void setStringType(String stringType) {
+  public void setStringType(/* @Nullable */ String stringType) {
     PGProperty.STRING_TYPE.set(properties, stringType);
   }
 
@@ -884,7 +891,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return current schema
    * @see PGProperty#CURRENT_SCHEMA
    */
-  public String getCurrentSchema() {
+  public /* @Nullable */ String getCurrentSchema() {
     return PGProperty.CURRENT_SCHEMA.get(properties);
   }
 
@@ -892,7 +899,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param currentSchema current schema
    * @see PGProperty#CURRENT_SCHEMA
    */
-  public void setCurrentSchema(String currentSchema) {
+  public void setCurrentSchema(/* @Nullable */ String currentSchema) {
     PGProperty.CURRENT_SCHEMA.set(properties, currentSchema);
   }
 
@@ -917,14 +924,14 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @see PGProperty#READ_ONLY_MODE
    */
   public String getReadOnlyMode() {
-    return PGProperty.READ_ONLY_MODE.get(properties);
+    return castNonNull(PGProperty.READ_ONLY_MODE.get(properties));
   }
 
   /**
    * @param mode the behavior when set read only
    * @see PGProperty#READ_ONLY_MODE
    */
-  public void setReadOnlyMode(String mode) {
+  public void setReadOnlyMode(/* @Nullable */ String mode) {
     PGProperty.READ_ONLY_MODE.set(properties, mode);
   }
 
@@ -964,7 +971,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return assumed minimal server version
    * @see PGProperty#ASSUME_MIN_SERVER_VERSION
    */
-  public String getAssumeMinServerVersion() {
+  public /* @Nullable */ String getAssumeMinServerVersion() {
     return PGProperty.ASSUME_MIN_SERVER_VERSION.get(properties);
   }
 
@@ -972,7 +979,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param minVersion assumed minimal server version
    * @see PGProperty#ASSUME_MIN_SERVER_VERSION
    */
-  public void setAssumeMinServerVersion(String minVersion) {
+  public void setAssumeMinServerVersion(/* @Nullable */ String minVersion) {
     PGProperty.ASSUME_MIN_SERVER_VERSION.set(properties, minVersion);
   }
 
@@ -980,7 +987,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return JAAS application name
    * @see PGProperty#JAAS_APPLICATION_NAME
    */
-  public String getJaasApplicationName() {
+  public /* @Nullable */ String getJaasApplicationName() {
     return PGProperty.JAAS_APPLICATION_NAME.get(properties);
   }
 
@@ -988,7 +995,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param name JAAS application name
    * @see PGProperty#JAAS_APPLICATION_NAME
    */
-  public void setJaasApplicationName(String name) {
+  public void setJaasApplicationName(/* @Nullable */ String name) {
     PGProperty.JAAS_APPLICATION_NAME.set(properties, name);
   }
 
@@ -1012,7 +1019,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return Kerberos server name
    * @see PGProperty#KERBEROS_SERVER_NAME
    */
-  public String getKerberosServerName() {
+  public /* @Nullable */ String getKerberosServerName() {
     return PGProperty.KERBEROS_SERVER_NAME.get(properties);
   }
 
@@ -1020,7 +1027,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param serverName Kerberos server name
    * @see PGProperty#KERBEROS_SERVER_NAME
    */
-  public void setKerberosServerName(String serverName) {
+  public void setKerberosServerName(/* @Nullable */ String serverName) {
     PGProperty.KERBEROS_SERVER_NAME.set(properties, serverName);
   }
 
@@ -1044,7 +1051,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return GSS mode: auto, sspi, or gssapi
    * @see PGProperty#GSS_LIB
    */
-  public String getGssLib() {
+  public /* @Nullable */ String getGssLib() {
     return PGProperty.GSS_LIB.get(properties);
   }
 
@@ -1052,15 +1059,31 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param lib GSS mode: auto, sspi, or gssapi
    * @see PGProperty#GSS_LIB
    */
-  public void setGssLib(String lib) {
+  public void setGssLib(/* @Nullable */ String lib) {
     PGProperty.GSS_LIB.set(properties, lib);
   }
 
+  /**
+   *
+   * @return GSS encryption mode: disable, prefer or require
+   */
+  public String getGssEncMode() {
+    return castNonNull(PGProperty.GSS_ENC_MODE.get(properties));
+  }
+
+  /**
+   *
+   * @param mode encryption mode: disable, prefer or require
+   */
+  public void setGssEncMode(/* @Nullable */ String mode) {
+    PGProperty.GSS_ENC_MODE.set(properties, mode);
+  }
+
   /**
    * @return SSPI service class
    * @see PGProperty#SSPI_SERVICE_CLASS
    */
-  public String getSspiServiceClass() {
+  public /* @Nullable */ String getSspiServiceClass() {
     return PGProperty.SSPI_SERVICE_CLASS.get(properties);
   }
 
@@ -1068,7 +1091,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param serviceClass SSPI service class
    * @see PGProperty#SSPI_SERVICE_CLASS
    */
-  public void setSspiServiceClass(String serviceClass) {
+  public void setSspiServiceClass(/* @Nullable */ String serviceClass) {
     PGProperty.SSPI_SERVICE_CLASS.set(properties, serviceClass);
   }
 
@@ -1092,7 +1115,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return socket factory class name
    * @see PGProperty#SOCKET_FACTORY
    */
-  public String getSocketFactory() {
+  public /* @Nullable */ String getSocketFactory() {
     return PGProperty.SOCKET_FACTORY.get(properties);
   }
 
@@ -1100,7 +1123,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param socketFactoryClassName socket factory class name
    * @see PGProperty#SOCKET_FACTORY
    */
-  public void setSocketFactory(String socketFactoryClassName) {
+  public void setSocketFactory(/* @Nullable */ String socketFactoryClassName) {
     PGProperty.SOCKET_FACTORY.set(properties, socketFactoryClassName);
   }
 
@@ -1108,7 +1131,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return socket factory argument
    * @see PGProperty#SOCKET_FACTORY_ARG
    */
-  public String getSocketFactoryArg() {
+  public /* @Nullable */ String getSocketFactoryArg() {
     return PGProperty.SOCKET_FACTORY_ARG.get(properties);
   }
 
@@ -1116,7 +1139,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param socketFactoryArg socket factory argument
    * @see PGProperty#SOCKET_FACTORY_ARG
    */
-  public void setSocketFactoryArg(String socketFactoryArg) {
+  public void setSocketFactoryArg(/* @Nullable */ String socketFactoryArg) {
     PGProperty.SOCKET_FACTORY_ARG.set(properties, socketFactoryArg);
   }
 
@@ -1124,7 +1147,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param replication set to 'database' for logical replication or 'true' for physical replication
    * @see PGProperty#REPLICATION
    */
-  public void setReplication(String replication) {
+  public void setReplication(/* @Nullable */ String replication) {
     PGProperty.REPLICATION.set(properties, replication);
   }
 
@@ -1133,14 +1156,14 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @see PGProperty#ESCAPE_SYNTAX_CALL_MODE
    */
   public String getEscapeSyntaxCallMode() {
-    return PGProperty.ESCAPE_SYNTAX_CALL_MODE.get(properties);
+    return castNonNull(PGProperty.ESCAPE_SYNTAX_CALL_MODE.get(properties));
   }
 
   /**
    * @param callMode the call mode to use for JDBC escape call syntax
    * @see PGProperty#ESCAPE_SYNTAX_CALL_MODE
    */
-  public void setEscapeSyntaxCallMode(String callMode) {
+  public void setEscapeSyntaxCallMode(/* @Nullable */ String callMode) {
     PGProperty.ESCAPE_SYNTAX_CALL_MODE.set(properties, callMode);
   }
 
@@ -1148,7 +1171,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return null, 'database', or 'true
    * @see PGProperty#REPLICATION
    */
-  public String getReplication() {
+  public /* @Nullable */ String getReplication() {
     return PGProperty.REPLICATION.get(properties);
   }
 
@@ -1156,7 +1179,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return Logger Level of the JDBC Driver
    * @see PGProperty#LOGGER_LEVEL
    */
-  public String getLoggerLevel() {
+  public /* @Nullable */ String getLoggerLevel() {
     return PGProperty.LOGGER_LEVEL.get(properties);
   }
 
@@ -1164,7 +1187,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param loggerLevel of the JDBC Driver
    * @see PGProperty#LOGGER_LEVEL
    */
-  public void setLoggerLevel(String loggerLevel) {
+  public void setLoggerLevel(/* @Nullable */ String loggerLevel) {
     PGProperty.LOGGER_LEVEL.set(properties, loggerLevel);
   }
 
@@ -1172,7 +1195,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @return File output of the Logger.
    * @see PGProperty#LOGGER_FILE
    */
-  public String getLoggerFile() {
+  public /* @Nullable */ String getLoggerFile() {
     ExpressionProperties exprProps = new ExpressionProperties(properties, System.getProperties());
     return PGProperty.LOGGER_FILE.get(exprProps);
   }
@@ -1181,7 +1204,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @param loggerFile File output of the Logger.
    * @see PGProperty#LOGGER_LEVEL
    */
-  public void setLoggerFile(String loggerFile) {
+  public void setLoggerFile(/* @Nullable */ String loggerFile) {
     PGProperty.LOGGER_FILE.set(properties, loggerFile);
   }
 
@@ -1202,7 +1225,10 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
         url.append(":").append(portNumbers[i]);
       }
     }
-    url.append("/").append(URLCoder.encode(databaseName));
+    url.append("/");
+    if (databaseName != null) {
+      url.append(URLCoder.encode(databaseName));
+    }
 
     StringBuilder query = new StringBuilder(100);
     for (PGProperty property : PGProperty.values()) {
@@ -1212,7 +1238,8 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
         }
         query.append(property.getName());
         query.append("=");
-        query.append(URLCoder.encode(property.get(properties)));
+        String value = castNonNull(property.get(properties));
+        query.append(URLCoder.encode(value));
       }
     }
 
@@ -1262,7 +1289,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
     setUrl(url);
   }
 
-  public String getProperty(String name) throws SQLException {
+  public /* @Nullable */ String getProperty(String name) throws SQLException {
     PGProperty pgProperty = PGProperty.forName(name);
     if (pgProperty != null) {
       return getProperty(pgProperty);
@@ -1272,7 +1299,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
     }
   }
 
-  public void setProperty(String name, String value) throws SQLException {
+  public void setProperty(String name, /* @Nullable */ String value) throws SQLException {
     PGProperty pgProperty = PGProperty.forName(name);
     if (pgProperty != null) {
       setProperty(pgProperty, value);
@@ -1282,12 +1309,14 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
     }
   }
 
-  public String getProperty(PGProperty property) {
+  public /* @Nullable */ String getProperty(PGProperty property) {
     return property.get(properties);
   }
 
-  public void setProperty(PGProperty property, String value) {
+  public void setProperty(PGProperty property, /* @Nullable */ String value) {
     if (value == null) {
+      // TODO: this is not consistent with PGProperty.PROPERTY.set(prop, null)
+      // PGProperty removes an entry for put(null) call, however here we just ignore null
       return;
     }
     switch (property) {
@@ -1360,7 +1389,8 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
 
     for (PGProperty property : PGProperty.values()) {
       if (property.isPresent(properties)) {
-        ref.add(new StringRefAddr(property.getName(), property.get(properties)));
+        String value = castNonNull(property.get(properties));
+        ref.add(new StringRefAddr(property.getName(), value));
       }
     }
 
@@ -1384,14 +1414,15 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
     } else {
       setPortNumbers(null);
     }
-    setServerNames(getReferenceProperty(ref, "serverName").split(","));
+    String serverName = castNonNull(getReferenceProperty(ref, "serverName"));
+    setServerNames(serverName.split(","));
 
     for (PGProperty property : PGProperty.values()) {
       setProperty(property, getReferenceProperty(ref, property.getName()));
     }
   }
 
-  private static String getReferenceProperty(Reference ref, String propertyName) {
+  private static /* @Nullable */ String getReferenceProperty(Reference ref, String propertyName) {
     RefAddr addr = ref.get(propertyName);
     if (addr == null) {
       return null;
@@ -1434,7 +1465,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @see PGProperty#PREFER_QUERY_MODE
    */
   public PreferQueryMode getPreferQueryMode() {
-    return PreferQueryMode.of(PGProperty.PREFER_QUERY_MODE.get(properties));
+    return PreferQueryMode.of(castNonNull(PGProperty.PREFER_QUERY_MODE.get(properties)));
   }
 
   /**
@@ -1450,7 +1481,7 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
    * @see PGProperty#AUTOSAVE
    */
   public AutoSave getAutosave() {
-    return AutoSave.of(PGProperty.AUTOSAVE.get(properties));
+    return AutoSave.of(castNonNull(PGProperty.AUTOSAVE.get(properties)));
   }
 
   /**
@@ -1511,11 +1542,11 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
     PGProperty.HIDE_UNPRIVILEGED_OBJECTS.set(properties, hideUnprivileged);
   }
 
-  public String getMaxResultBuffer() {
+  public /* @Nullable */ String getMaxResultBuffer() {
     return PGProperty.MAX_RESULT_BUFFER.get(properties);
   }
 
-  public void setMaxResultBuffer(String maxResultBuffer) {
+  public void setMaxResultBuffer(/* @Nullable */ String maxResultBuffer) {
     PGProperty.MAX_RESULT_BUFFER.set(properties, maxResultBuffer);
   }
 
@@ -1527,10 +1558,10 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
   }
 
   public String getXmlFactoryFactory() {
-    return PGProperty.XML_FACTORY_FACTORY.get(properties);
+    return castNonNull(PGProperty.XML_FACTORY_FACTORY.get(properties));
   }
 
-  public void setXmlFactoryFactory(String xmlFactoryFactory) {
+  public void setXmlFactoryFactory(/* @Nullable */ String xmlFactoryFactory) {
     PGProperty.XML_FACTORY_FACTORY.set(properties, xmlFactoryFactory);
   }
 
@@ -1543,63 +1574,63 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
     return getSsl();
   }
 
-  public String getSslfactoryarg() {
+  public /* @Nullable */ String getSslfactoryarg() {
     return getSslFactoryArg();
   }
 
-  public void setSslfactoryarg(final String arg) {
+  public void setSslfactoryarg(final /* @Nullable */ String arg) {
     setSslFactoryArg(arg);
   }
 
-  public String getSslcert() {
+  public /* @Nullable */ String getSslcert() {
     return getSslCert();
   }
 
-  public void setSslcert(final String file) {
+  public void setSslcert(final /* @Nullable */ String file) {
     setSslCert(file);
   }
 
-  public String getSslmode() {
+  public /* @Nullable */ String getSslmode() {
     return getSslMode();
   }
 
-  public void setSslmode(final String mode) {
+  public void setSslmode(final /* @Nullable */ String mode) {
     setSslMode(mode);
   }
 
-  public String getSslhostnameverifier() {
+  public /* @Nullable */ String getSslhostnameverifier() {
     return getSslHostnameVerifier();
   }
 
-  public void setSslhostnameverifier(final String className) {
+  public void setSslhostnameverifier(final /* @Nullable */ String className) {
     setSslHostnameVerifier(className);
   }
 
-  public String getSslkey() {
+  public /* @Nullable */ String getSslkey() {
     return getSslKey();
   }
 
-  public void setSslkey(final String file) {
+  public void setSslkey(final /* @Nullable */ String file) {
     setSslKey(file);
   }
 
-  public String getSslrootcert() {
+  public /* @Nullable */ String getSslrootcert() {
     return getSslRootCert();
   }
 
-  public void setSslrootcert(final String file) {
+  public void setSslrootcert(final /* @Nullable */ String file) {
     setSslRootCert(file);
   }
 
-  public String getSslpasswordcallback() {
+  public /* @Nullable */ String getSslpasswordcallback() {
     return getSslPasswordCallback();
   }
 
-  public void setSslpasswordcallback(final String className) {
+  public void setSslpasswordcallback(final /* @Nullable */ String className) {
     setSslPasswordCallback(className);
   }
 
-  public String getSslpassword() {
+  public /* @Nullable */ String getSslpassword() {
     return getSslPassword();
   }
 
diff --git a/src/main/java/org/postgresql/ds/common/PGObjectFactory.java b/src/main/java/org/postgresql/ds/common/PGObjectFactory.java
index 66c54bc2e42fd13cc637c530c59c7ba3b748ca6f..2a45bc1da41ded828542416ba619d67d390c0fbe 100644
--- a/src/main/java/org/postgresql/ds/common/PGObjectFactory.java
+++ b/src/main/java/org/postgresql/ds/common/PGObjectFactory.java
@@ -8,6 +8,9 @@ package org.postgresql.ds.common;
 import org.postgresql.ds.PGConnectionPoolDataSource;
 import org.postgresql.ds.PGPoolingDataSource;
 import org.postgresql.ds.PGSimpleDataSource;
+import org.postgresql.util.internal.Nullness;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
 
 import java.util.Hashtable;
 
@@ -30,7 +33,7 @@ public class PGObjectFactory implements ObjectFactory {
   /**
    * Dereferences a PostgreSQL DataSource. Other types of references are ignored.
    */
-  public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+  public /* @Nullable */ Object getObjectInstance(Object obj, Name name, Context nameCtx,
       Hashtable<?, ?> environment) throws Exception {
     Reference ref = (Reference) obj;
     String className = ref.getClassName();
@@ -54,7 +57,7 @@ public class PGObjectFactory implements ObjectFactory {
 
   private Object loadPoolingDataSource(Reference ref) {
     // If DataSource exists, return it
-    String name = getProperty(ref, "dataSourceName");
+    String name = Nullness.castNonNull(getProperty(ref, "dataSourceName"));
     PGPoolingDataSource pds = PGPoolingDataSource.getDataSource(name);
     if (pds != null) {
       return pds;
@@ -90,7 +93,7 @@ public class PGObjectFactory implements ObjectFactory {
     return ds;
   }
 
-  protected String getProperty(Reference ref, String s) {
+  protected /* @Nullable */ String getProperty(Reference ref, String s) {
     RefAddr addr = ref.get(s);
     if (addr == null) {
       return null;
diff --git a/src/main/java/org/postgresql/fastpath/Fastpath.java b/src/main/java/org/postgresql/fastpath/Fastpath.java
index 445af18a498cc27628c9be932922a6268c415540..bfa065b4794715b386fe81456769f131efd6756a 100644
--- a/src/main/java/org/postgresql/fastpath/Fastpath.java
+++ b/src/main/java/org/postgresql/fastpath/Fastpath.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.fastpath;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseConnection;
 import org.postgresql.core.ParameterList;
 import org.postgresql.core.QueryExecutor;
@@ -13,6 +15,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.util.HashMap;
@@ -66,7 +70,8 @@ public class Fastpath {
    * @deprecated please use {@link #fastpath(int, FastpathArg[])}
    */
   @Deprecated
-  public Object fastpath(int fnId, boolean resultType, FastpathArg[] args) throws SQLException {
+  public /* @Nullable */ Object fastpath(int fnId, boolean resultType, FastpathArg[] args)
+      throws SQLException {
     // Run it.
     byte[] returnValue = fastpath(fnId, args);
 
@@ -94,7 +99,7 @@ public class Fastpath {
    * @return null if no data, byte[] otherwise
    * @throws SQLException if a database-access error occurs.
    */
-  public byte[] fastpath(int fnId, FastpathArg[] args) throws SQLException {
+  public byte /* @Nullable */ [] fastpath(int fnId, FastpathArg[] args) throws SQLException {
     // Turn fastpath array into a parameter list.
     ParameterList params = executor.createFastpathParameters(args.length);
     for (int i = 0; i < args.length; ++i) {
@@ -119,7 +124,8 @@ public class Fastpath {
    *             {@link #getLong(String, FastpathArg[])} if you expect a numeric one
    */
   @Deprecated
-  public Object fastpath(String name, boolean resulttype, FastpathArg[] args) throws SQLException {
+  public /* @Nullable */ Object fastpath(String name, boolean resulttype, FastpathArg[] args)
+      throws SQLException {
     connection.getLogger().log(Level.FINEST, "Fastpath: calling {0}", name);
     return fastpath(getID(name), resulttype, args);
   }
@@ -141,7 +147,7 @@ public class Fastpath {
    * @throws SQLException if name is unknown or if a database-access error occurs.
    * @see org.postgresql.largeobject.LargeObject
    */
-  public byte[] fastpath(String name, FastpathArg[] args) throws SQLException {
+  public byte /* @Nullable */ [] fastpath(String name, FastpathArg[] args) throws SQLException {
     connection.getLogger().log(Level.FINEST, "Fastpath: calling {0}", name);
     return fastpath(getID(name), args);
   }
@@ -221,7 +227,7 @@ public class Fastpath {
    * @return byte[] array containing result
    * @throws SQLException if a database-access error occurs or no result
    */
-  public byte[] getData(String name, FastpathArg[] args) throws SQLException {
+  public byte /* @Nullable */ [] getData(String name, FastpathArg[] args) throws SQLException {
     return fastpath(name, args);
   }
 
@@ -269,7 +275,7 @@ public class Fastpath {
    */
   public void addFunctions(ResultSet rs) throws SQLException {
     while (rs.next()) {
-      func.put(rs.getString(1), rs.getInt(2));
+      func.put(castNonNull(rs.getString(1)), rs.getInt(2));
     }
   }
 
diff --git a/src/main/java/org/postgresql/geometric/PGbox.java b/src/main/java/org/postgresql/geometric/PGbox.java
index d2c40a6cf00d17e4388a429eaf2803ca360b6f1e..75d15d89dd1769c055d01b5c2a16173eb0045a7c 100644
--- a/src/main/java/org/postgresql/geometric/PGbox.java
+++ b/src/main/java/org/postgresql/geometric/PGbox.java
@@ -12,6 +12,8 @@ import org.postgresql.util.PGtokenizer;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 
@@ -50,6 +52,7 @@ public class PGbox extends PGobject implements PGBinaryObject, Serializable, Clo
    * @param s Box definition in PostgreSQL syntax
    * @throws SQLException if definition is invalid
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGbox(String s) throws SQLException {
     this();
     setValue(s);
@@ -59,7 +62,7 @@ public class PGbox extends PGobject implements PGBinaryObject, Serializable, Clo
    * Required constructor.
    */
   public PGbox() {
-    setType("box");
+    type = "box";
   }
 
   /**
@@ -96,7 +99,7 @@ public class PGbox extends PGobject implements PGBinaryObject, Serializable, Clo
    * @param obj Object to compare with
    * @return true if the two boxes are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj instanceof PGbox) {
       PGbox p = (PGbox) obj;
 
diff --git a/src/main/java/org/postgresql/geometric/PGcircle.java b/src/main/java/org/postgresql/geometric/PGcircle.java
index 9f3f6802ef16273532b77c94d002335b7613f0a5..0cface2b910b316bcfcf0eab110ba2e2ee118ce8 100644
--- a/src/main/java/org/postgresql/geometric/PGcircle.java
+++ b/src/main/java/org/postgresql/geometric/PGcircle.java
@@ -11,6 +11,8 @@ import org.postgresql.util.PGtokenizer;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 
@@ -21,7 +23,7 @@ public class PGcircle extends PGobject implements Serializable, Cloneable {
   /**
    * This is the center point.
    */
-  public PGpoint center;
+  public /* @Nullable */ PGpoint center;
 
   /**
    * This is the radius.
@@ -51,6 +53,7 @@ public class PGcircle extends PGobject implements Serializable, Cloneable {
    * @param s definition of the circle in PostgreSQL's syntax.
    * @throws SQLException on conversion failure
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGcircle(String s) throws SQLException {
     this();
     setValue(s);
@@ -60,7 +63,7 @@ public class PGcircle extends PGobject implements Serializable, Cloneable {
    * This constructor is used by the driver.
    */
   public PGcircle() {
-    setType("circle");
+    type = "circle";
   }
 
   /**
@@ -88,17 +91,21 @@ public class PGcircle extends PGobject implements Serializable, Cloneable {
    * @param obj Object to compare with
    * @return true if the two circles are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj instanceof PGcircle) {
       PGcircle p = (PGcircle) obj;
-      return p.center.equals(center) && p.radius == radius;
+      return p.radius == radius && equals(p.center, center);
     }
     return false;
   }
 
   public int hashCode() {
-    long v = Double.doubleToLongBits(radius);
-    return (int) (center.hashCode() ^ v ^ (v >>> 32));
+    long bits = Double.doubleToLongBits(radius);
+    int v = (int)(bits ^ (bits >>> 32));
+    if (center != null) {
+      v = v * 31 + center.hashCode();
+    }
+    return v;
   }
 
   public Object clone() throws CloneNotSupportedException {
diff --git a/src/main/java/org/postgresql/geometric/PGline.java b/src/main/java/org/postgresql/geometric/PGline.java
index 5a82eb507305e003675f357e8ba6cd6df4d600c6..f0904004d7428ba17ccccf4ca9b3c5b8e6b8ef6c 100644
--- a/src/main/java/org/postgresql/geometric/PGline.java
+++ b/src/main/java/org/postgresql/geometric/PGline.java
@@ -11,6 +11,8 @@ import org.postgresql.util.PGtokenizer;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 
@@ -83,6 +85,7 @@ public class PGline extends PGobject implements Serializable, Cloneable {
    * @param s definition of the line in PostgreSQL's syntax.
    * @throws SQLException on conversion failure
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGline(String s) throws SQLException {
     this();
     setValue(s);
@@ -92,7 +95,7 @@ public class PGline extends PGobject implements Serializable, Cloneable {
    * required by the driver.
    */
   public PGline() {
-    setType("line");
+    type = "line";
   }
 
   /**
@@ -128,7 +131,7 @@ public class PGline extends PGobject implements Serializable, Cloneable {
    * @param obj Object to compare with
    * @return true if the two lines are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (this == obj) {
       return true;
     }
diff --git a/src/main/java/org/postgresql/geometric/PGlseg.java b/src/main/java/org/postgresql/geometric/PGlseg.java
index 729ae28943a98616093fefdd90235930aa6bdedb..f8654f16e0fde79224505e5740422ab9974d8435 100644
--- a/src/main/java/org/postgresql/geometric/PGlseg.java
+++ b/src/main/java/org/postgresql/geometric/PGlseg.java
@@ -11,6 +11,8 @@ import org.postgresql.util.PGtokenizer;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 
@@ -47,6 +49,7 @@ public class PGlseg extends PGobject implements Serializable, Cloneable {
    * @param s definition of the line segment in PostgreSQL's syntax.
    * @throws SQLException on conversion failure
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGlseg(String s) throws SQLException {
     this();
     setValue(s);
@@ -56,7 +59,7 @@ public class PGlseg extends PGobject implements Serializable, Cloneable {
    * required by the driver.
    */
   public PGlseg() {
-    setType("lseg");
+    type = "lseg";
   }
 
   /**
@@ -79,7 +82,7 @@ public class PGlseg extends PGobject implements Serializable, Cloneable {
    * @param obj Object to compare with
    * @return true if the two line segments are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj instanceof PGlseg) {
       PGlseg p = (PGlseg) obj;
       return (p.point[0].equals(point[0]) && p.point[1].equals(point[1]))
diff --git a/src/main/java/org/postgresql/geometric/PGpath.java b/src/main/java/org/postgresql/geometric/PGpath.java
index 2ee7dcbc811d2cb74f80df660268256e00cdc313..f5af4061e85f6f37d57d578af2706950ea0986b2 100644
--- a/src/main/java/org/postgresql/geometric/PGpath.java
+++ b/src/main/java/org/postgresql/geometric/PGpath.java
@@ -11,6 +11,8 @@ import org.postgresql.util.PGtokenizer;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 
@@ -26,7 +28,7 @@ public class PGpath extends PGobject implements Serializable, Cloneable {
   /**
    * The points defining this path.
    */
-  public PGpoint[] points;
+  public PGpoint /* @Nullable */ [] points;
 
   /**
    * @param points the PGpoints that define the path
@@ -42,13 +44,14 @@ public class PGpath extends PGobject implements Serializable, Cloneable {
    * Required by the driver.
    */
   public PGpath() {
-    setType("path");
+    type = "path";
   }
 
   /**
    * @param s definition of the path in PostgreSQL's syntax.
    * @throws SQLException on conversion failure
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGpath(String s) throws SQLException {
     this();
     setValue(s);
@@ -73,7 +76,8 @@ public class PGpath extends PGobject implements Serializable, Cloneable {
 
     PGtokenizer t = new PGtokenizer(s, ',');
     int npoints = t.getSize();
-    points = new PGpoint[npoints];
+    PGpoint[] points = new PGpoint[npoints];
+    this.points = points;
     for (int p = 0; p < npoints; p++) {
       points[p] = new PGpoint(t.getToken(p));
     }
@@ -83,15 +87,23 @@ public class PGpath extends PGobject implements Serializable, Cloneable {
    * @param obj Object to compare with
    * @return true if the two paths are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj instanceof PGpath) {
       PGpath p = (PGpath) obj;
 
-      if (p.points.length != points.length) {
+      if (p.open != open) {
         return false;
       }
 
-      if (p.open != open) {
+      if (points == null ^ p.points == null) {
+        return false;
+      }
+
+      if (points == null) {
+        return true;
+      }
+
+      if (p.points.length != points.length) {
         return false;
       }
 
@@ -108,9 +120,12 @@ public class PGpath extends PGobject implements Serializable, Cloneable {
 
   public int hashCode() {
     // XXX not very good..
-    int hash = 0;
+    int hash = open ? 1231 : 1237;
+    if (points == null) {
+      return hash;
+    }
     for (int i = 0; i < points.length && i < 5; ++i) {
-      hash = hash ^ points[i].hashCode();
+      hash = hash * 31 + points[i].hashCode();
     }
     return hash;
   }
@@ -118,9 +133,10 @@ public class PGpath extends PGobject implements Serializable, Cloneable {
   public Object clone() throws CloneNotSupportedException {
     PGpath newPGpath = (PGpath) super.clone();
     if (newPGpath.points != null) {
-      newPGpath.points = (PGpoint[]) newPGpath.points.clone();
+      PGpoint[] newPoints = newPGpath.points.clone();
+      newPGpath.points = newPoints;
       for (int i = 0; i < newPGpath.points.length; ++i) {
-        newPGpath.points[i] = (PGpoint) newPGpath.points[i].clone();
+        newPoints[i] = (PGpoint) newPGpath.points[i].clone();
       }
     }
     return newPGpath;
@@ -132,7 +148,8 @@ public class PGpath extends PGobject implements Serializable, Cloneable {
   public String getValue() {
     StringBuilder b = new StringBuilder(open ? "[" : "(");
 
-    for (int p = 0; p < points.length; p++) {
+    PGpoint[] points = this.points;
+    for (int p = 0; points != null && p < points.length; p++) {
       if (p > 0) {
         b.append(",");
       }
diff --git a/src/main/java/org/postgresql/geometric/PGpoint.java b/src/main/java/org/postgresql/geometric/PGpoint.java
index cbdaddf1a02385e20c8e16968554f01c25f3e1d2..186d198d60c1ce7aebca465a5d88fc904f36f2a0 100644
--- a/src/main/java/org/postgresql/geometric/PGpoint.java
+++ b/src/main/java/org/postgresql/geometric/PGpoint.java
@@ -13,6 +13,8 @@ import org.postgresql.util.PGtokenizer;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.awt.Point;
 import java.io.Serializable;
 import java.sql.SQLException;
@@ -50,6 +52,7 @@ public class PGpoint extends PGobject implements PGBinaryObject, Serializable, C
    * @param value Definition of this point in PostgreSQL's syntax
    * @throws SQLException if something goes wrong
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGpoint(String value) throws SQLException {
     this();
     setValue(value);
@@ -59,7 +62,7 @@ public class PGpoint extends PGobject implements PGBinaryObject, Serializable, C
    * Required by the driver.
    */
   public PGpoint() {
-    setType("point");
+    type = "point";
   }
 
   /**
@@ -90,7 +93,7 @@ public class PGpoint extends PGobject implements PGBinaryObject, Serializable, C
    * @param obj Object to compare with
    * @return true if the two points are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj instanceof PGpoint) {
       PGpoint p = (PGpoint) obj;
       return x == p.x && y == p.y;
diff --git a/src/main/java/org/postgresql/geometric/PGpolygon.java b/src/main/java/org/postgresql/geometric/PGpolygon.java
index f295f343ca8f9710fc82187c07bc7a98fe0c3be5..5cb67d8e08cc3418135f0e61c588c017173d02a7 100644
--- a/src/main/java/org/postgresql/geometric/PGpolygon.java
+++ b/src/main/java/org/postgresql/geometric/PGpolygon.java
@@ -8,6 +8,8 @@ package org.postgresql.geometric;
 import org.postgresql.util.PGobject;
 import org.postgresql.util.PGtokenizer;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 
@@ -18,7 +20,7 @@ public class PGpolygon extends PGobject implements Serializable, Cloneable {
   /**
    * The points defining the polygon.
    */
-  public PGpoint[] points;
+  public PGpoint /* @Nullable */ [] points;
 
   /**
    * Creates a polygon using an array of PGpoints.
@@ -34,6 +36,7 @@ public class PGpolygon extends PGobject implements Serializable, Cloneable {
    * @param s definition of the polygon in PostgreSQL's syntax.
    * @throws SQLException on conversion failure
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGpolygon(String s) throws SQLException {
     this();
     setValue(s);
@@ -43,7 +46,7 @@ public class PGpolygon extends PGobject implements Serializable, Cloneable {
    * Required by the driver.
    */
   public PGpolygon() {
-    setType("polygon");
+    type = "polygon";
   }
 
   /**
@@ -53,7 +56,8 @@ public class PGpolygon extends PGobject implements Serializable, Cloneable {
   public void setValue(String s) throws SQLException {
     PGtokenizer t = new PGtokenizer(PGtokenizer.removePara(s), ',');
     int npoints = t.getSize();
-    points = new PGpoint[npoints];
+    PGpoint[] points = new PGpoint[npoints];
+    this.points = points;
     for (int p = 0; p < npoints; p++) {
       points[p] = new PGpoint(t.getToken(p));
     }
@@ -63,10 +67,18 @@ public class PGpolygon extends PGobject implements Serializable, Cloneable {
    * @param obj Object to compare with
    * @return true if the two polygons are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj instanceof PGpolygon) {
       PGpolygon p = (PGpolygon) obj;
 
+      if (points == null ^ p.points == null) {
+        return false;
+      }
+
+      if (points == null) {
+        return true;
+      }
+
       if (p.points.length != points.length) {
         return false;
       }
@@ -85,8 +97,12 @@ public class PGpolygon extends PGobject implements Serializable, Cloneable {
   public int hashCode() {
     // XXX not very good..
     int hash = 0;
+    PGpoint[] points = this.points;
+    if (points == null) {
+      return hash;
+    }
     for (int i = 0; i < points.length && i < 5; ++i) {
-      hash = hash ^ points[i].hashCode();
+      hash = hash * 31 + points[i].hashCode();
     }
     return hash;
   }
@@ -94,10 +110,11 @@ public class PGpolygon extends PGobject implements Serializable, Cloneable {
   public Object clone() throws CloneNotSupportedException {
     PGpolygon newPGpolygon = (PGpolygon) super.clone();
     if (newPGpolygon.points != null) {
-      newPGpolygon.points = (PGpoint[]) newPGpolygon.points.clone();
+      PGpoint[] newPoints = newPGpolygon.points.clone();
+      newPGpolygon.points = newPoints;
       for (int i = 0; i < newPGpolygon.points.length; ++i) {
         if (newPGpolygon.points[i] != null) {
-          newPGpolygon.points[i] = (PGpoint) newPGpolygon.points[i].clone();
+          newPoints[i] = (PGpoint) newPGpolygon.points[i].clone();
         }
       }
     }
@@ -110,7 +127,8 @@ public class PGpolygon extends PGobject implements Serializable, Cloneable {
   public String getValue() {
     StringBuilder b = new StringBuilder();
     b.append("(");
-    for (int p = 0; p < points.length; p++) {
+    PGpoint[] points = this.points;
+    for (int p = 0; points != null && p < points.length; p++) {
       if (p > 0) {
         b.append(",");
       }
diff --git a/src/main/java/org/postgresql/gss/GSSCallbackHandler.java b/src/main/java/org/postgresql/gss/GSSCallbackHandler.java
index 23b8293d30ea2d205fb6d8edcbf247a09b303b0b..13d2915408a03b1d25404083edc2379696a2b89e 100644
--- a/src/main/java/org/postgresql/gss/GSSCallbackHandler.java
+++ b/src/main/java/org/postgresql/gss/GSSCallbackHandler.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.gss;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 
 import javax.security.auth.callback.Callback;
@@ -17,9 +19,9 @@ import javax.security.auth.callback.UnsupportedCallbackException;
 public class GSSCallbackHandler implements CallbackHandler {
 
   private final String user;
-  private final String password;
+  private final /* @Nullable */ String password;
 
-  public GSSCallbackHandler(String user, String password) {
+  public GSSCallbackHandler(String user, /* @Nullable */ String password) {
     this.user = user;
     this.password = password;
   }
diff --git a/src/main/java/org/postgresql/gss/GSSInputStream.java b/src/main/java/org/postgresql/gss/GSSInputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..5cc035f99eb3e7844fc8bac1374c804d6955d810
--- /dev/null
+++ b/src/main/java/org/postgresql/gss/GSSInputStream.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2008, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.gss;
+
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.MessageProp;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+public class GSSInputStream extends InputStream {
+  private GSSContext gssContext;
+  private MessageProp messageProp;
+  private InputStream wrapped;
+  byte /* @Nullable */ [] unencrypted;
+  int unencryptedPos;
+  int unencryptedLength;
+
+  public GSSInputStream( InputStream wrapped, GSSContext gssContext, MessageProp messageProp) {
+    this.wrapped = wrapped;
+    this.gssContext = gssContext;
+    this.messageProp = messageProp;
+  }
+
+  @Override
+  public int read() throws IOException {
+    return 0;
+  }
+
+  @Override
+  public int read(byte [] buffer, int pos, int len) throws IOException {
+    byte[] int4Buf = new byte[4];
+    int encryptedLength;
+    int copyLength = 0;
+
+    if ( unencryptedLength > 0 ) {
+      copyLength = Math.min(len, unencryptedLength);
+      System.arraycopy(castNonNull(unencrypted), unencryptedPos, buffer, pos, copyLength);
+      unencryptedLength -= copyLength;
+      unencryptedPos += copyLength;
+    } else {
+      if (wrapped.read(int4Buf, 0, 4) == 4 ) {
+
+        encryptedLength = ((int4Buf[0] & 0xFF) << 24 | (int4Buf[1] & 0xFF) << 16 | (int4Buf[2] & 0xFF) << 8
+            | int4Buf[3] & 0xFF);
+
+        byte[] encryptedBuffer = new byte[encryptedLength];
+        wrapped.read(encryptedBuffer, 0, encryptedLength);
+
+        try {
+          byte[] unencrypted = gssContext.unwrap(encryptedBuffer, 0, encryptedLength, messageProp);
+          this.unencrypted = unencrypted;
+          unencryptedLength = unencrypted.length;
+          unencryptedPos = 0;
+
+          copyLength = Math.min(len, unencrypted.length);
+          System.arraycopy(unencrypted, unencryptedPos, buffer, pos, copyLength);
+          unencryptedLength -= copyLength;
+          unencryptedPos += copyLength;
+
+        } catch (GSSException e) {
+          throw new IOException(e);
+        }
+        return copyLength;
+      }
+    }
+    return copyLength;
+  }
+}
diff --git a/src/main/java/org/postgresql/gss/GSSOutputStream.java b/src/main/java/org/postgresql/gss/GSSOutputStream.java
new file mode 100644
index 0000000000000000000000000000000000000000..b32d8f8f216c687b58a348b856432117f4096fc9
--- /dev/null
+++ b/src/main/java/org/postgresql/gss/GSSOutputStream.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.gss;
+
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.MessageProp;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class GSSOutputStream extends OutputStream {
+  private final GSSContext gssContext;
+  private final MessageProp messageProp;
+  private byte[] buffer;
+  private byte[] int4Buf = new byte[4];
+  private int index;
+  private OutputStream wrapped;
+
+  public GSSOutputStream(OutputStream out, GSSContext gssContext, MessageProp messageProp, int bufferSize)  {
+    wrapped = out;
+    this.gssContext = gssContext;
+    this.messageProp = messageProp;
+    buffer = new byte[bufferSize];
+  }
+
+  @Override
+  public void write(int b) throws IOException {
+    buffer[index++] = (byte)b;
+    if (index >= buffer.length) {
+      flush();
+    }
+  }
+
+  @Override
+  public void write(byte[] buf) throws IOException {
+    write(buf, 0, buf.length);
+  }
+
+  @Override
+  public void write(byte[] b, int pos, int len) throws IOException {
+    int max;
+
+    while ( len > 0 ) {
+      int roomToWrite = buffer.length - index;
+      if ( len < roomToWrite ) {
+        System.arraycopy(b, pos,buffer, index, len);
+        index += len;
+        len -= roomToWrite;
+      } else {
+        System.arraycopy(b, pos, buffer, index, roomToWrite );
+        index += roomToWrite;
+        len -= roomToWrite;
+      }
+      if (roomToWrite == 0) {
+        flush();
+      }
+    }
+  }
+
+  @Override
+  public void flush() throws IOException {
+    try {
+      byte[] token = gssContext.wrap(buffer, 0, index, messageProp);
+      sendInteger4Raw(token.length);
+      wrapped.write(token, 0, token.length);
+      index = 0;
+    } catch ( GSSException ex ) {
+      throw new IOException(ex);
+    }
+    wrapped.flush();
+  }
+
+  private void sendInteger4Raw(int val) throws IOException {
+    int4Buf[0] = (byte) (val >>> 24);
+    int4Buf[1] = (byte) (val >>> 16);
+    int4Buf[2] = (byte) (val >>> 8);
+    int4Buf[3] = (byte) (val);
+    wrapped.write(int4Buf);
+  }
+
+}
diff --git a/src/main/java/org/postgresql/gss/GssAction.java b/src/main/java/org/postgresql/gss/GssAction.java
index 298a80f4c9953ff8224b69b3373fc0b92001589d..34f003e41649989c03e89cdf2bd16ffef9c8e2c3 100644
--- a/src/main/java/org/postgresql/gss/GssAction.java
+++ b/src/main/java/org/postgresql/gss/GssAction.java
@@ -11,6 +11,7 @@ import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 import org.postgresql.util.ServerErrorMessage;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
 import org.ietf.jgss.GSSContext;
 import org.ietf.jgss.GSSCredential;
 import org.ietf.jgss.GSSException;
@@ -23,7 +24,7 @@ import java.security.PrivilegedAction;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-class GssAction implements PrivilegedAction<Exception> {
+class GssAction implements PrivilegedAction</* @Nullable */ Exception> {
 
   private static final Logger LOGGER = Logger.getLogger(GssAction.class.getName());
   private final PGStream pgStream;
@@ -31,10 +32,10 @@ class GssAction implements PrivilegedAction<Exception> {
   private final String user;
   private final String kerberosServerName;
   private final boolean useSpnego;
-  private final GSSCredential clientCredentials;
+  private final /* @Nullable */ GSSCredential clientCredentials;
   private final boolean logServerErrorDetail;
 
-  GssAction(PGStream pgStream, GSSCredential clientCredentials, String host, String user,
+  GssAction(PGStream pgStream, /* @Nullable */ GSSCredential clientCredentials, String host, String user,
       String kerberosServerName, boolean useSpnego, boolean logServerErrorDetail) {
     this.pgStream = pgStream;
     this.clientCredentials = clientCredentials;
@@ -59,7 +60,7 @@ class GssAction implements PrivilegedAction<Exception> {
   }
 
   @Override
-  public Exception run() {
+  public /* @Nullable */ Exception run() {
     try {
       GSSManager manager = GSSManager.getInstance();
       GSSCredential clientCreds = null;
@@ -136,7 +137,6 @@ class GssAction implements PrivilegedAction<Exception> {
       return new PSQLException(GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE,
           gsse);
     }
-
     return null;
   }
 }
diff --git a/src/main/java/org/postgresql/gss/GssEncAction.java b/src/main/java/org/postgresql/gss/GssEncAction.java
new file mode 100644
index 0000000000000000000000000000000000000000..13c20feafd766f35f17c260225dc841287a89776
--- /dev/null
+++ b/src/main/java/org/postgresql/gss/GssEncAction.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.gss;
+
+import org.postgresql.core.PGStream;
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+import org.ietf.jgss.GSSContext;
+import org.ietf.jgss.GSSCredential;
+import org.ietf.jgss.GSSException;
+import org.ietf.jgss.GSSManager;
+import org.ietf.jgss.GSSName;
+import org.ietf.jgss.Oid;
+
+import java.io.IOException;
+import java.security.PrivilegedAction;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+public class GssEncAction implements PrivilegedAction</* @Nullable */ Exception> {
+  private static final Logger LOGGER = Logger.getLogger(GssAction.class.getName());
+  private final PGStream pgStream;
+  private final String host;
+  private final String user;
+  private final String kerberosServerName;
+  private final boolean useSpnego;
+  private final /* @Nullable */ GSSCredential clientCredentials;
+  private final boolean logServerErrorDetail;
+
+  public GssEncAction(PGStream pgStream, /* @Nullable */ GSSCredential clientCredentials,
+      String host, String user,
+      String kerberosServerName, boolean useSpnego, boolean logServerErrorDetail) {
+    this.pgStream = pgStream;
+    this.clientCredentials = clientCredentials;
+    this.host = host;
+    this.user = user;
+    this.kerberosServerName = kerberosServerName;
+    this.useSpnego = useSpnego;
+    this.logServerErrorDetail = logServerErrorDetail;
+  }
+
+  private static boolean hasSpnegoSupport(GSSManager manager) throws GSSException {
+    org.ietf.jgss.Oid spnego = new org.ietf.jgss.Oid("1.3.6.1.5.5.2");
+    org.ietf.jgss.Oid[] mechs = manager.getMechs();
+
+    for (Oid mech : mechs) {
+      if (mech.equals(spnego)) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  @Override
+  public /* @Nullable */ Exception run() {
+    try {
+      GSSManager manager = GSSManager.getInstance();
+      GSSCredential clientCreds = null;
+      Oid[] desiredMechs = new Oid[1];
+      if (clientCredentials == null) {
+        if (useSpnego && hasSpnegoSupport(manager)) {
+          desiredMechs[0] = new Oid("1.3.6.1.5.5.2");
+        } else {
+          desiredMechs[0] = new Oid("1.2.840.113554.1.2.2");
+        }
+        GSSName clientName = manager.createName(user, GSSName.NT_USER_NAME);
+        clientCreds = manager.createCredential(clientName, 8 * 3600, desiredMechs,
+            GSSCredential.INITIATE_ONLY);
+      } else {
+        desiredMechs[0] = new Oid("1.2.840.113554.1.2.2");
+        clientCreds = clientCredentials;
+      }
+
+      GSSName serverName =
+          manager.createName(kerberosServerName + "@" + host, GSSName.NT_HOSTBASED_SERVICE);
+
+      GSSContext secContext = manager.createContext(serverName, desiredMechs[0], clientCreds,
+          GSSContext.DEFAULT_LIFETIME);
+      secContext.requestMutualAuth(true);
+      secContext.requestConf(true);
+      secContext.requestInteg(true);
+
+      byte[] inToken = new byte[0];
+      byte[] outToken = null;
+
+      boolean established = false;
+      while (!established) {
+        outToken = secContext.initSecContext(inToken, 0, inToken.length);
+
+        if (outToken != null) {
+          LOGGER.log(Level.FINEST, " FE=> Password(GSS Authentication Token)");
+
+          pgStream.sendInteger4(outToken.length);
+          pgStream.send(outToken);
+          pgStream.flush();
+        }
+
+        if (!secContext.isEstablished()) {
+          int len = pgStream.receiveInteger4();
+          // should check type = 8
+          inToken = pgStream.receive(len);
+        } else {
+          established = true;
+          pgStream.setSecContext(secContext);
+        }
+      }
+
+    } catch (IOException e) {
+      return e;
+    } catch (GSSException gsse) {
+      return new PSQLException(GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE,
+          gsse);
+    }
+
+    return null;
+  }
+
+}
diff --git a/src/main/java/org/postgresql/gss/MakeGSS.java b/src/main/java/org/postgresql/gss/MakeGSS.java
index 0c5d27dbcda3d74a89d9ec275cf6612ec71c8d93..8aec6e845cb4d8db5c86bb2f2bfc97a3033394b6 100644
--- a/src/main/java/org/postgresql/gss/MakeGSS.java
+++ b/src/main/java/org/postgresql/gss/MakeGSS.java
@@ -10,12 +10,12 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
 import org.ietf.jgss.GSSCredential;
 
 import java.io.IOException;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
-import java.sql.SQLException;
 import java.util.Set;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -26,10 +26,12 @@ import javax.security.auth.login.LoginContext;
 public class MakeGSS {
   private static final Logger LOGGER = Logger.getLogger(MakeGSS.class.getName());
 
-  public static void authenticate(PGStream pgStream, String host, String user, String password,
-      String jaasApplicationName, String kerberosServerName, boolean useSpnego, boolean jaasLogin,
+  public static void authenticate(boolean encrypted,
+      PGStream pgStream, String host, String user, /* @Nullable */ String password,
+      /* @Nullable */ String jaasApplicationName, /* @Nullable */ String kerberosServerName,
+      boolean useSpnego, boolean jaasLogin,
       boolean logServerErrorDetail)
-          throws IOException, SQLException {
+          throws IOException, PSQLException {
     LOGGER.log(Level.FINEST, " <=BE AuthenticationReqGSS");
 
     if (jaasApplicationName == null) {
@@ -39,7 +41,7 @@ public class MakeGSS {
       kerberosServerName = "postgres";
     }
 
-    Exception result;
+    /* @Nullable */ Exception result;
     try {
       boolean performAuthentication = jaasLogin;
       GSSCredential gssCredential = null;
@@ -57,18 +59,25 @@ public class MakeGSS {
         lc.login();
         sub = lc.getSubject();
       }
-      PrivilegedAction<Exception> action = new GssAction(pgStream, gssCredential, host, user,
-          kerberosServerName, useSpnego, logServerErrorDetail);
+      if ( encrypted ) {
+        PrivilegedAction</* @Nullable */ Exception> action = new GssEncAction(pgStream, gssCredential, host, user,
+            kerberosServerName, useSpnego, logServerErrorDetail);
 
-      result = Subject.doAs(sub, action);
+        result = Subject.doAs(sub, action);
+      } else {
+        PrivilegedAction</* @Nullable */ Exception> action = new GssAction(pgStream, gssCredential, host, user,
+            kerberosServerName, useSpnego, logServerErrorDetail);
+
+        result = Subject.doAs(sub, action);
+      }
     } catch (Exception e) {
       throw new PSQLException(GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE, e);
     }
 
     if (result instanceof IOException) {
       throw (IOException) result;
-    } else if (result instanceof SQLException) {
-      throw (SQLException) result;
+    } else if (result instanceof PSQLException) {
+      throw (PSQLException) result;
     } else if (result != null) {
       throw new PSQLException(GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE,
           result);
diff --git a/src/main/java/org/postgresql/hostchooser/GlobalHostStatusTracker.java b/src/main/java/org/postgresql/hostchooser/GlobalHostStatusTracker.java
index 83a0a5847bdccc0a7be21cdb7f09d81df1cfa7e4..50e0dfb8ef3ddc096ea37761cc7fb7c36a92d7df 100644
--- a/src/main/java/org/postgresql/hostchooser/GlobalHostStatusTracker.java
+++ b/src/main/java/org/postgresql/hostchooser/GlobalHostStatusTracker.java
@@ -7,6 +7,8 @@ package org.postgresql.hostchooser;
 
 import org.postgresql.util.HostSpec;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
@@ -66,7 +68,7 @@ public class GlobalHostStatusTracker {
 
   static class HostSpecStatus {
     final HostSpec host;
-    HostStatus status;
+    /* @Nullable */ HostStatus status;
     long lastUpdated;
 
     HostSpecStatus(HostSpec host) {
diff --git a/src/main/java/org/postgresql/hostchooser/HostRequirement.java b/src/main/java/org/postgresql/hostchooser/HostRequirement.java
index d2ddf887fdd823fdebbd7859ee7567edfa93342b..ddd3e353dcd4f81680d8db491eb75609158b5141 100644
--- a/src/main/java/org/postgresql/hostchooser/HostRequirement.java
+++ b/src/main/java/org/postgresql/hostchooser/HostRequirement.java
@@ -5,12 +5,14 @@
 
 package org.postgresql.hostchooser;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Describes the required server type.
  */
 public enum HostRequirement {
   any {
-    public boolean allowConnectingTo(HostStatus status) {
+    public boolean allowConnectingTo(/* @Nullable */ HostStatus status) {
       return status != HostStatus.ConnectFail;
     }
   },
@@ -20,27 +22,27 @@ public enum HostRequirement {
    */
   @Deprecated
   master {
-    public boolean allowConnectingTo(HostStatus status) {
+    public boolean allowConnectingTo(/* @Nullable */ HostStatus status) {
       return primary.allowConnectingTo(status);
     }
   },
   primary {
-    public boolean allowConnectingTo(HostStatus status) {
+    public boolean allowConnectingTo(/* @Nullable */ HostStatus status) {
       return status == HostStatus.Primary || status == HostStatus.ConnectOK;
     }
   },
   secondary {
-    public boolean allowConnectingTo(HostStatus status) {
+    public boolean allowConnectingTo(/* @Nullable */ HostStatus status) {
       return status == HostStatus.Secondary || status == HostStatus.ConnectOK;
     }
   },
   preferSecondary {
-    public boolean allowConnectingTo(HostStatus status) {
+    public boolean allowConnectingTo(/* @Nullable */ HostStatus status) {
       return status != HostStatus.ConnectFail;
     }
   };
 
-  public abstract boolean allowConnectingTo(HostStatus status);
+  public abstract boolean allowConnectingTo(/* @Nullable */ HostStatus status);
 
   /**
    * <p>The postgreSQL project has decided not to use the term slave to refer to alternate servers.
diff --git a/src/main/java/org/postgresql/jdbc/AbstractBlobClob.java b/src/main/java/org/postgresql/jdbc/AbstractBlobClob.java
index 89f7148505dccf78396293dae45b9e233c63b089..ea246c480637c2b7e0c0995c20913721cb5c8938 100644
--- a/src/main/java/org/postgresql/jdbc/AbstractBlobClob.java
+++ b/src/main/java/org/postgresql/jdbc/AbstractBlobClob.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseConnection;
 import org.postgresql.core.ServerVersion;
 import org.postgresql.largeobject.LargeObject;
@@ -13,6 +15,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.InputStream;
 import java.io.OutputStream;
 import java.sql.Blob;
@@ -27,7 +31,7 @@ import java.util.ArrayList;
 public abstract class AbstractBlobClob {
   protected BaseConnection conn;
 
-  private LargeObject currentLo;
+  private /* @Nullable */ LargeObject currentLo;
   private boolean currentLoIsWriteable;
   private boolean support64bit;
 
@@ -35,19 +39,16 @@ public abstract class AbstractBlobClob {
    * We create separate LargeObjects for methods that use streams so they won't interfere with each
    * other.
    */
-  private ArrayList<LargeObject> subLOs;
+  private /* @Nullable */ ArrayList<LargeObject> subLOs = new ArrayList<LargeObject>();
 
   private final long oid;
 
   public AbstractBlobClob(BaseConnection conn, long oid) throws SQLException {
     this.conn = conn;
     this.oid = oid;
-    this.currentLo = null;
     this.currentLoIsWriteable = false;
 
     support64bit = conn.haveMinimumServerVersion(90300);
-
-    subLOs = new ArrayList<LargeObject>();
   }
 
   public synchronized void free() throws SQLException {
@@ -56,8 +57,10 @@ public abstract class AbstractBlobClob {
       currentLo = null;
       currentLoIsWriteable = false;
     }
-    for (LargeObject subLO : subLOs) {
-      subLO.close();
+    if (subLOs != null) {
+      for (LargeObject subLO : subLOs) {
+        subLO.close();
+      }
     }
     subLOs = null;
   }
@@ -165,7 +168,7 @@ public abstract class AbstractBlobClob {
    */
   private class LOIterator  {
     private static final int BUFFER_SIZE = 8096;
-    private byte[] buffer = new byte[BUFFER_SIZE];
+    private final byte[] buffer = new byte[BUFFER_SIZE];
     private int idx = BUFFER_SIZE;
     private int numBytes = BUFFER_SIZE;
 
@@ -246,30 +249,32 @@ public abstract class AbstractBlobClob {
   }
 
   protected synchronized LargeObject getLo(boolean forWrite) throws SQLException {
-    if (this.currentLo != null) {
+    LargeObject currentLo = this.currentLo;
+    if (currentLo != null) {
       if (forWrite && !currentLoIsWriteable) {
         // Reopen the stream in read-write, at the same pos.
-        int currentPos = this.currentLo.tell();
+        int currentPos = currentLo.tell();
 
         LargeObjectManager lom = conn.getLargeObjectAPI();
         LargeObject newLo = lom.open(oid, LargeObjectManager.READWRITE);
-        this.subLOs.add(this.currentLo);
-        this.currentLo = newLo;
+        castNonNull(subLOs).add(currentLo);
+        this.currentLo = currentLo = newLo;
 
         if (currentPos != 0) {
-          this.currentLo.seek(currentPos);
+          currentLo.seek(currentPos);
         }
       }
 
-      return this.currentLo;
+      return currentLo;
     }
     LargeObjectManager lom = conn.getLargeObjectAPI();
-    currentLo = lom.open(oid, forWrite ? LargeObjectManager.READWRITE : LargeObjectManager.READ);
+    this.currentLo = currentLo =
+        lom.open(oid, forWrite ? LargeObjectManager.READWRITE : LargeObjectManager.READ);
     currentLoIsWriteable = forWrite;
     return currentLo;
   }
 
   protected void addSubLO(LargeObject subLO) {
-    subLOs.add(subLO);
+    castNonNull(subLOs).add(subLO);
   }
 }
diff --git a/src/main/java/org/postgresql/jdbc/ArrayDecoding.java b/src/main/java/org/postgresql/jdbc/ArrayDecoding.java
new file mode 100644
index 0000000000000000000000000000000000000000..793291a42046f59fac37a8afe44353b1a5dd5538
--- /dev/null
+++ b/src/main/java/org/postgresql/jdbc/ArrayDecoding.java
@@ -0,0 +1,791 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.Oid;
+import org.postgresql.jdbc2.ArrayAssistant;
+import org.postgresql.jdbc2.ArrayAssistantRegistry;
+import org.postgresql.util.GT;
+import org.postgresql.util.PGbytea;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+// import org.checkerframework.checker.index.qual.NonNegative;
+// import org.checkerframework.checker.nullness.qual.NonNull;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility for decoding arrays.
+ *
+ * <p>
+ * See {@code ArrayEncoding} for description of the binary format of arrays.
+ * </p>
+ *
+ * @author Brett Okken
+ */
+final class ArrayDecoding {
+
+  /**
+   * Array list implementation specific for storing PG array elements. If
+   * {@link PgArrayList#dimensionsCount} is {@code 1}, the contents will be
+   * {@link String}. For all larger <i>dimensionsCount</i>, the values will be
+   * {@link PgArrayList} instances.
+   */
+  static final class PgArrayList extends ArrayList</* @Nullable */ Object> {
+
+    private static final long serialVersionUID = 1L;
+
+    /**
+     * How many dimensions.
+     */
+    int dimensionsCount = 1;
+
+  }
+
+  private interface ArrayDecoder<A extends /* @NonNull */ Object> {
+
+    A createArray(/* @NonNegative */ int size);
+
+    Object[] createMultiDimensionalArray(/* @NonNegative */ int[] sizes);
+
+    boolean supportBinary();
+
+    void populateFromBinary(A array, /* @NonNegative */ int index, /* @NonNegative */ int count, ByteBuffer bytes, BaseConnection connection)
+        throws SQLException;
+
+    void populateFromString(A array, List</* @Nullable */ String> strings, BaseConnection connection) throws SQLException;
+  }
+
+  private abstract static class AbstractObjectStringArrayDecoder<A extends /* @NonNull */ Object> implements ArrayDecoder<A> {
+    final Class<?> baseClazz;
+
+    AbstractObjectStringArrayDecoder(Class<?> baseClazz) {
+      this.baseClazz = baseClazz;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean supportBinary() {
+      return false;
+    }
+
+    @SuppressWarnings("unchecked")
+    @Override
+    public A createArray(int size) {
+      return (A) Array.newInstance(baseClazz, size);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Object[] createMultiDimensionalArray(int[] sizes) {
+      return (Object[]) Array.newInstance(baseClazz, sizes);
+    }
+
+    @Override
+    public void populateFromBinary(A arr, int index, int count, ByteBuffer bytes, BaseConnection connection)
+        throws SQLException {
+      throw new SQLFeatureNotSupportedException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void populateFromString(A arr, List</* @Nullable */ String> strings, BaseConnection connection) throws SQLException {
+      final /* @Nullable */ Object[] array = (Object[]) arr;
+
+      for (int i = 0, j = strings.size(); i < j; ++i) {
+        final String stringVal = strings.get(i);
+        array[i] = stringVal != null ? parseValue(stringVal, connection) : null;
+      }
+    }
+
+    abstract Object parseValue(String stringVal, BaseConnection connection) throws SQLException;
+  }
+
+  private abstract static class AbstractObjectArrayDecoder<A extends /* @NonNull */ Object> extends AbstractObjectStringArrayDecoder<A> {
+
+    AbstractObjectArrayDecoder(Class<?> baseClazz) {
+      super(baseClazz);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean supportBinary() {
+      return true;
+    }
+
+    @Override
+    public void populateFromBinary(A arr, /* @NonNegative */ int index, /* @NonNegative */ int count, ByteBuffer bytes, BaseConnection connection)
+        throws SQLException {
+      final /* @Nullable */ Object[] array = (Object[]) arr;
+
+      // skip through to the requested index
+      for (int i = 0; i < index; ++i) {
+        final int length = bytes.getInt();
+        if (length > 0) {
+          bytes.position(bytes.position() + length);
+        }
+      }
+
+      for (int i = 0; i < count; ++i) {
+        final int length = bytes.getInt();
+        if (length != -1) {
+          array[i] = parseValue(length, bytes, connection);
+        } else {
+          // explicitly set to null for reader's clarity
+          array[i] = null;
+        }
+      }
+    }
+
+    abstract Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) throws SQLException;
+  }
+
+  private static final ArrayDecoder<Long[]> LONG_OBJ_ARRAY = new AbstractObjectArrayDecoder<Long[]>(Long.class) {
+
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) {
+      return bytes.getLong();
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return PgResultSet.toLong(stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<Long[]> INT4_UNSIGNED_OBJ_ARRAY = new AbstractObjectArrayDecoder<Long[]>(
+      Long.class) {
+
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) {
+      final long value = bytes.getInt() & 0xFFFFFFFFL;
+      return value;
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return PgResultSet.toLong(stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<Integer[]> INTEGER_OBJ_ARRAY = new AbstractObjectArrayDecoder<Integer[]>(
+      Integer.class) {
+
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) {
+      return bytes.getInt();
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return PgResultSet.toInt(stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<Short[]> SHORT_OBJ_ARRAY = new AbstractObjectArrayDecoder<Short[]>(Short.class) {
+
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) {
+      return bytes.getShort();
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return PgResultSet.toShort(stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<Double[]> DOUBLE_OBJ_ARRAY = new AbstractObjectArrayDecoder<Double[]>(
+      Double.class) {
+
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) {
+      return bytes.getDouble();
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return PgResultSet.toDouble(stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<Float[]> FLOAT_OBJ_ARRAY = new AbstractObjectArrayDecoder<Float[]>(Float.class) {
+
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) {
+      return bytes.getFloat();
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return PgResultSet.toFloat(stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<Boolean[]> BOOLEAN_OBJ_ARRAY = new AbstractObjectArrayDecoder<Boolean[]>(
+      Boolean.class) {
+
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) {
+      return bytes.get() == 1;
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return BooleanTypeUtil.fromString(stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<String[]> STRING_ARRAY = new AbstractObjectArrayDecoder<String[]>(String.class) {
+
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) throws SQLException {
+      assert bytes.hasArray();
+      final byte[] byteArray = bytes.array();
+      final int offset = bytes.arrayOffset() + bytes.position();
+
+      String val;
+      try {
+        val = connection.getEncoding().decode(byteArray, offset, length);
+      } catch (IOException e) {
+        throw new PSQLException(GT.tr(
+            "Invalid character data was found.  This is most likely caused by stored data containing characters that are invalid for the character set the database was created in.  The most common example of this is storing 8bit data in a SQL_ASCII database."),
+            PSQLState.DATA_ERROR, e);
+      }
+      bytes.position(bytes.position() + length);
+      return val;
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return stringVal;
+    }
+  };
+
+  private static final ArrayDecoder<byte[][]> BYTE_ARRAY_ARRAY = new AbstractObjectArrayDecoder<byte[][]>(
+      byte[].class) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) throws SQLException {
+      final byte[] array = new byte[length];
+      bytes.get(array);
+      return array;
+    }
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      try {
+        return PGbytea.toBytes(stringVal.getBytes("ascii"));
+      } catch (UnsupportedEncodingException e) {
+        throw new java.lang.Error("ascii must be supported");
+      }
+    }
+  };
+
+  private static final ArrayDecoder<BigDecimal[]> BIG_DECIMAL_STRING_DECODER = new AbstractObjectStringArrayDecoder<BigDecimal[]>(
+      BigDecimal.class) {
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return PgResultSet.toBigDecimal(stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<String[]> STRING_ONLY_DECODER = new AbstractObjectStringArrayDecoder<String[]>(
+      String.class) {
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return stringVal;
+    }
+  };
+
+  private static final ArrayDecoder<java.sql.Date[]> DATE_DECODER = new AbstractObjectStringArrayDecoder<java.sql.Date[]>(
+      java.sql.Date.class) {
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return connection.getTimestampUtils().toDate(null, stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<java.sql.Time[]> TIME_DECODER = new AbstractObjectStringArrayDecoder<java.sql.Time[]>(
+      java.sql.Time.class) {
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return connection.getTimestampUtils().toTime(null, stringVal);
+    }
+  };
+
+  private static final ArrayDecoder<java.sql.Timestamp[]> TIMESTAMP_DECODER = new AbstractObjectStringArrayDecoder<java.sql.Timestamp[]>(
+      java.sql.Timestamp.class) {
+
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return connection.getTimestampUtils().toTimestamp(null, stringVal);
+    }
+  };
+
+  /**
+   * Maps from base type oid to {@link ArrayDecoder} capable of processing
+   * entries.
+   */
+  @SuppressWarnings("rawtypes")
+  private static final Map<Integer, ArrayDecoder> OID_TO_DECODER = new HashMap<Integer, ArrayDecoder>(
+      (int) (21 / .75) + 1);
+
+  static {
+    OID_TO_DECODER.put(Oid.OID, INT4_UNSIGNED_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.INT8, LONG_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.INT4, INTEGER_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.INT2, SHORT_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.MONEY, DOUBLE_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.FLOAT8, DOUBLE_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.FLOAT4, FLOAT_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.TEXT, STRING_ARRAY);
+    OID_TO_DECODER.put(Oid.VARCHAR, STRING_ARRAY);
+    OID_TO_DECODER.put(Oid.BIT, BOOLEAN_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.BOOL, BOOLEAN_OBJ_ARRAY);
+    OID_TO_DECODER.put(Oid.BYTEA, BYTE_ARRAY_ARRAY);
+    OID_TO_DECODER.put(Oid.NUMERIC, BIG_DECIMAL_STRING_DECODER);
+    OID_TO_DECODER.put(Oid.BPCHAR, STRING_ONLY_DECODER);
+    OID_TO_DECODER.put(Oid.CHAR, STRING_ONLY_DECODER);
+    OID_TO_DECODER.put(Oid.JSON, STRING_ONLY_DECODER);
+    OID_TO_DECODER.put(Oid.DATE, DATE_DECODER);
+    OID_TO_DECODER.put(Oid.TIME, TIME_DECODER);
+    OID_TO_DECODER.put(Oid.TIMETZ, TIME_DECODER);
+    OID_TO_DECODER.put(Oid.TIMESTAMP, TIMESTAMP_DECODER);
+    OID_TO_DECODER.put(Oid.TIMESTAMPTZ, TIMESTAMP_DECODER);
+  }
+
+  @SuppressWarnings("rawtypes")
+  private static final class ArrayAssistantObjectArrayDecoder extends AbstractObjectArrayDecoder {
+    private final ArrayAssistant arrayAssistant;
+
+    @SuppressWarnings("unchecked")
+    ArrayAssistantObjectArrayDecoder(ArrayAssistant arrayAssistant) {
+      super(arrayAssistant.baseType());
+      this.arrayAssistant = arrayAssistant;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) throws SQLException {
+
+      assert bytes.hasArray();
+      final byte[] byteArray = bytes.array();
+      final int offset = bytes.arrayOffset() + bytes.position();
+
+      final Object val = arrayAssistant.buildElement(byteArray, offset, length);
+
+      bytes.position(bytes.position() + length);
+      return val;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return arrayAssistant.buildElement(stringVal);
+    }
+  }
+
+  private static final class MappedTypeObjectArrayDecoder extends AbstractObjectArrayDecoder<Object[]> {
+
+    private final String typeName;
+
+    MappedTypeObjectArrayDecoder(String baseTypeName) {
+      super(Object.class);
+      this.typeName = baseTypeName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    Object parseValue(int length, ByteBuffer bytes, BaseConnection connection) throws SQLException {
+      final byte[] copy = new byte[length];
+      bytes.get(copy);
+      return connection.getObject(typeName, null, copy);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    Object parseValue(String stringVal, BaseConnection connection) throws SQLException {
+      return connection.getObject(typeName, stringVal, null);
+    }
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <A extends /* @NonNull */ Object> ArrayDecoder<A> getDecoder(int oid, BaseConnection connection) throws SQLException {
+    final Integer key = oid;
+    @SuppressWarnings("rawtypes")
+    final ArrayDecoder decoder = OID_TO_DECODER.get(key);
+    if (decoder != null) {
+      return decoder;
+    }
+
+    final ArrayAssistant assistant = ArrayAssistantRegistry.getAssistant(oid);
+
+    if (assistant != null) {
+      return new ArrayAssistantObjectArrayDecoder(assistant);
+    }
+
+    final String typeName = connection.getTypeInfo().getPGType(oid);
+    if (typeName == null) {
+      throw org.postgresql.Driver.notImplemented(PgArray.class, "readArray(data,oid)");
+    }
+
+    return (ArrayDecoder<A>) new MappedTypeObjectArrayDecoder(typeName);
+  }
+
+  /**
+   * Reads binary representation of array into object model.
+   *
+   * @param index
+   *          1 based index of where to start on outermost array.
+   * @param count
+   *          The number of items to return from outermost array (beginning at
+   *          <i>index</i>).
+   * @param bytes
+   *          The binary representation of the array.
+   * @param connection
+   *          The connection the <i>bytes</i> were retrieved from.
+   * @return The parsed array.
+   * @throws SQLException
+   *           For failures encountered during parsing.
+   */
+  @SuppressWarnings("unchecked")
+  public static Object readBinaryArray(int index, int count, byte[] bytes, BaseConnection connection)
+      throws SQLException {
+    final ByteBuffer buffer = ByteBuffer.wrap(bytes);
+    buffer.order(ByteOrder.BIG_ENDIAN);
+    final int dimensions = buffer.getInt();
+    @SuppressWarnings("unused")
+    final boolean hasNulls = buffer.getInt() != 0;
+    final int elementOid = buffer.getInt();
+
+    @SuppressWarnings("rawtypes")
+    final ArrayDecoder decoder = getDecoder(elementOid, connection);
+
+    if (!decoder.supportBinary()) {
+      throw org.postgresql.Driver.notImplemented(PgArray.class, "readBinaryArray(data,oid)");
+    }
+
+    if (dimensions == 0) {
+      return decoder.createArray(0);
+    }
+
+    final int adjustedSkipIndex = index > 0 ? index - 1 : 0;
+
+    // optimize for single dimension array
+    if (dimensions == 1) {
+      int length = buffer.getInt();
+      buffer.position(buffer.position() + 4);
+      if (count > 0) {
+        length = Math.min(length, count);
+      }
+      final Object array = decoder.createArray(length);
+      decoder.populateFromBinary(array, adjustedSkipIndex, length, buffer, connection);
+      return array;
+    }
+
+    final int[] dimensionLengths = new int[dimensions];
+    for (int i = 0; i < dimensions; ++i) {
+      dimensionLengths[i] = buffer.getInt();
+      buffer.position(buffer.position() + 4);
+    }
+
+    if (count > 0) {
+      dimensionLengths[0] = Math.min(count, dimensionLengths[0]);
+    }
+
+    final Object[] array = decoder.createMultiDimensionalArray(dimensionLengths);
+
+    // TODO: in certain circumstances (no nulls, fixed size data types)
+    // if adjustedSkipIndex is > 0, we could advance through the buffer rather than
+    // parse our way through throwing away the results
+
+    storeValues(array, decoder, buffer, adjustedSkipIndex, dimensionLengths, 0, connection);
+
+    return array;
+  }
+
+  @SuppressWarnings("unchecked")
+  private static <A extends /* @NonNull */ Object> void storeValues(A[] array, ArrayDecoder<A> decoder, ByteBuffer bytes,
+      int skip, int[] dimensionLengths, int dim, BaseConnection connection) throws SQLException {
+    assert dim <= dimensionLengths.length - 2;
+
+    for (int i = 0; i < skip; ++i) {
+      if (dim == dimensionLengths.length - 2) {
+        decoder.populateFromBinary(array[0], 0, dimensionLengths[dim + 1], bytes, connection);
+      } else {
+        storeValues((/* @NonNull */ A /* @NonNull */[]) array[0], decoder, bytes, 0, dimensionLengths, dim + 1, connection);
+      }
+    }
+
+    for (int i = 0; i < dimensionLengths[dim]; ++i) {
+      if (dim == dimensionLengths.length - 2) {
+        decoder.populateFromBinary(array[i], 0, dimensionLengths[dim + 1], bytes, connection);
+      } else {
+        storeValues((/* @NonNull */ A /* @NonNull */[]) array[i], decoder, bytes, 0, dimensionLengths, dim + 1, connection);
+      }
+    }
+  }
+
+  /**
+   * Parses the string representation of an array into a {@link PgArrayList}.
+   *
+   * @param fieldString
+   *          The array value to parse.
+   * @param delim
+   *          The delimiter character appropriate for the data type.
+   * @return A {@link PgArrayList} representing the parsed <i>fieldString</i>.
+   */
+  static PgArrayList buildArrayList(String fieldString, char delim) {
+
+    final PgArrayList arrayList = new PgArrayList();
+
+    if (fieldString == null) {
+      return arrayList;
+    }
+
+    final char[] chars = fieldString.toCharArray();
+    StringBuilder buffer = null;
+    boolean insideString = false;
+
+    // needed for checking if NULL value occurred
+    boolean wasInsideString = false;
+
+    // array dimension arrays
+    final List<PgArrayList> dims = new ArrayList<PgArrayList>();
+
+    // currently processed array
+    PgArrayList curArray = arrayList;
+
+    // Starting with 8.0 non-standard (beginning index
+    // isn't 1) bounds the dimensions are returned in the
+    // data formatted like so "[0:3]={0,1,2,3,4}".
+    // Older versions simply do not return the bounds.
+    //
+    // Right now we ignore these bounds, but we could
+    // consider allowing these index values to be used
+    // even though the JDBC spec says 1 is the first
+    // index. I'm not sure what a client would like
+    // to see, so we just retain the old behavior.
+    int startOffset = 0;
+    {
+      if (chars[0] == '[') {
+        while (chars[startOffset] != '=') {
+          startOffset++;
+        }
+        startOffset++; // skip =
+      }
+    }
+
+    for (int i = startOffset; i < chars.length; i++) {
+
+      // escape character that we need to skip
+      if (chars[i] == '\\') {
+        i++;
+      } else if (!insideString && chars[i] == '{') {
+        // subarray start
+        if (dims.isEmpty()) {
+          dims.add(arrayList);
+        } else {
+          PgArrayList a = new PgArrayList();
+          PgArrayList p = dims.get(dims.size() - 1);
+          p.add(a);
+          dims.add(a);
+        }
+        curArray = dims.get(dims.size() - 1);
+
+        // number of dimensions
+        {
+          for (int t = i + 1; t < chars.length; t++) {
+            if (Character.isWhitespace(chars[t])) {
+              continue;
+            } else if (chars[t] == '{') {
+              curArray.dimensionsCount++;
+            } else {
+              break;
+            }
+          }
+        }
+
+        buffer = new StringBuilder();
+        continue;
+      } else if (chars[i] == '"') {
+        // quoted element
+        insideString = !insideString;
+        wasInsideString = true;
+        continue;
+      } else if (!insideString && Character.isWhitespace(chars[i])) {
+        // white space
+        continue;
+      } else if ((!insideString && (chars[i] == delim || chars[i] == '}')) || i == chars.length - 1) {
+        // array end or element end
+        // when character that is a part of array element
+        if (chars[i] != '"' && chars[i] != '}' && chars[i] != delim && buffer != null) {
+          buffer.append(chars[i]);
+        }
+
+        String b = buffer == null ? null : buffer.toString();
+
+        // add element to current array
+        if (b != null && (!b.isEmpty() || wasInsideString)) {
+          curArray.add(!wasInsideString && b.equals("NULL") ? null : b);
+        }
+
+        wasInsideString = false;
+        buffer = new StringBuilder();
+
+        // when end of an array
+        if (chars[i] == '}') {
+          dims.remove(dims.size() - 1);
+
+          // when multi-dimension
+          if (!dims.isEmpty()) {
+            curArray = dims.get(dims.size() - 1);
+          }
+
+          buffer = null;
+        }
+
+        continue;
+      }
+
+      if (buffer != null) {
+        buffer.append(chars[i]);
+      }
+    }
+
+    return arrayList;
+  }
+
+  /**
+   * Reads {@code String} representation of array into object model.
+   *
+   * @param index
+   *          1 based index of where to start on outermost array.
+   * @param count
+   *          The number of items to return from outermost array (beginning at
+   *          <i>index</i>).
+   * @param oid
+   *          The oid of the base type of the array.
+   * @param list
+   *          The {@code #buildArrayList(String, char) processed} string
+   *          representation of an array.
+   * @param connection
+   *          The connection the <i>bytes</i> were retrieved from.
+   * @return The parsed array.
+   * @throws SQLException
+   *           For failures encountered during parsing.
+   */
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  public static Object readStringArray(int index, int count, int oid, PgArrayList list, BaseConnection connection)
+      throws SQLException {
+
+    final ArrayDecoder decoder = getDecoder(oid, connection);
+
+    final int dims = list.dimensionsCount;
+
+    if (dims == 0) {
+      return decoder.createArray(0);
+    }
+
+    boolean sublist = false;
+
+    int adjustedSkipIndex = 0;
+    if (index > 1) {
+      sublist = true;
+      adjustedSkipIndex = index - 1;
+    }
+
+    int adjustedCount = list.size();
+    if (count > 0 && count != adjustedCount) {
+      sublist = true;
+      adjustedCount = Math.min(adjustedCount, count);
+    }
+
+    final List adjustedList = sublist ? list.subList(adjustedSkipIndex, adjustedSkipIndex + adjustedCount) : list;
+
+    if (dims == 1) {
+      int length = adjustedList.size();
+      if (count > 0) {
+        length = Math.min(length, count);
+      }
+      final Object array = decoder.createArray(length);
+      decoder.populateFromString(array, adjustedList, connection);
+      return array;
+    }
+
+    // dimensions length array (to be used with
+    // java.lang.reflect.Array.newInstance(Class<?>, int[]))
+    final int[] dimensionLengths = new int[dims];
+    dimensionLengths[0] = adjustedCount;
+    {
+      List tmpList = (List) adjustedList.get(0);
+      for (int i = 1; i < dims; i++) {
+        dimensionLengths[i] = tmpList.size();
+        if (i != dims - 1) {
+          tmpList = (List) tmpList.get(0);
+        }
+      }
+    }
+
+    final Object[] array = decoder.createMultiDimensionalArray(dimensionLengths);
+
+    storeStringValues(array, decoder, adjustedList, dimensionLengths, 0, connection);
+
+    return array;
+  }
+
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  private static <A extends /* @NonNull */ Object> void storeStringValues(A[] array, ArrayDecoder<A> decoder, List list, int /* @NonNull */[] dimensionLengths,
+      int dim, BaseConnection connection) throws SQLException {
+    assert dim <= dimensionLengths.length - 2;
+
+    for (int i = 0; i < dimensionLengths[dim]; ++i) {
+      if (dim == dimensionLengths.length - 2) {
+        decoder.populateFromString(array[i], (List</* @Nullable */ String>) list.get(i), connection);
+      } else {
+        storeStringValues((/* @NonNull */ A /* @NonNull */[]) array[i], decoder, (List) list.get(i), dimensionLengths, dim + 1, connection);
+      }
+    }
+  }
+}
diff --git a/src/main/java/org/postgresql/jdbc/ArrayEncoding.java b/src/main/java/org/postgresql/jdbc/ArrayEncoding.java
new file mode 100644
index 0000000000000000000000000000000000000000..694c891ed0bc78f4980975444782dfa81fc1108c
--- /dev/null
+++ b/src/main/java/org/postgresql/jdbc/ArrayEncoding.java
@@ -0,0 +1,1403 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.Encoding;
+import org.postgresql.core.Oid;
+import org.postgresql.util.ByteConverter;
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+// import org.checkerframework.checker.index.qual.Positive;
+// import org.checkerframework.checker.nullness.qual.NonNull;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.lang.reflect.Array;
+import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Utility for using arrays in requests.
+ *
+ * <p>
+ * Binary format:
+ * <ul>
+ * <li>4 bytes with number of dimensions</li>
+ * <li>4 bytes, boolean indicating nulls present or not</li>
+ * <li>4 bytes type oid</li>
+ * <li>8 bytes describing the length of each dimension (repeated for each dimension)</li>
+ * <ul>
+ * <li>4 bytes for length</li>
+ * <li>4 bytes for lower bound on length to check for overflow (it appears this value can always be 0)</li>
+ * </ul>
+ * <li>data in depth first element order corresponding number and length of dimensions</li>
+ * <ul>
+ * <li>4 bytes describing length of element, {@code 0xFFFFFFFF} ({@code -1}) means {@code null}</li>
+ * <li>binary representation of element (iff not {@code null}).
+ * </ul>
+ * </ul>
+ * </p>
+ *
+ * @author Brett Okken
+ */
+final class ArrayEncoding {
+
+  public interface ArrayEncoder<A> {
+
+    /**
+     * The default array type oid supported by this instance.
+     *
+     * @return The default array type oid supported by this instance.
+     */
+    int getDefaultArrayTypeOid();
+
+    /**
+     * Creates {@code String} representation of the <i>array</i>.
+     *
+     * @param delim
+     *          The character to use to delimit between elements.
+     * @param array
+     *          The array to represent as a {@code String}.
+     * @return {@code String} representation of the <i>array</i>.
+     */
+    String toArrayString(char delim, A array);
+
+    /**
+     * Indicates if an array can be encoded in binary form to array <i>oid</i>.
+     *
+     * @param oid
+     *          The array oid to see check for binary support.
+     * @return Indication of whether
+     *         {@link #toBinaryRepresentation(BaseConnection, Object, int)} is
+     *         supported for <i>oid</i>.
+     */
+    boolean supportBinaryRepresentation(int oid);
+
+    /**
+     * Creates binary representation of the <i>array</i>.
+     *
+     * @param connection
+     *          The connection the binary representation will be used on. Attributes
+     *          from the connection might impact how values are translated to
+     *          binary.
+     * @param array
+     *          The array to binary encode. Must not be {@code null}, but may
+     *          contain {@code null} elements.
+     * @param oid
+     *          The array type oid to use. Calls to
+     *          {@link #supportBinaryRepresentation(int)} must have returned
+     *          {@code true}.
+     * @return The binary representation of <i>array</i>.
+     * @throws SQLFeatureNotSupportedException
+     *           If {@link #supportBinaryRepresentation(int)} is false for
+     *           <i>oid</i>.
+     */
+    byte[] toBinaryRepresentation(BaseConnection connection, A array, int oid)
+        throws SQLException, SQLFeatureNotSupportedException;
+  }
+
+  /**
+   * Base class to implement {@link ArrayEncoding.ArrayEncoder} and provide
+   * multi-dimensional support.
+   *
+   * @param <A>
+   *          Base array type supported.
+   */
+  private abstract static class AbstractArrayEncoder<A extends /* @NonNull */ Object>
+      implements ArrayEncoder<A> {
+
+    private final int oid;
+
+    final int arrayOid;
+
+    /**
+     *
+     * @param oid
+     *          The default/primary base oid type.
+     * @param arrayOid
+     *          The default/primary array oid type.
+     */
+    AbstractArrayEncoder(int oid, int arrayOid) {
+      this.oid = oid;
+      this.arrayOid = arrayOid;
+    }
+
+    /**
+     *
+     * @param arrayOid
+     *          The array oid to get base oid type for.
+     * @return The base oid type for the given array oid type given to
+     *         {@link #toBinaryRepresentation(BaseConnection, Object, int)}.
+     */
+    int getTypeOID(@SuppressWarnings("unused") int arrayOid) {
+      return oid;
+    }
+
+    /**
+     * By default returns the <i>arrayOid</i> this instance was instantiated with.
+     */
+    @Override
+    public int getDefaultArrayTypeOid() {
+      return arrayOid;
+    }
+
+    /**
+     * Counts the number of {@code null} elements in <i>array</i>.
+     *
+     * @param array
+     *          The array to count {@code null} elements in.
+     * @return The number of {@code null} elements in <i>array</i>.
+     */
+    int countNulls(A array) {
+      int nulls = 0;
+      final int arrayLength = Array.getLength(array);
+      for (int i = 0; i < arrayLength; ++i) {
+        if (Array.get(array, i) == null) {
+          ++nulls;
+        }
+      }
+      return nulls;
+    }
+
+    /**
+     * Creates {@code byte[]} of just the raw data (no metadata).
+     *
+     * @param connection
+     *          The connection the binary representation will be used on.
+     * @param array
+     *          The array to create binary representation of. Will not be
+     *          {@code null}, but may contain {@code null} elements.
+     * @return {@code byte[]} of just the raw data (no metadata).
+     * @throws SQLFeatureNotSupportedException
+     *           If {@link #supportBinaryRepresentation(int)} is false for
+     *           <i>oid</i>.
+     */
+    abstract byte[] toSingleDimensionBinaryRepresentation(BaseConnection connection, A array)
+        throws SQLException, SQLFeatureNotSupportedException;
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toArrayString(char delim, A array) {
+      final StringBuilder sb = new StringBuilder(1024);
+      appendArray(sb, delim, array);
+      return sb.toString();
+    }
+
+    /**
+     * Append {@code String} representation of <i>array</i> to <i>sb</i>.
+     *
+     * @param sb
+     *          The {@link StringBuilder} to append to.
+     * @param delim
+     *          The delimiter between elements.
+     * @param array
+     *          The array to represent. Will not be {@code null}, but may contain
+     *          {@code null} elements.
+     */
+    abstract void appendArray(StringBuilder sb, char delim, A array);
+
+    /**
+     * By default returns {@code true} if <i>oid</i> matches the <i>arrayOid</i>
+     * this instance was instantiated with.
+     */
+    @Override
+    public boolean supportBinaryRepresentation(int oid) {
+      return oid == arrayOid;
+    }
+  }
+
+  /**
+   * Base class to provide support for {@code Number} based arrays.
+   *
+   * @param <N>
+   *          The base type of array.
+   */
+  private abstract static class NumberArrayEncoder<N extends Number> extends AbstractArrayEncoder<N /* @NonNull */ []> {
+
+    private final int fieldSize;
+
+    /**
+     *
+     * @param fieldSize
+     *          The fixed size to represent each value in binary.
+     * @param oid
+     *          The base type oid.
+     * @param arrayOid
+     *          The array type oid.
+     */
+    NumberArrayEncoder(int fieldSize, int oid, int arrayOid) {
+      super(oid, arrayOid);
+      this.fieldSize = fieldSize;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    final int countNulls(N[] array) {
+      int count = 0;
+      for (int i = 0; i < array.length; ++i) {
+        if (array[i] == null) {
+          ++count;
+        }
+      }
+      return count;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final byte[] toBinaryRepresentation(BaseConnection connection, N[] array, int oid)
+        throws SQLException, SQLFeatureNotSupportedException {
+      assert oid == this.arrayOid;
+
+      final int nullCount = countNulls(array);
+
+      final byte[] bytes = writeBytes(array, nullCount, 20);
+
+      // 1 dimension
+      ByteConverter.int4(bytes, 0, 1);
+      // no null
+      ByteConverter.int4(bytes, 4, nullCount == 0 ? 0 : 1);
+      // oid
+      ByteConverter.int4(bytes, 8, getTypeOID(oid));
+      // length
+      ByteConverter.int4(bytes, 12, array.length);
+
+      return bytes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    final byte[] toSingleDimensionBinaryRepresentation(BaseConnection connection, N[] array)
+        throws SQLException, SQLFeatureNotSupportedException {
+
+      final int nullCount = countNulls(array);
+
+      return writeBytes(array, nullCount, 0);
+    }
+
+    private byte[] writeBytes(final N[] array, final int nullCount, final int offset) {
+      final int length = offset + (4 * array.length) + (fieldSize * (array.length - nullCount));
+      final byte[] bytes = new byte[length];
+
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        if (array[i] == null) {
+          ByteConverter.int4(bytes, idx, -1);
+          idx += 4;
+        } else {
+          ByteConverter.int4(bytes, idx, fieldSize);
+          idx += 4;
+          write(array[i], bytes, idx);
+          idx += fieldSize;
+        }
+      }
+
+      return bytes;
+    }
+
+    /**
+     * Write single value (<i>number</i>) to <i>bytes</i> beginning at
+     * <i>offset</i>.
+     *
+     * @param number
+     *          The value to write to <i>bytes</i>. This will never be {@code null}.
+     * @param bytes
+     *          The {@code byte[]} to write to.
+     * @param offset
+     *          The offset into <i>bytes</i> to write the <i>number</i> value.
+     */
+    protected abstract void write(N number, byte[] bytes, int offset);
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    final void appendArray(StringBuilder sb, char delim, N[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i != 0) {
+          sb.append(delim);
+        }
+        if (array[i] == null) {
+          sb.append('N').append('U').append('L').append('L');
+        } else {
+          sb.append('"');
+          sb.append(array[i].toString());
+          sb.append('"');
+        }
+      }
+      sb.append('}');
+    }
+  }
+
+  /**
+   * Base support for primitive arrays.
+   *
+   * @param <A>
+   *          The primitive array to support.
+   */
+  private abstract static class FixedSizePrimitiveArrayEncoder<A extends /* @NonNull */ Object>
+      extends AbstractArrayEncoder<A> {
+
+    private final int fieldSize;
+
+    FixedSizePrimitiveArrayEncoder(int fieldSize, int oid, int arrayOid) {
+      super(oid, arrayOid);
+      this.fieldSize = fieldSize;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * <p>
+     * Always returns {@code 0}.
+     * </p>
+     */
+    @Override
+    final int countNulls(A array) {
+      return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public final byte[] toBinaryRepresentation(BaseConnection connection, A array, int oid)
+        throws SQLException, SQLFeatureNotSupportedException {
+      assert oid == arrayOid;
+
+      final int arrayLength = Array.getLength(array);
+      final int length = 20 + ((fieldSize + 4) * arrayLength);
+      final byte[] bytes = new byte[length];
+
+      // 1 dimension
+      ByteConverter.int4(bytes, 0, 1);
+      // no null
+      ByteConverter.int4(bytes, 4, 0);
+      // oid
+      ByteConverter.int4(bytes, 8, getTypeOID(oid));
+      // length
+      ByteConverter.int4(bytes, 12, arrayLength);
+
+      write(array, bytes, 20);
+
+      return bytes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    final byte[] toSingleDimensionBinaryRepresentation(BaseConnection connection, A array)
+        throws SQLException, SQLFeatureNotSupportedException {
+      final int length = ((fieldSize + 4) * Array.getLength(array));
+      final byte[] bytes = new byte[length];
+
+      write(array, bytes, 0);
+      return bytes;
+    }
+
+    /**
+     * Write the entire contents of <i>array</i> to <i>bytes</i> starting at
+     * <i>offset</i> without metadata describing type or length.
+     *
+     * @param array
+     *          The array to write.
+     * @param bytes
+     *          The {@code byte[]} to write to.
+     * @param offset
+     *          The offset into <i>bytes</i> to start writing.
+     */
+    protected abstract void write(A array, byte[] bytes, int offset);
+  }
+
+  private static final AbstractArrayEncoder<long /* @NonNull */ []> LONG_ARRAY = new FixedSizePrimitiveArrayEncoder<long /* @NonNull */ []>(8, Oid.INT8,
+      Oid.INT8_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void appendArray(StringBuilder sb, char delim, long[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        sb.append(array[i]);
+      }
+      sb.append('}');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(long[] array, byte[] bytes, int offset) {
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        bytes[idx + 3] = 8;
+        ByteConverter.int8(bytes, idx + 4, array[i]);
+        idx += 12;
+      }
+    }
+  };
+
+  private static final AbstractArrayEncoder<Long /* @NonNull */ []> LONG_OBJ_ARRAY = new NumberArrayEncoder<Long>(8, Oid.INT8,
+      Oid.INT8_ARRAY) {
+
+    @Override
+    protected void write(Long number, byte[] bytes, int offset) {
+      ByteConverter.int8(bytes, offset, number.longValue());
+    }
+  };
+
+  private static final AbstractArrayEncoder<int /* @NonNull */ []> INT_ARRAY = new FixedSizePrimitiveArrayEncoder<int /* @NonNull */ []>(4, Oid.INT4,
+      Oid.INT4_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void appendArray(StringBuilder sb, char delim, int[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        sb.append(array[i]);
+      }
+      sb.append('}');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(int[] array, byte[] bytes, int offset) {
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        bytes[idx + 3] = 4;
+        ByteConverter.int4(bytes, idx + 4, array[i]);
+        idx += 8;
+      }
+    }
+  };
+
+  private static final AbstractArrayEncoder<Integer /* @NonNull */ []> INT_OBJ_ARRAY = new NumberArrayEncoder<Integer>(4, Oid.INT4,
+      Oid.INT4_ARRAY) {
+
+    @Override
+    protected void write(Integer number, byte[] bytes, int offset) {
+      ByteConverter.int4(bytes, offset, number.intValue());
+    }
+  };
+
+  private static final AbstractArrayEncoder<short /* @NonNull */ []> SHORT_ARRAY = new FixedSizePrimitiveArrayEncoder<short /* @NonNull */ []>(2,
+      Oid.INT2, Oid.INT2_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void appendArray(StringBuilder sb, char delim, short[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        sb.append(array[i]);
+      }
+      sb.append('}');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(short[] array, byte[] bytes, int offset) {
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        bytes[idx + 3] = 2;
+        ByteConverter.int2(bytes, idx + 4, array[i]);
+        idx += 6;
+      }
+    }
+  };
+
+  private static final AbstractArrayEncoder<Short /* @NonNull */ []> SHORT_OBJ_ARRAY = new NumberArrayEncoder<Short>(2, Oid.INT2,
+      Oid.INT2_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(Short number, byte[] bytes, int offset) {
+      ByteConverter.int2(bytes, offset, number.shortValue());
+    }
+  };
+
+  private static final AbstractArrayEncoder<double /* @NonNull */ []> DOUBLE_ARRAY = new FixedSizePrimitiveArrayEncoder<double /* @NonNull */ []>(8,
+      Oid.FLOAT8, Oid.FLOAT8_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void appendArray(StringBuilder sb, char delim, double[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        // use quotes to account for any issues with scientific notation
+        sb.append('"');
+        sb.append(array[i]);
+        sb.append('"');
+      }
+      sb.append('}');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(double[] array, byte[] bytes, int offset) {
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        bytes[idx + 3] = 8;
+        ByteConverter.float8(bytes, idx + 4, array[i]);
+        idx += 12;
+      }
+    }
+  };
+
+  private static final AbstractArrayEncoder<Double /* @NonNull */ []> DOUBLE_OBJ_ARRAY = new NumberArrayEncoder<Double>(8, Oid.FLOAT8,
+      Oid.FLOAT8_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(Double number, byte[] bytes, int offset) {
+      ByteConverter.float8(bytes, offset, number.doubleValue());
+    }
+  };
+
+  private static final AbstractArrayEncoder<float /* @NonNull */ []> FLOAT_ARRAY = new FixedSizePrimitiveArrayEncoder<float /* @NonNull */ []>(4,
+      Oid.FLOAT4, Oid.FLOAT4_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void appendArray(StringBuilder sb, char delim, float[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        // use quotes to account for any issues with scientific notation
+        sb.append('"');
+        sb.append(array[i]);
+        sb.append('"');
+      }
+      sb.append('}');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(float[] array, byte[] bytes, int offset) {
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        bytes[idx + 3] = 4;
+        ByteConverter.float4(bytes, idx + 4, array[i]);
+        idx += 8;
+      }
+    }
+  };
+
+  private static final AbstractArrayEncoder<Float /* @NonNull */ []> FLOAT_OBJ_ARRAY = new NumberArrayEncoder<Float>(4, Oid.FLOAT4,
+      Oid.FLOAT4_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(Float number, byte[] bytes, int offset) {
+      ByteConverter.float4(bytes, offset, number.floatValue());
+    }
+  };
+
+  private static final AbstractArrayEncoder<boolean /* @NonNull */ []> BOOLEAN_ARRAY = new FixedSizePrimitiveArrayEncoder<boolean /* @NonNull */ []>(1,
+      Oid.BOOL, Oid.BOOL_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void appendArray(StringBuilder sb, char delim, boolean[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        sb.append(array[i] ? '1' : '0');
+      }
+      sb.append('}');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void write(boolean[] array, byte[] bytes, int offset) {
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        bytes[idx + 3] = 1;
+        ByteConverter.bool(bytes, idx + 4, array[i]);
+        idx += 5;
+      }
+    }
+  };
+
+  private static final AbstractArrayEncoder<Boolean /* @NonNull */ []> BOOLEAN_OBJ_ARRAY = new AbstractArrayEncoder<Boolean /* @NonNull */ []>(Oid.BOOL,
+      Oid.BOOL_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public byte[] toBinaryRepresentation(BaseConnection connection, Boolean[] array, int oid)
+        throws SQLException, SQLFeatureNotSupportedException {
+      assert oid == arrayOid;
+
+      final int nullCount = countNulls(array);
+
+      final byte[] bytes = writeBytes(array, nullCount, 20);
+
+      // 1 dimension
+      ByteConverter.int4(bytes, 0, 1);
+      // no null
+      ByteConverter.int4(bytes, 4, nullCount == 0 ? 0 : 1);
+      // oid
+      ByteConverter.int4(bytes, 8, getTypeOID(oid));
+      // length
+      ByteConverter.int4(bytes, 12, array.length);
+
+      return bytes;
+    }
+
+    private byte[] writeBytes(final Boolean[] array, final int nullCount, final int offset) {
+      final int length = offset + (4 * array.length) + (array.length - nullCount);
+      final byte[] bytes = new byte[length];
+
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        if (array[i] == null) {
+          ByteConverter.int4(bytes, idx, -1);
+          idx += 4;
+        } else {
+          ByteConverter.int4(bytes, idx, 1);
+          idx += 4;
+          write(array[i], bytes, idx);
+          ++idx;
+        }
+      }
+
+      return bytes;
+    }
+
+    private void write(Boolean bool, byte[] bytes, int idx) {
+      ByteConverter.bool(bytes, idx, bool.booleanValue());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    byte[] toSingleDimensionBinaryRepresentation(BaseConnection connection, Boolean[] array)
+        throws SQLException, SQLFeatureNotSupportedException {
+      final int nullCount = countNulls(array);
+      return writeBytes(array, nullCount, 0);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void appendArray(StringBuilder sb, char delim, Boolean[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i != 0) {
+          sb.append(delim);
+        }
+        if (array[i] == null) {
+          sb.append('N').append('U').append('L').append('L');
+        } else {
+          sb.append(array[i].booleanValue() ? '1' : '0');
+        }
+      }
+      sb.append('}');
+    }
+  };
+
+  private static final AbstractArrayEncoder<String /* @NonNull */ []> STRING_ARRAY = new AbstractArrayEncoder<String /* @NonNull */ []>(Oid.VARCHAR,
+      Oid.VARCHAR_ARRAY) {
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    int countNulls(String[] array) {
+      int count = 0;
+      for (int i = 0; i < array.length; ++i) {
+        if (array[i] == null) {
+          ++count;
+        }
+      }
+      return count;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean supportBinaryRepresentation(int oid) {
+      return oid == Oid.VARCHAR_ARRAY || oid == Oid.TEXT_ARRAY;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    int getTypeOID(int arrayOid) {
+      if (arrayOid == Oid.VARCHAR_ARRAY) {
+        return Oid.VARCHAR;
+      }
+
+      if (arrayOid == Oid.TEXT_ARRAY) {
+        return Oid.TEXT;
+      }
+
+      // this should not be possible based on supportBinaryRepresentation returning
+      // false for all other types
+      throw new IllegalStateException("Invalid array oid: " + arrayOid);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void appendArray(StringBuilder sb, char delim, String[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        if (array[i] == null) {
+          sb.append('N').append('U').append('L').append('L');
+        } else {
+          PgArray.escapeArrayElement(sb, array[i]);
+        }
+      }
+      sb.append('}');
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public byte[] toBinaryRepresentation(BaseConnection connection, String[] array, int oid) throws SQLException {
+      final ByteArrayOutputStream baos = new ByteArrayOutputStream(Math.min(1024, (array.length * 32) + 20));
+
+      assert supportBinaryRepresentation(oid);
+
+      final byte[] buffer = new byte[4];
+
+      try {
+        // 1 dimension
+        ByteConverter.int4(buffer, 0, 1);
+        baos.write(buffer);
+        // null
+        ByteConverter.int4(buffer, 0, countNulls(array) > 0 ? 1 : 0);
+        baos.write(buffer);
+        // oid
+        ByteConverter.int4(buffer, 0, getTypeOID(oid));
+        baos.write(buffer);
+        // length
+        ByteConverter.int4(buffer, 0, array.length);
+        baos.write(buffer);
+
+        // write 4 empty bytes
+        java.util.Arrays.fill(buffer, (byte) 0);
+        baos.write(buffer);
+
+        final Encoding encoding = connection.getEncoding();
+        for (int i = 0; i < array.length; ++i) {
+          final String string = array[i];
+          if (string != null) {
+            final byte[] encoded;
+            try {
+              encoded = encoding.encode(string);
+            } catch (IOException e) {
+              throw new PSQLException(GT.tr("Unable to translate data into the desired encoding."),
+                  PSQLState.DATA_ERROR, e);
+            }
+            ByteConverter.int4(buffer, 0, encoded.length);
+            baos.write(buffer);
+            baos.write(encoded);
+          } else {
+            ByteConverter.int4(buffer, 0, -1);
+            baos.write(buffer);
+          }
+        }
+
+        return baos.toByteArray();
+      } catch (IOException e) {
+        // this IO exception is from writing to baos, which will never throw an
+        // IOException
+        throw new java.lang.AssertionError(e);
+      }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    byte[] toSingleDimensionBinaryRepresentation(BaseConnection connection, String[] array)
+        throws SQLException, SQLFeatureNotSupportedException {
+      try {
+        final ByteArrayOutputStream baos = new ByteArrayOutputStream(Math.min(1024, (array.length * 32) + 20));
+        final byte[] buffer = new byte[4];
+        final Encoding encoding = connection.getEncoding();
+        for (int i = 0; i < array.length; ++i) {
+          final String string = array[i];
+          if (string != null) {
+            final byte[] encoded;
+            try {
+              encoded = encoding.encode(string);
+            } catch (IOException e) {
+              throw new PSQLException(GT.tr("Unable to translate data into the desired encoding."),
+                  PSQLState.DATA_ERROR, e);
+            }
+            ByteConverter.int4(buffer, 0, encoded.length);
+            baos.write(buffer);
+            baos.write(encoded);
+          } else {
+            ByteConverter.int4(buffer, 0, -1);
+            baos.write(buffer);
+          }
+        }
+
+        return baos.toByteArray();
+      } catch (IOException e) {
+        // this IO exception is from writing to baos, which will never throw an
+        // IOException
+        throw new java.lang.AssertionError(e);
+      }
+    }
+  };
+
+  private static final AbstractArrayEncoder<byte[] /* @NonNull */ []> BYTEA_ARRAY = new AbstractArrayEncoder<byte[] /* @NonNull */ []>(Oid.BYTEA,
+      Oid.BYTEA_ARRAY) {
+
+    /**
+     * The possible characters to use for representing hex binary data.
+     */
+    private final char[] hexDigits = new char[] { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd',
+        'e', 'f' };
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public byte[] toBinaryRepresentation(BaseConnection connection, byte[][] array, int oid)
+        throws SQLException, SQLFeatureNotSupportedException {
+
+      assert oid == arrayOid;
+
+      int length = 20;
+      for (int i = 0; i < array.length; ++i) {
+        length += 4;
+        if (array[i] != null) {
+          length += array[i].length;
+        }
+      }
+      final byte[] bytes = new byte[length];
+
+      // 1 dimension
+      ByteConverter.int4(bytes, 0, 1);
+      // no null
+      ByteConverter.int4(bytes, 4, 0);
+      // oid
+      ByteConverter.int4(bytes, 8, getTypeOID(oid));
+      // length
+      ByteConverter.int4(bytes, 12, array.length);
+
+      write(array, bytes, 20);
+
+      return bytes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    byte[] toSingleDimensionBinaryRepresentation(BaseConnection connection, byte[][] array)
+        throws SQLException, SQLFeatureNotSupportedException {
+      int length = 0;
+      for (int i = 0; i < array.length; ++i) {
+        length += 4;
+        if (array[i] != null) {
+          length += array[i].length;
+        }
+      }
+      final byte[] bytes = new byte[length];
+
+      write(array, bytes, 0);
+      return bytes;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    int countNulls(byte[][] array) {
+      int nulls = 0;
+      for (int i = 0; i < array.length; ++i) {
+        if (array[i] == null) {
+          ++nulls;
+        }
+      }
+      return nulls;
+    }
+
+    private void write(byte[][] array, byte[] bytes, int offset) {
+      int idx = offset;
+      for (int i = 0; i < array.length; ++i) {
+        if (array[i] != null) {
+          ByteConverter.int4(bytes, idx, array[i].length);
+          idx += 4;
+          System.arraycopy(array[i], 0, bytes, idx, array[i].length);
+          idx += array[i].length;
+        } else {
+          ByteConverter.int4(bytes, idx, -1);
+          idx += 4;
+        }
+      }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void appendArray(StringBuilder sb, char delim, byte[][] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+
+        if (array[i] != null) {
+          sb.append("\"\\\\x");
+          for (int j = 0; j < array[i].length; ++j) {
+            byte b = array[i][j];
+
+            // get the value for the left 4 bits (drop sign)
+            sb.append(hexDigits[(b & 0xF0) >>> 4]);
+            // get the value for the right 4 bits
+            sb.append(hexDigits[b & 0x0F]);
+          }
+          sb.append('"');
+        } else {
+          sb.append("NULL");
+        }
+      }
+      sb.append('}');
+    }
+  };
+
+  private static final AbstractArrayEncoder<Object /* @NonNull */ []> OBJECT_ARRAY = new AbstractArrayEncoder<Object /* @NonNull */ []>(0, 0) {
+
+    @Override
+    public int getDefaultArrayTypeOid() {
+      return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean supportBinaryRepresentation(int oid) {
+      return false;
+    }
+
+    @Override
+    public byte[] toBinaryRepresentation(BaseConnection connection, Object[] array, int oid)
+        throws SQLException, SQLFeatureNotSupportedException {
+      throw new SQLFeatureNotSupportedException();
+    }
+
+    @Override
+    byte[] toSingleDimensionBinaryRepresentation(BaseConnection connection, Object[] array)
+        throws SQLException, SQLFeatureNotSupportedException {
+      throw new SQLFeatureNotSupportedException();
+    }
+
+    @Override
+    void appendArray(StringBuilder sb, char delim, Object[] array) {
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        if (array[i] == null) {
+          sb.append('N').append('U').append('L').append('L');
+        } else {
+          PgArray.escapeArrayElement(sb, array[i].toString());
+        }
+      }
+      sb.append('}');
+    }
+  };
+
+  @SuppressWarnings("rawtypes")
+  private static final Map</* @NonNull */ Class, /* @NonNull */ AbstractArrayEncoder> ARRAY_CLASS_TO_ENCODER = new HashMap</* @NonNull */ Class, /* @NonNull */ AbstractArrayEncoder>(
+      (int) (14 / .75) + 1);
+
+  static {
+    ARRAY_CLASS_TO_ENCODER.put(long.class, LONG_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(Long.class, LONG_OBJ_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(int.class, INT_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(Integer.class, INT_OBJ_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(short.class, SHORT_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(Short.class, SHORT_OBJ_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(double.class, DOUBLE_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(Double.class, DOUBLE_OBJ_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(float.class, FLOAT_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(Float.class, FLOAT_OBJ_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(boolean.class, BOOLEAN_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(Boolean.class, BOOLEAN_OBJ_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(byte[].class, BYTEA_ARRAY);
+    ARRAY_CLASS_TO_ENCODER.put(String.class, STRING_ARRAY);
+  }
+
+  /**
+   * Returns support for encoding <i>array</i>.
+   *
+   * @param array
+   *          The array to encode. Must not be {@code null}.
+   * @return An instance capable of encoding <i>array</i> as a {@code String} at
+   *         minimum. Some types may support binary encoding.
+   * @throws PSQLException
+   *           if <i>array</i> is not a supported type.
+   * @see ArrayEncoding.ArrayEncoder#supportBinaryRepresentation(int)
+   */
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  public static <A extends /* @NonNull */ Object> ArrayEncoder<A> getArrayEncoder(A array) throws PSQLException {
+    final Class<?> arrayClazz = array.getClass();
+    Class<?> subClazz = arrayClazz.getComponentType();
+    if (subClazz == null) {
+      throw new PSQLException(GT.tr("Invalid elements {0}", array), PSQLState.INVALID_PARAMETER_TYPE);
+    }
+    AbstractArrayEncoder<A> support = ARRAY_CLASS_TO_ENCODER.get(subClazz);
+    if (support != null) {
+      return support;
+    }
+    Class<?> subSubClazz = subClazz.getComponentType();
+    if (subSubClazz == null) {
+      if (Object.class.isAssignableFrom(subClazz)) {
+        return (ArrayEncoder<A>) OBJECT_ARRAY;
+      }
+      throw new PSQLException(GT.tr("Invalid elements {0}", array), PSQLState.INVALID_PARAMETER_TYPE);
+    }
+
+    subClazz = subSubClazz;
+    int dimensions = 2;
+    while (subClazz != null) {
+      support = ARRAY_CLASS_TO_ENCODER.get(subClazz);
+      if (support != null) {
+        if (dimensions == 2) {
+          return new TwoDimensionPrimitiveArrayEncoder(support);
+        }
+        return new RecursiveArrayEncoder(support, dimensions);
+      }
+      subSubClazz = subClazz.getComponentType();
+      if (subSubClazz == null) {
+        if (Object.class.isAssignableFrom(subClazz)) {
+          if (dimensions == 2) {
+            return new TwoDimensionPrimitiveArrayEncoder(OBJECT_ARRAY);
+          }
+          return new RecursiveArrayEncoder(OBJECT_ARRAY, dimensions);
+        }
+      }
+      ++dimensions;
+      subClazz = subSubClazz;
+    }
+
+    throw new PSQLException(GT.tr("Invalid elements {0}", array), PSQLState.INVALID_PARAMETER_TYPE);
+  }
+
+  /**
+   * Wraps an {@link AbstractArrayEncoder} implementation and provides optimized
+   * support for 2 dimensions.
+   */
+  private static final class TwoDimensionPrimitiveArrayEncoder<A extends /* @NonNull */ Object> implements ArrayEncoder<A /* @NonNull */ []> {
+    private final AbstractArrayEncoder<A> support;
+
+    /**
+     * @param support
+     *          The instance providing support for the base array type.
+     */
+    TwoDimensionPrimitiveArrayEncoder(AbstractArrayEncoder<A> support) {
+      super();
+      this.support = support;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getDefaultArrayTypeOid() {
+      return support.getDefaultArrayTypeOid();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toArrayString(char delim, A /* @NonNull */[] array) {
+      final StringBuilder sb = new StringBuilder(1024);
+      sb.append('{');
+      for (int i = 0; i < array.length; ++i) {
+        if (i > 0) {
+          sb.append(delim);
+        }
+        support.appendArray(sb, delim, array[i]);
+      }
+      sb.append('}');
+      return sb.toString();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean supportBinaryRepresentation(int oid) {
+      return support.supportBinaryRepresentation(oid);
+    }
+
+    /**
+     * {@inheritDoc} 4 bytes - dimension 4 bytes - oid 4 bytes - ? 8*d bytes -
+     * dimension length
+     */
+    @Override
+    public byte[] toBinaryRepresentation(BaseConnection connection, A[] array, int oid)
+        throws SQLException, SQLFeatureNotSupportedException {
+      final ByteArrayOutputStream baos = new ByteArrayOutputStream(Math.min(1024, (array.length * 32) + 20));
+      final byte[] buffer = new byte[4];
+
+      boolean hasNulls = false;
+      for (int i = 0; !hasNulls && i < array.length; ++i) {
+        if (support.countNulls(array[i]) > 0) {
+          hasNulls = true;
+        }
+      }
+
+      try {
+        // 2 dimension
+        ByteConverter.int4(buffer, 0, 2);
+        baos.write(buffer);
+        // nulls
+        ByteConverter.int4(buffer, 0, hasNulls ? 1 : 0);
+        baos.write(buffer);
+        // oid
+        ByteConverter.int4(buffer, 0, support.getTypeOID(oid));
+        baos.write(buffer);
+
+        // length
+        ByteConverter.int4(buffer, 0, array.length);
+        baos.write(buffer);
+        // write 4 empty bytes
+        java.util.Arrays.fill(buffer, (byte) 0);
+        baos.write(buffer);
+
+        ByteConverter.int4(buffer, 0, array.length > 0 ? Array.getLength(array[0]) : 0);
+        baos.write(buffer);
+        // write 4 empty bytes
+        java.util.Arrays.fill(buffer, (byte) 0);
+        baos.write(buffer);
+
+        for (int i = 0; i < array.length; ++i) {
+          baos.write(support.toSingleDimensionBinaryRepresentation(connection, array[i]));
+        }
+
+        return baos.toByteArray();
+
+      } catch (IOException e) {
+        // this IO exception is from writing to baos, which will never throw an
+        // IOException
+        throw new java.lang.AssertionError(e);
+      }
+    }
+  }
+
+  /**
+   * Wraps an {@link AbstractArrayEncoder} implementation and provides support for
+   * 2 or more dimensions using recursion.
+   */
+  @SuppressWarnings({ "unchecked", "rawtypes" })
+  private static final class RecursiveArrayEncoder implements ArrayEncoder {
+
+    private final AbstractArrayEncoder support;
+    private final /* @Positive */ int dimensions;
+
+    /**
+     * @param support
+     *          The instance providing support for the base array type.
+     */
+    RecursiveArrayEncoder(AbstractArrayEncoder support, /* @Positive */ int dimensions) {
+      super();
+      this.support = support;
+      this.dimensions = dimensions;
+      assert dimensions >= 2;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public int getDefaultArrayTypeOid() {
+      return support.getDefaultArrayTypeOid();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String toArrayString(char delim, Object array) {
+      final StringBuilder sb = new StringBuilder(2048);
+      arrayString(sb, array, delim, dimensions);
+      return sb.toString();
+    }
+
+    private void arrayString(StringBuilder sb, Object array, char delim, int depth) {
+
+      if (depth > 1) {
+        sb.append('{');
+        for (int i = 0, j = Array.getLength(array); i < j; ++i) {
+          if (i > 0) {
+            sb.append(delim);
+          }
+          arrayString(sb, Array.get(array, i), delim, depth - 1);
+        }
+        sb.append('}');
+      } else {
+        support.appendArray(sb, delim, array);
+      }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean supportBinaryRepresentation(int oid) {
+      return support.supportBinaryRepresentation(oid);
+    }
+
+    private boolean hasNulls(Object array, int depth) {
+      if (depth > 1) {
+        for (int i = 0, j = Array.getLength(array); i < j; ++i) {
+          if (hasNulls(Array.get(array, i), depth - 1)) {
+            return true;
+          }
+        }
+        return false;
+      }
+
+      return support.countNulls(array) > 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public byte[] toBinaryRepresentation(BaseConnection connection, Object array, int oid)
+        throws SQLException, SQLFeatureNotSupportedException {
+
+      final boolean hasNulls = hasNulls(array, dimensions);
+
+      final ByteArrayOutputStream baos = new ByteArrayOutputStream(1024 * dimensions);
+      final byte[] buffer = new byte[4];
+
+      try {
+        // dimensions
+        ByteConverter.int4(buffer, 0, dimensions);
+        baos.write(buffer);
+        // nulls
+        ByteConverter.int4(buffer, 0, hasNulls ? 1 : 0);
+        baos.write(buffer);
+        // oid
+        ByteConverter.int4(buffer, 0, support.getTypeOID(oid));
+        baos.write(buffer);
+
+        // length
+        ByteConverter.int4(buffer, 0, Array.getLength(array));
+        baos.write(buffer);
+        // write 4 empty bytes for lower bounds value. this is
+        java.util.Arrays.fill(buffer, (byte) 0);
+        baos.write(buffer);
+
+        writeArray(connection, buffer, baos, array, dimensions, true);
+
+        return baos.toByteArray();
+
+      } catch (IOException e) {
+        // this IO exception is from writing to baos, which will never throw an
+        // IOException
+        throw new java.lang.AssertionError(e);
+      }
+    }
+
+    private void writeArray(BaseConnection connection, byte[] buffer, ByteArrayOutputStream baos,
+        Object array, int depth, boolean first) throws IOException, SQLException {
+      final int length = Array.getLength(array);
+
+      if (first) {
+        ByteConverter.int4(buffer, 0, length > 0 ? Array.getLength(Array.get(array, 0)) : 0);
+        baos.write(buffer);
+        // write 4 empty bytes
+        java.util.Arrays.fill(buffer, (byte) 0);
+        baos.write(buffer);
+      }
+
+      for (int i = 0; i < length; ++i) {
+        final Object subArray = Array.get(array, i);
+        if (depth > 2) {
+          writeArray(connection, buffer, baos, subArray, depth - 1, i == 0);
+        } else {
+          baos.write(support.toSingleDimensionBinaryRepresentation(connection, subArray));
+        }
+      }
+    }
+
+  }
+}
diff --git a/src/main/java/org/postgresql/jdbc/BatchResultHandler.java b/src/main/java/org/postgresql/jdbc/BatchResultHandler.java
index b2df750080b4008c57467fbcc5cda9adaf25b47b..09171fee213943f43def6cc941fb2f441f7fa482 100644
--- a/src/main/java/org/postgresql/jdbc/BatchResultHandler.java
+++ b/src/main/java/org/postgresql/jdbc/BatchResultHandler.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.Field;
 import org.postgresql.core.ParameterList;
 import org.postgresql.core.Query;
@@ -16,6 +18,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.BatchUpdateException;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -35,15 +39,16 @@ public class BatchResultHandler extends ResultHandlerBase {
 
   private final Query[] queries;
   private final long[] longUpdateCounts;
-  private final ParameterList[] parameterLists;
+  private final /* @Nullable */ ParameterList /* @Nullable */ [] parameterLists;
   private final boolean expectGeneratedKeys;
-  private PgResultSet generatedKeys;
+  private /* @Nullable */ PgResultSet generatedKeys;
   private int committedRows; // 0 means no rows committed. 1 means row 0 was committed, and so on
-  private final List<List<Tuple>> allGeneratedRows;
-  private List<Tuple> latestGeneratedRows;
-  private PgResultSet latestGeneratedKeysRs;
+  private final /* @Nullable */ List<List<Tuple>> allGeneratedRows;
+  private /* @Nullable */ List<Tuple> latestGeneratedRows;
+  private /* @Nullable */ PgResultSet latestGeneratedKeysRs;
 
-  BatchResultHandler(PgStatement pgStatement, Query[] queries, ParameterList[] parameterLists,
+  BatchResultHandler(PgStatement pgStatement, Query[] queries,
+      /* @Nullable */ ParameterList /* @Nullable */ [] parameterLists,
       boolean expectGeneratedKeys) {
     this.pgStatement = pgStatement;
     this.queries = queries;
@@ -55,7 +60,7 @@ public class BatchResultHandler extends ResultHandlerBase {
 
   @Override
   public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
-      ResultCursor cursor) {
+      /* @Nullable */ ResultCursor cursor) {
     // If SELECT, then handleCommandStatus call would just be missing
     resultIndex++;
     if (!expectGeneratedKeys) {
@@ -77,18 +82,20 @@ public class BatchResultHandler extends ResultHandlerBase {
 
   @Override
   public void handleCommandStatus(String status, long updateCount, long insertOID) {
+    List<Tuple> latestGeneratedRows = this.latestGeneratedRows;
     if (latestGeneratedRows != null) {
       // We have DML. Decrease resultIndex that was just increased in handleResultRows
       resultIndex--;
       // If exception thrown, no need to collect generated keys
       // Note: some generated keys might be secured in generatedKeys
       if (updateCount > 0 && (getException() == null || isAutoCommit())) {
+        List<List<Tuple>> allGeneratedRows = castNonNull(this.allGeneratedRows, "allGeneratedRows");
         allGeneratedRows.add(latestGeneratedRows);
         if (generatedKeys == null) {
           generatedKeys = latestGeneratedKeysRs;
         }
       }
-      latestGeneratedRows = null;
+      this.latestGeneratedRows = null;
     }
 
     if (resultIndex >= queries.length) {
@@ -119,9 +126,11 @@ public class BatchResultHandler extends ResultHandlerBase {
   }
 
   private void updateGeneratedKeys() {
+    List<List<Tuple>> allGeneratedRows = this.allGeneratedRows;
     if (allGeneratedRows == null || allGeneratedRows.isEmpty()) {
       return;
     }
+    PgResultSet generatedKeys = castNonNull(this.generatedKeys, "generatedKeys");
     for (List<Tuple> rows : allGeneratedRows) {
       generatedKeys.addRows(rows);
     }
@@ -143,7 +152,8 @@ public class BatchResultHandler extends ResultHandlerBase {
 
       String queryString = "<unknown>";
       if (resultIndex < queries.length) {
-        queryString = queries[resultIndex].toString(parameterLists[resultIndex]);
+        queryString = queries[resultIndex].toString(
+            parameterLists == null ? null : parameterLists[resultIndex]);
       }
 
       BatchUpdateException batchException;
@@ -200,7 +210,7 @@ public class BatchResultHandler extends ResultHandlerBase {
     }
   }
 
-  public ResultSet getGeneratedKeys() {
+  public /* @Nullable */ ResultSet getGeneratedKeys() {
     return generatedKeys;
   }
 
diff --git a/src/main/java/org/postgresql/jdbc/BooleanTypeUtil.java b/src/main/java/org/postgresql/jdbc/BooleanTypeUtil.java
index 84080db22edbaa659d36127759a09f433a125e52..b7b22251558d8eaf5001080e9718cf09e8f81f6e 100644
--- a/src/main/java/org/postgresql/jdbc/BooleanTypeUtil.java
+++ b/src/main/java/org/postgresql/jdbc/BooleanTypeUtil.java
@@ -51,7 +51,7 @@ class BooleanTypeUtil {
     throw new PSQLException("Cannot cast to boolean", PSQLState.CANNOT_COERCE);
   }
 
-  private static boolean fromString(final String strval) throws PSQLException {
+  static boolean fromString(final String strval) throws PSQLException {
     // Leading or trailing whitespace is ignored, and case does not matter.
     final String val = strval.trim();
     if ("1".equals(val) || "true".equalsIgnoreCase(val)
diff --git a/src/main/java/org/postgresql/jdbc/CallableBatchResultHandler.java b/src/main/java/org/postgresql/jdbc/CallableBatchResultHandler.java
index e38bd52f607f7fb435a05921947fe1ae99874765..3dfc8da65dcc0287e658dae9f97321f50d364e33 100644
--- a/src/main/java/org/postgresql/jdbc/CallableBatchResultHandler.java
+++ b/src/main/java/org/postgresql/jdbc/CallableBatchResultHandler.java
@@ -11,14 +11,18 @@ import org.postgresql.core.Query;
 import org.postgresql.core.ResultCursor;
 import org.postgresql.core.Tuple;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.List;
 
 class CallableBatchResultHandler extends BatchResultHandler {
-  CallableBatchResultHandler(PgStatement statement, Query[] queries, ParameterList[] parameterLists) {
+  CallableBatchResultHandler(PgStatement statement, Query[] queries,
+      /* @Nullable */ ParameterList[] parameterLists) {
     super(statement, queries, parameterLists, false);
   }
 
-  public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples, ResultCursor cursor) {
+  public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
+      /* @Nullable */ ResultCursor cursor) {
     /* ignore */
   }
 }
diff --git a/src/main/java/org/postgresql/jdbc/EscapedFunctions.java b/src/main/java/org/postgresql/jdbc/EscapedFunctions.java
index 99bf94898c441871ab28e26a99bcd60a8468560c..bd884db9c21b75db5124b8f771f9ec554cdd253f 100644
--- a/src/main/java/org/postgresql/jdbc/EscapedFunctions.java
+++ b/src/main/java/org/postgresql/jdbc/EscapedFunctions.java
@@ -9,6 +9,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.reflect.Method;
 import java.sql.SQLException;
 import java.util.HashMap;
@@ -134,7 +136,7 @@ public class EscapedFunctions {
    * @param functionName name of the searched function
    * @return a Method object or null if not found
    */
-  public static Method getFunction(String functionName) {
+  public static /* @Nullable */ Method getFunction(String functionName) {
     return functionMap.get("sql" + functionName.toLowerCase(Locale.US));
   }
 
@@ -578,7 +580,8 @@ public class EscapedFunctions {
    * @return sql call
    * @throws SQLException if something wrong happens
    */
-  public static String sqltimestampadd(List<?> parsedArgs) throws SQLException {
+  @SuppressWarnings("TypeParameterExplicitlyExtendsObject")
+  public static String sqltimestampadd(List<? extends Object> parsedArgs) throws SQLException {
     if (parsedArgs.size() != 3) {
       throw new PSQLException(
           GT.tr("{0} function takes three and only three arguments.", "timestampadd"),
@@ -630,7 +633,8 @@ public class EscapedFunctions {
    * @return sql call
    * @throws SQLException if something wrong happens
    */
-  public static String sqltimestampdiff(List<?> parsedArgs) throws SQLException {
+  @SuppressWarnings("TypeParameterExplicitlyExtendsObject")
+  public static String sqltimestampdiff(List<? extends Object> parsedArgs) throws SQLException {
     if (parsedArgs.size() != 3) {
       throw new PSQLException(
           GT.tr("{0} function takes three and only three arguments.", "timestampdiff"),
diff --git a/src/main/java/org/postgresql/jdbc/EscapedFunctions2.java b/src/main/java/org/postgresql/jdbc/EscapedFunctions2.java
index 65205f47e5aed06bde7e1c6eba82cb8e7c873a27..99110341853469fdbe5181ee8465ba884e7e4570 100644
--- a/src/main/java/org/postgresql/jdbc/EscapedFunctions2.java
+++ b/src/main/java/org/postgresql/jdbc/EscapedFunctions2.java
@@ -9,6 +9,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.reflect.Method;
 import java.sql.SQLException;
 import java.util.List;
@@ -55,7 +57,7 @@ public final class EscapedFunctions2 {
    * @param functionName name of the searched function
    * @return a Method object or null if not found
    */
-  public static Method getFunction(String functionName) {
+  public static /* @Nullable */ Method getFunction(String functionName) {
     Method method = FUNCTION_MAP.get(functionName);
     if (method != null) {
       return method;
diff --git a/src/main/java/org/postgresql/jdbc/FieldMetadata.java b/src/main/java/org/postgresql/jdbc/FieldMetadata.java
index 3f4363ce18b97724af1b69c54f8105081a1bff9d..37fcec39668a787701a3652ee5aa8d8ab271fec8 100644
--- a/src/main/java/org/postgresql/jdbc/FieldMetadata.java
+++ b/src/main/java/org/postgresql/jdbc/FieldMetadata.java
@@ -7,6 +7,8 @@ package org.postgresql.jdbc;
 
 import org.postgresql.util.CanEstimateSize;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * This is an internal class to hold field metadata info like table name, column name, etc.
  * This class is not meant to be used outside of pgjdbc.
@@ -22,7 +24,7 @@ public class FieldMetadata implements CanEstimateSize {
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(/* @Nullable */ Object o) {
       if (this == o) {
         return true;
       }
diff --git a/src/main/java/org/postgresql/jdbc/GSSEncMode.java b/src/main/java/org/postgresql/jdbc/GSSEncMode.java
new file mode 100644
index 0000000000000000000000000000000000000000..f52921d2023857658514fc86388f81bc3e9c2659
--- /dev/null
+++ b/src/main/java/org/postgresql/jdbc/GSSEncMode.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.PGProperty;
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+import java.util.Properties;
+
+public enum GSSEncMode {
+
+  /**
+   * Do not use encrypted connections.
+   */
+  DISABLE("disable"),
+
+  /**
+   * Start with non-encrypted connection, then try encrypted one.
+   */
+  ALLOW("allow"),
+
+  /**
+   * Start with encrypted connection, fallback to non-encrypted (default).
+   */
+  PREFER("prefer"),
+
+  /**
+   * Ensure connection is encrypted.
+   */
+  REQUIRE("require");
+
+  private static final GSSEncMode[] VALUES = values();
+
+  public final String value;
+
+  GSSEncMode(String value) {
+    this.value = value;
+  }
+
+  public boolean requireEncryption() {
+    return this.compareTo(REQUIRE) >= 0;
+  }
+
+  public static GSSEncMode of(Properties info) throws PSQLException {
+    String gssEncMode = PGProperty.GSS_ENC_MODE.get(info);
+    // If gssEncMode is not set, fallback to prefer
+    if (gssEncMode == null) {
+      return PREFER;
+    }
+
+    for (GSSEncMode mode : VALUES) {
+      if (mode.value.equalsIgnoreCase(gssEncMode)) {
+        return mode;
+      }
+    }
+    throw new PSQLException(GT.tr("Invalid gssEncMode value: {0}", gssEncMode),
+        PSQLState.CONNECTION_UNABLE_TO_CONNECT);
+  }
+
+}
diff --git a/src/main/java/org/postgresql/jdbc/PSQLSavepoint.java b/src/main/java/org/postgresql/jdbc/PSQLSavepoint.java
index daf436ac692add0b7e986b20742eecbf357426fe..0f90b8bb5275595ece23704318806c5a27bf920e 100644
--- a/src/main/java/org/postgresql/jdbc/PSQLSavepoint.java
+++ b/src/main/java/org/postgresql/jdbc/PSQLSavepoint.java
@@ -10,6 +10,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 import java.sql.Savepoint;
 
@@ -18,7 +20,7 @@ public class PSQLSavepoint implements Savepoint {
   private boolean isValid;
   private final boolean isNamed;
   private int id;
-  private String name;
+  private /* @Nullable */ String name;
 
   public PSQLSavepoint(int id) {
     this.isValid = true;
@@ -54,7 +56,7 @@ public class PSQLSavepoint implements Savepoint {
           PSQLState.INVALID_SAVEPOINT_SPECIFICATION);
     }
 
-    if (!isNamed) {
+    if (!isNamed || name == null) {
       throw new PSQLException(GT.tr("Cannot retrieve the name of an unnamed savepoint."),
           PSQLState.WRONG_OBJECT_TYPE);
     }
@@ -72,7 +74,7 @@ public class PSQLSavepoint implements Savepoint {
           PSQLState.INVALID_SAVEPOINT_SPECIFICATION);
     }
 
-    if (isNamed) {
+    if (isNamed && name != null) {
       // We need to quote and escape the name in case it
       // contains spaces/quotes/etc.
       //
diff --git a/src/main/java/org/postgresql/jdbc/PgArray.java b/src/main/java/org/postgresql/jdbc/PgArray.java
index a4dda3b88fc2ab1e953c0e677a679b332b9aceee..6b49ab928f7805701507c198e5577a27bb2d1751 100644
--- a/src/main/java/org/postgresql/jdbc/PgArray.java
+++ b/src/main/java/org/postgresql/jdbc/PgArray.java
@@ -5,28 +5,27 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseConnection;
 import org.postgresql.core.BaseStatement;
-import org.postgresql.core.Encoding;
 import org.postgresql.core.Field;
 import org.postgresql.core.Oid;
 import org.postgresql.core.Tuple;
-import org.postgresql.jdbc2.ArrayAssistant;
+import org.postgresql.jdbc.ArrayDecoding.PgArrayList;
 import org.postgresql.jdbc2.ArrayAssistantRegistry;
 import org.postgresql.util.ByteConverter;
 import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
-import java.io.IOException;
-import java.math.BigDecimal;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.ResultSet;
 import java.sql.SQLException;
-import java.sql.Types;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
-import java.util.logging.Level;
 
 /**
  * <p>Array is used collect one column of query result data.</p>
@@ -47,55 +46,32 @@ public class PgArray implements java.sql.Array {
     ArrayAssistantRegistry.register(Oid.UUID_ARRAY, new UUIDArrayAssistant());
   }
 
-  /**
-   * Array list implementation specific for storing PG array elements.
-   */
-  private static class PgArrayList extends ArrayList<Object> {
-
-    private static final long serialVersionUID = 2052783752654562677L;
-
-    /**
-     * How many dimensions.
-     */
-    int dimensionsCount = 1;
-
-  }
-
   /**
    * A database connection.
    */
-  protected BaseConnection connection = null;
+  protected /* @Nullable */ BaseConnection connection;
 
   /**
    * The OID of this field.
    */
-  private int oid;
+  private final int oid;
 
   /**
    * Field value as String.
    */
-  protected String fieldString = null;
-
-  /**
-   * Whether Object[] should be used instead primitive arrays. Object[] can contain null elements.
-   * It should be set to <Code>true</Code> if
-   * {@link BaseConnection#haveMinimumCompatibleVersion(String)} returns <Code>true</Code> for
-   * argument "8.3".
-   */
-  private final boolean useObjects;
+  protected /* @Nullable */ String fieldString;
 
   /**
    * Value of field as {@link PgArrayList}. Will be initialized only once within
-   * {@link #buildArrayList()}.
+   * {@link #buildArrayList(String)}.
    */
-  protected PgArrayList arrayList;
+  protected ArrayDecoding./* @Nullable */ PgArrayList arrayList;
 
-  protected byte[] fieldBytes;
+  protected byte /* @Nullable */ [] fieldBytes;
 
   private PgArray(BaseConnection connection, int oid) throws SQLException {
     this.connection = connection;
     this.oid = oid;
-    this.useObjects = true;
   }
 
   /**
@@ -106,7 +82,8 @@ public class PgArray implements java.sql.Array {
    * @param fieldString the array data in string form
    * @throws SQLException if something wrong happens
    */
-  public PgArray(BaseConnection connection, int oid, String fieldString) throws SQLException {
+  public PgArray(BaseConnection connection, int oid, /* @Nullable */ String fieldString)
+      throws SQLException {
     this(connection, oid);
     this.fieldString = fieldString;
   }
@@ -119,32 +96,44 @@ public class PgArray implements java.sql.Array {
    * @param fieldBytes the array data in byte form
    * @throws SQLException if something wrong happens
    */
-  public PgArray(BaseConnection connection, int oid, byte[] fieldBytes) throws SQLException {
+  public PgArray(BaseConnection connection, int oid, byte /* @Nullable */ [] fieldBytes)
+      throws SQLException {
     this(connection, oid);
     this.fieldBytes = fieldBytes;
   }
 
+  private BaseConnection getConnection() {
+    return castNonNull(connection);
+  }
+
+  @SuppressWarnings("return.type.incompatible")
   public Object getArray() throws SQLException {
     return getArrayImpl(1, 0, null);
   }
 
+  @SuppressWarnings("return.type.incompatible")
   public Object getArray(long index, int count) throws SQLException {
     return getArrayImpl(index, count, null);
   }
 
+  @SuppressWarnings("return.type.incompatible")
   public Object getArrayImpl(Map<String, Class<?>> map) throws SQLException {
     return getArrayImpl(1, 0, map);
   }
 
+  @SuppressWarnings("return.type.incompatible")
   public Object getArray(Map<String, Class<?>> map) throws SQLException {
     return getArrayImpl(map);
   }
 
-  public Object getArray(long index, int count, Map<String, Class<?>> map) throws SQLException {
+  @SuppressWarnings("return.type.incompatible")
+  public Object getArray(long index, int count, /* @Nullable */ Map<String, Class<?>> map)
+      throws SQLException {
     return getArrayImpl(index, count, map);
   }
 
-  public Object getArrayImpl(long index, int count, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getArrayImpl(long index, int count, /* @Nullable */ Map<String, Class<?>> map)
+      throws SQLException {
 
     // for now maps aren't supported.
     if (map != null && !map.isEmpty()) {
@@ -158,21 +147,21 @@ public class PgArray implements java.sql.Array {
     }
 
     if (fieldBytes != null) {
-      return readBinaryArray((int) index, count);
+      return readBinaryArray(fieldBytes, (int) index, count);
     }
 
     if (fieldString == null) {
       return null;
     }
 
-    buildArrayList();
+    final PgArrayList arrayList = buildArrayList(fieldString);
 
     if (count == 0) {
       count = arrayList.size();
     }
 
     // array index out of range
-    if ((--index) + count > arrayList.size()) {
+    if ((index - 1) + count > arrayList.size()) {
       throw new PSQLException(
           GT.tr("The array index is out of range: {0}, number of elements: {1}.",
               index + count, (long) arrayList.size()),
@@ -182,96 +171,12 @@ public class PgArray implements java.sql.Array {
     return buildArray(arrayList, (int) index, count);
   }
 
-  private Object readBinaryArray(int index, int count) throws SQLException {
-    int dimensions = ByteConverter.int4(fieldBytes, 0);
-    // int flags = ByteConverter.int4(fieldBytes, 4); // bit 0: 0=no-nulls, 1=has-nulls
-    int elementOid = ByteConverter.int4(fieldBytes, 8);
-    int pos = 12;
-    int[] dims = new int[dimensions];
-    for (int d = 0; d < dimensions; ++d) {
-      dims[d] = ByteConverter.int4(fieldBytes, pos);
-      pos += 4;
-      /* int lbound = ByteConverter.int4(fieldBytes, pos); */
-      pos += 4;
-    }
-    if (dimensions == 0) {
-      return java.lang.reflect.Array.newInstance(elementOidToClass(elementOid), 0);
-    }
-    if (count > 0) {
-      dims[0] = Math.min(count, dims[0]);
-    }
-    Object arr = java.lang.reflect.Array.newInstance(elementOidToClass(elementOid), dims);
-    try {
-      storeValues((Object[]) arr, elementOid, dims, pos, 0, index);
-    } catch (IOException ioe) {
-      throw new PSQLException(
-          GT.tr(
-              "Invalid character data was found.  This is most likely caused by stored data containing characters that are invalid for the character set the database was created in.  The most common example of this is storing 8bit data in a SQL_ASCII database."),
-          PSQLState.DATA_ERROR, ioe);
-    }
-    return arr;
-  }
-
-  private int storeValues(final Object[] arr, int elementOid, final int[] dims, int pos,
-      final int thisDimension, int index) throws SQLException, IOException {
-    if (thisDimension == dims.length - 1) {
-      for (int i = 1; i < index; ++i) {
-        int len = ByteConverter.int4(fieldBytes, pos);
-        pos += 4;
-        if (len != -1) {
-          pos += len;
-        }
-      }
-      for (int i = 0; i < dims[thisDimension]; ++i) {
-        int len = ByteConverter.int4(fieldBytes, pos);
-        pos += 4;
-        if (len == -1) {
-          continue;
-        }
-        switch (elementOid) {
-          case Oid.INT2:
-            arr[i] = ByteConverter.int2(fieldBytes, pos);
-            break;
-          case Oid.INT4:
-            arr[i] = ByteConverter.int4(fieldBytes, pos);
-            break;
-          case Oid.INT8:
-            arr[i] = ByteConverter.int8(fieldBytes, pos);
-            break;
-          case Oid.FLOAT4:
-            arr[i] = ByteConverter.float4(fieldBytes, pos);
-            break;
-          case Oid.FLOAT8:
-            arr[i] = ByteConverter.float8(fieldBytes, pos);
-            break;
-          case Oid.NUMERIC:
-            arr[i] = ByteConverter.numeric(fieldBytes, pos, len);
-            break;
-          case Oid.TEXT:
-          case Oid.VARCHAR:
-            Encoding encoding = connection.getEncoding();
-            arr[i] = encoding.decode(fieldBytes, pos, len);
-            break;
-          case Oid.BOOL:
-            arr[i] = ByteConverter.bool(fieldBytes, pos);
-            break;
-          default:
-            ArrayAssistant arrAssistant = ArrayAssistantRegistry.getAssistant(elementOid);
-            if (arrAssistant != null) {
-              arr[i] = arrAssistant.buildElement(fieldBytes, pos, len);
-            }
-        }
-        pos += len;
-      }
-    } else {
-      for (int i = 0; i < dims[thisDimension]; ++i) {
-        pos = storeValues((Object[]) arr[i], elementOid, dims, pos, thisDimension + 1, 0);
-      }
-    }
-    return pos;
+  private Object readBinaryArray(byte[] fieldBytes, int index, int count) throws SQLException {
+    return ArrayDecoding.readBinaryArray(index, count, fieldBytes, getConnection());
   }
 
-  private ResultSet readBinaryResultSet(int index, int count) throws SQLException {
+  private ResultSet readBinaryResultSet(byte[] fieldBytes, int index, int count)
+      throws SQLException {
     int dimensions = ByteConverter.int4(fieldBytes, 0);
     // int flags = ByteConverter.int4(fieldBytes, 4); // bit 0: 0=no-nulls, 1=has-nulls
     int elementOid = ByteConverter.int4(fieldBytes, 8);
@@ -289,14 +194,15 @@ public class PgArray implements java.sql.Array {
     List<Tuple> rows = new ArrayList<Tuple>();
     Field[] fields = new Field[2];
 
-    storeValues(rows, fields, elementOid, dims, pos, 0, index);
+    storeValues(fieldBytes, rows, fields, elementOid, dims, pos, 0, index);
 
-    BaseStatement stat = (BaseStatement) connection
+    BaseStatement stat = (BaseStatement) getConnection()
         .createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
     return stat.createDriverResultSet(fields, rows);
   }
 
-  private int storeValues(List<Tuple> rows, Field[] fields, int elementOid, final int[] dims,
+  private int storeValues(byte[] fieldBytes, List<Tuple> rows, Field[] fields, int elementOid,
+      final int[] dims,
       int pos, final int thisDimension, int index) throws SQLException {
     // handle an empty array
     if (dims.length == 0) {
@@ -345,14 +251,14 @@ public class PgArray implements java.sql.Array {
       int nextDimension = thisDimension + 1;
       int dimensionsLeft = dims.length - nextDimension;
       for (int i = 1; i < index; ++i) {
-        pos = calcRemainingDataLength(dims, pos, elementOid, nextDimension);
+        pos = calcRemainingDataLength(fieldBytes, dims, pos, elementOid, nextDimension);
       }
       for (int i = 0; i < dims[thisDimension]; ++i) {
         byte[][] rowData = new byte[2][];
         rowData[0] = new byte[4];
         ByteConverter.int4(rowData[0], 0, i + index);
         rows.add(new Tuple(rowData));
-        int dataEndPos = calcRemainingDataLength(dims, pos, elementOid, nextDimension);
+        int dataEndPos = calcRemainingDataLength(fieldBytes, dims, pos, elementOid, nextDimension);
         int dataLength = dataEndPos - pos;
         rowData[1] = new byte[12 + 8 * dimensionsLeft + dataLength];
         ByteConverter.int4(rowData[1], 0, dimensionsLeft);
@@ -365,7 +271,8 @@ public class PgArray implements java.sql.Array {
     return pos;
   }
 
-  private int calcRemainingDataLength(int[] dims, int pos, int elementOid, int thisDimension) {
+  private int calcRemainingDataLength(byte[] fieldBytes,
+      int[] dims, int pos, int elementOid, int thisDimension) {
     if (thisDimension == dims.length - 1) {
       for (int i = 0; i < dims[thisDimension]; ++i) {
         int len = ByteConverter.int4(fieldBytes, pos);
@@ -376,162 +283,21 @@ public class PgArray implements java.sql.Array {
         pos += len;
       }
     } else {
-      pos = calcRemainingDataLength(dims, elementOid, pos, thisDimension + 1);
+      pos = calcRemainingDataLength(fieldBytes, dims, elementOid, pos, thisDimension + 1);
     }
     return pos;
   }
 
-  private Class<?> elementOidToClass(int oid) throws SQLException {
-    switch (oid) {
-      case Oid.INT2:
-        return Short.class;
-      case Oid.INT4:
-        return Integer.class;
-      case Oid.INT8:
-        return Long.class;
-      case Oid.FLOAT4:
-        return Float.class;
-      case Oid.FLOAT8:
-        return Double.class;
-      case Oid.NUMERIC:
-        return BigDecimal.class;
-      case Oid.TEXT:
-      case Oid.VARCHAR:
-        return String.class;
-      case Oid.BOOL:
-        return Boolean.class;
-      default:
-        ArrayAssistant arrElemBuilder = ArrayAssistantRegistry.getAssistant(oid);
-        if (arrElemBuilder != null) {
-          return arrElemBuilder.baseType();
-        }
-
-        throw org.postgresql.Driver.notImplemented(this.getClass(), "readBinaryArray(data,oid)");
-    }
-  }
-
   /**
    * Build {@link ArrayList} from field's string input. As a result of this method
    * {@link #arrayList} is build. Method can be called many times in order to make sure that array
    * list is ready to use, however {@link #arrayList} will be set only once during first call.
    */
-  private synchronized void buildArrayList() throws SQLException {
-    if (arrayList != null) {
-      return;
-    }
-
-    arrayList = new PgArrayList();
-
-    char delim = connection.getTypeInfo().getArrayDelimiter(oid);
-
-    if (fieldString != null) {
-
-      char[] chars = fieldString.toCharArray();
-      StringBuilder buffer = null;
-      boolean insideString = false;
-      boolean wasInsideString = false; // needed for checking if NULL
-      // value occurred
-      List<PgArrayList> dims = new ArrayList<PgArrayList>(); // array dimension arrays
-      PgArrayList curArray = arrayList; // currently processed array
-
-      // Starting with 8.0 non-standard (beginning index
-      // isn't 1) bounds the dimensions are returned in the
-      // data formatted like so "[0:3]={0,1,2,3,4}".
-      // Older versions simply do not return the bounds.
-      //
-      // Right now we ignore these bounds, but we could
-      // consider allowing these index values to be used
-      // even though the JDBC spec says 1 is the first
-      // index. I'm not sure what a client would like
-      // to see, so we just retain the old behavior.
-      int startOffset = 0;
-      {
-        if (chars[0] == '[') {
-          while (chars[startOffset] != '=') {
-            startOffset++;
-          }
-          startOffset++; // skip =
-        }
-      }
-
-      for (int i = startOffset; i < chars.length; i++) {
-
-        // escape character that we need to skip
-        if (chars[i] == '\\') {
-          i++;
-        } else if (!insideString && chars[i] == '{') {
-          // subarray start
-          if (dims.isEmpty()) {
-            dims.add(arrayList);
-          } else {
-            PgArrayList a = new PgArrayList();
-            PgArrayList p = dims.get(dims.size() - 1);
-            p.add(a);
-            dims.add(a);
-          }
-          curArray = dims.get(dims.size() - 1);
-
-          // number of dimensions
-          {
-            for (int t = i + 1; t < chars.length; t++) {
-              if (Character.isWhitespace(chars[t])) {
-                continue;
-              } else if (chars[t] == '{') {
-                curArray.dimensionsCount++;
-              } else {
-                break;
-              }
-            }
-          }
-
-          buffer = new StringBuilder();
-          continue;
-        } else if (chars[i] == '"') {
-          // quoted element
-          insideString = !insideString;
-          wasInsideString = true;
-          continue;
-        } else if (!insideString && Character.isWhitespace(chars[i])) {
-          // white space
-          continue;
-        } else if ((!insideString && (chars[i] == delim || chars[i] == '}'))
-            || i == chars.length - 1) {
-          // array end or element end
-          // when character that is a part of array element
-          if (chars[i] != '"' && chars[i] != '}' && chars[i] != delim && buffer != null) {
-            buffer.append(chars[i]);
-          }
-
-          String b = buffer == null ? null : buffer.toString();
-
-          // add element to current array
-          if (b != null && (!b.isEmpty() || wasInsideString)) {
-            curArray.add(!wasInsideString && b.equals("NULL") ? null : b);
-          }
-
-          wasInsideString = false;
-          buffer = new StringBuilder();
-
-          // when end of an array
-          if (chars[i] == '}') {
-            dims.remove(dims.size() - 1);
-
-            // when multi-dimension
-            if (!dims.isEmpty()) {
-              curArray = dims.get(dims.size() - 1);
-            }
-
-            buffer = null;
-          }
-
-          continue;
-        }
-
-        if (buffer != null) {
-          buffer.append(chars[i]);
-        }
-      }
+  private synchronized PgArrayList buildArrayList(String fieldString) throws SQLException {
+    if (arrayList == null) {
+      arrayList = ArrayDecoding.buildArrayList(fieldString, getConnection().getTypeInfo().getArrayDelimiter(oid));
     }
+    return arrayList;
   }
 
   /**
@@ -539,278 +305,18 @@ public class PgArray implements java.sql.Array {
    *
    * @param input list to be converted into array
    */
-  private Object buildArray(PgArrayList input, int index, int count) throws SQLException {
-
-    if (count < 0) {
-      count = input.size();
-    }
-
-    // array to be returned
-    Object ret = null;
-
-    // how many dimensions
-    int dims = input.dimensionsCount;
-
-    // dimensions length array (to be used with java.lang.reflect.Array.newInstance(Class<?>,
-    // int[]))
-    int[] dimsLength = dims > 1 ? new int[dims] : null;
-    if (dims > 1) {
-      for (int i = 0; i < dims; i++) {
-        dimsLength[i] = (i == 0 ? count : 0);
-      }
-    }
-
-    // array elements counter
-    int length = 0;
-
-    // array elements type
-    final int type =
-        connection.getTypeInfo().getSQLType(connection.getTypeInfo().getPGArrayElement(oid));
-
-    if (type == Types.BIT) {
-      boolean[] pa = null; // primitive array
-      Object[] oa = null; // objects array
-
-      if (dims > 1 || useObjects) {
-        ret = oa = (dims > 1
-            ? (Object[]) java.lang.reflect.Array
-                .newInstance(useObjects ? Boolean.class : boolean.class, dimsLength)
-            : new Boolean[count]);
-      } else {
-        ret = pa = new boolean[count];
-      }
-
-      // add elements
-      for (; count > 0; count--) {
-        Object o = input.get(index++);
-
-        if (dims > 1 || useObjects) {
-          oa[length++] = o == null ? null
-            : (dims > 1 ? buildArray((PgArrayList) o, 0, -1) : BooleanTypeUtil.castToBoolean((String) o));
-        } else {
-          pa[length++] = o == null ? false : BooleanTypeUtil.castToBoolean((String) o);
-        }
-      }
-    } else if (type == Types.SMALLINT) {
-      short[] pa = null;
-      Object[] oa = null;
-
-      if (dims > 1 || useObjects) {
-        ret =
-            oa = (dims > 1
-                ? (Object[]) java.lang.reflect.Array
-                    .newInstance(useObjects ? Short.class : short.class, dimsLength)
-                : new Short[count]);
-      } else {
-        ret = pa = new short[count];
-      }
-
-      for (; count > 0; count--) {
-        Object o = input.get(index++);
-
-        if (dims > 1 || useObjects) {
-          oa[length++] = o == null ? null
-              : (dims > 1 ? buildArray((PgArrayList) o, 0, -1) : PgResultSet.toShort((String) o));
-        } else {
-          pa[length++] = o == null ? 0 : PgResultSet.toShort((String) o);
-        }
-      }
-    } else if (type == Types.INTEGER) {
-      int[] pa = null;
-      Object[] oa = null;
-
-      if (dims > 1 || useObjects) {
-        ret =
-            oa = (dims > 1
-                ? (Object[]) java.lang.reflect.Array
-                    .newInstance(useObjects ? Integer.class : int.class, dimsLength)
-                : new Integer[count]);
-      } else {
-        ret = pa = new int[count];
-      }
-
-      for (; count > 0; count--) {
-        Object o = input.get(index++);
-
-        if (dims > 1 || useObjects) {
-          oa[length++] = o == null ? null
-              : (dims > 1 ? buildArray((PgArrayList) o, 0, -1) : PgResultSet.toInt((String) o));
-        } else {
-          pa[length++] = o == null ? 0 : PgResultSet.toInt((String) o);
-        }
-      }
-    } else if (type == Types.BIGINT) {
-      long[] pa = null;
-      Object[] oa = null;
-
-      if (dims > 1 || useObjects) {
-        ret =
-            oa = (dims > 1
-                ? (Object[]) java.lang.reflect.Array
-                    .newInstance(useObjects ? Long.class : long.class, dimsLength)
-                : new Long[count]);
-      } else {
-        ret = pa = new long[count];
-      }
-
-      for (; count > 0; count--) {
-        Object o = input.get(index++);
-
-        if (dims > 1 || useObjects) {
-          oa[length++] = o == null ? null
-              : (dims > 1 ? buildArray((PgArrayList) o, 0, -1) : PgResultSet.toLong((String) o));
-        } else {
-          pa[length++] = o == null ? 0L : PgResultSet.toLong((String) o);
-        }
-      }
-    } else if (type == Types.NUMERIC) {
-      Object[] oa = null;
-      ret = oa =
-          (dims > 1 ? (Object[]) java.lang.reflect.Array.newInstance(BigDecimal.class, dimsLength)
-              : new BigDecimal[count]);
-
-      for (; count > 0; count--) {
-        Object v = input.get(index++);
-        oa[length++] = dims > 1 && v != null ? buildArray((PgArrayList) v, 0, -1)
-            : (v == null ? null : PgResultSet.toBigDecimal((String) v));
-      }
-    } else if (type == Types.REAL) {
-      float[] pa = null;
-      Object[] oa = null;
-
-      if (dims > 1 || useObjects) {
-        ret =
-            oa = (dims > 1
-                ? (Object[]) java.lang.reflect.Array
-                    .newInstance(useObjects ? Float.class : float.class, dimsLength)
-                : new Float[count]);
-      } else {
-        ret = pa = new float[count];
-      }
-
-      for (; count > 0; count--) {
-        Object o = input.get(index++);
-
-        if (dims > 1 || useObjects) {
-          oa[length++] = o == null ? null
-              : (dims > 1 ? buildArray((PgArrayList) o, 0, -1) : PgResultSet.toFloat((String) o));
-        } else {
-          pa[length++] = o == null ? 0f : PgResultSet.toFloat((String) o);
-        }
-      }
-    } else if (type == Types.DOUBLE) {
-      double[] pa = null;
-      Object[] oa = null;
-
-      if (dims > 1 || useObjects) {
-        ret = oa = (dims > 1
-            ? (Object[]) java.lang.reflect.Array
-                .newInstance(useObjects ? Double.class : double.class, dimsLength)
-            : new Double[count]);
-      } else {
-        ret = pa = new double[count];
-      }
-
-      for (; count > 0; count--) {
-        Object o = input.get(index++);
-
-        if (dims > 1 || useObjects) {
-          oa[length++] = o == null ? null
-              : (dims > 1 ? buildArray((PgArrayList) o, 0, -1) : PgResultSet.toDouble((String) o));
-        } else {
-          pa[length++] = o == null ? 0d : PgResultSet.toDouble((String) o);
-        }
-      }
-    } else if (type == Types.CHAR || type == Types.VARCHAR || oid == Oid.JSONB_ARRAY) {
-      Object[] oa = null;
-      ret =
-          oa = (dims > 1 ? (Object[]) java.lang.reflect.Array.newInstance(String.class, dimsLength)
-              : new String[count]);
-
-      for (; count > 0; count--) {
-        Object v = input.get(index++);
-        oa[length++] = dims > 1 && v != null ? buildArray((PgArrayList) v, 0, -1) : v;
-      }
-    } else if (type == Types.DATE) {
-      Object[] oa = null;
-      ret = oa = (dims > 1
-          ? (Object[]) java.lang.reflect.Array.newInstance(java.sql.Date.class, dimsLength)
-          : new java.sql.Date[count]);
-
-      for (; count > 0; count--) {
-        Object v = input.get(index++);
-        oa[length++] = dims > 1 && v != null ? buildArray((PgArrayList) v, 0, -1)
-            : (v == null ? null : connection.getTimestampUtils().toDate(null, (String) v));
-      }
-    } else if (type == Types.TIME) {
-      Object[] oa = null;
-      ret = oa = (dims > 1
-          ? (Object[]) java.lang.reflect.Array.newInstance(java.sql.Time.class, dimsLength)
-          : new java.sql.Time[count]);
-
-      for (; count > 0; count--) {
-        Object v = input.get(index++);
-        oa[length++] = dims > 1 && v != null ? buildArray((PgArrayList) v, 0, -1)
-            : (v == null ? null : connection.getTimestampUtils().toTime(null, (String) v));
-      }
-    } else if (type == Types.TIMESTAMP) {
-      Object[] oa = null;
-      ret = oa = (dims > 1
-          ? (Object[]) java.lang.reflect.Array.newInstance(java.sql.Timestamp.class, dimsLength)
-          : new java.sql.Timestamp[count]);
-
-      for (; count > 0; count--) {
-        Object v = input.get(index++);
-        oa[length++] = dims > 1 && v != null ? buildArray((PgArrayList) v, 0, -1)
-            : (v == null ? null : connection.getTimestampUtils().toTimestamp(null, (String) v));
-      }
-    } else if (ArrayAssistantRegistry.getAssistant(oid) != null) {
-      ArrayAssistant arrAssistant = ArrayAssistantRegistry.getAssistant(oid);
-
-      Object[] oa = null;
-      ret = oa = (dims > 1)
-          ? (Object[]) java.lang.reflect.Array.newInstance(arrAssistant.baseType(), dimsLength)
-          : (Object[]) java.lang.reflect.Array.newInstance(arrAssistant.baseType(), count);
-
-      for (; count > 0; count--) {
-        Object v = input.get(index++);
-        oa[length++] = (dims > 1 && v != null) ? buildArray((PgArrayList) v, 0, -1)
-            : (v == null ? null : arrAssistant.buildElement((String) v));
-      }
-    } else if (dims == 1) {
-      Object[] oa = new Object[count];
-      String typeName = getBaseTypeName();
-      for (; count > 0; count--) {
-        Object v = input.get(index++);
-        if (v instanceof String) {
-          oa[length++] = connection.getObject(typeName, (String) v, null);
-        } else if (v instanceof byte[]) {
-          oa[length++] = connection.getObject(typeName, null, (byte[]) v);
-        } else if (v == null) {
-          oa[length++] = null;
-        } else {
-          throw org.postgresql.Driver.notImplemented(this.getClass(), "getArrayImpl(long,int,Map)");
-        }
-      }
-      ret = oa;
-    } else {
-      // other datatypes not currently supported
-      connection.getLogger().log(Level.FINEST, "getArrayImpl(long,int,Map) with {0}", getBaseTypeName());
-
-      throw org.postgresql.Driver.notImplemented(this.getClass(), "getArrayImpl(long,int,Map)");
-    }
-
-    return ret;
+  private Object buildArray(ArrayDecoding.PgArrayList input, int index, int count) throws SQLException {
+    final BaseConnection connection = getConnection();
+    return ArrayDecoding.readStringArray(index, count, connection.getTypeInfo().getPGArrayElement(oid), input, connection);
   }
 
   public int getBaseType() throws SQLException {
-    return connection.getTypeInfo().getSQLType(getBaseTypeName());
+    return getConnection().getTypeInfo().getSQLType(getBaseTypeName());
   }
 
   public String getBaseTypeName() throws SQLException {
-    buildArrayList();
-    int elementOID = connection.getTypeInfo().getPGArrayElement(oid);
-    return connection.getTypeInfo().getPGType(elementOID);
+    int elementOID = getConnection().getTypeInfo().getPGArrayElement(oid);
+    return castNonNull(getConnection().getTypeInfo().getPGType(elementOID));
   }
 
   public java.sql.ResultSet getResultSet() throws SQLException {
@@ -821,20 +327,20 @@ public class PgArray implements java.sql.Array {
     return getResultSetImpl(index, count, null);
   }
 
-  public ResultSet getResultSet(Map<String, Class<?>> map) throws SQLException {
+  public ResultSet getResultSet(/* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     return getResultSetImpl(map);
   }
 
-  public ResultSet getResultSet(long index, int count, Map<String, Class<?>> map)
+  public ResultSet getResultSet(long index, int count, /* @Nullable */ Map<String, Class<?>> map)
       throws SQLException {
     return getResultSetImpl(index, count, map);
   }
 
-  public ResultSet getResultSetImpl(Map<String, Class<?>> map) throws SQLException {
+  public ResultSet getResultSetImpl(/* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     return getResultSetImpl(1, 0, map);
   }
 
-  public ResultSet getResultSetImpl(long index, int count, Map<String, Class<?>> map)
+  public ResultSet getResultSetImpl(long index, int count, /* @Nullable */ Map<String, Class<?>> map)
       throws SQLException {
 
     // for now maps aren't supported.
@@ -849,10 +355,10 @@ public class PgArray implements java.sql.Array {
     }
 
     if (fieldBytes != null) {
-      return readBinaryResultSet((int) index, count);
+      return readBinaryResultSet(fieldBytes, (int) index, count);
     }
 
-    buildArrayList();
+    final PgArrayList arrayList = buildArrayList(castNonNull(fieldString));
 
     if (count == 0) {
       count = arrayList.size();
@@ -873,16 +379,16 @@ public class PgArray implements java.sql.Array {
     // one dimensional array
     if (arrayList.dimensionsCount <= 1) {
       // array element type
-      final int baseOid = connection.getTypeInfo().getPGArrayElement(oid);
+      final int baseOid = getConnection().getTypeInfo().getPGArrayElement(oid);
       fields[0] = new Field("INDEX", Oid.INT4);
       fields[1] = new Field("VALUE", baseOid);
 
       for (int i = 0; i < count; i++) {
         int offset = (int) index + i;
-        byte[][] t = new byte[2][0];
+        byte[] /* @Nullable */ [] t = new byte[2][0];
         String v = (String) arrayList.get(offset);
-        t[0] = connection.encodeString(Integer.toString(offset + 1));
-        t[1] = v == null ? null : connection.encodeString(v);
+        t[0] = getConnection().encodeString(Integer.toString(offset + 1));
+        t[1] = v == null ? null : getConnection().encodeString(v);
         rows.add(new Tuple(t));
       }
     } else {
@@ -891,33 +397,29 @@ public class PgArray implements java.sql.Array {
       fields[1] = new Field("VALUE", oid);
       for (int i = 0; i < count; i++) {
         int offset = (int) index + i;
-        byte[][] t = new byte[2][0];
+        byte[] /* @Nullable */ [] t = new byte[2][0];
         Object v = arrayList.get(offset);
 
-        t[0] = connection.encodeString(Integer.toString(offset + 1));
-        t[1] = v == null ? null : connection.encodeString(toString((PgArrayList) v));
+        t[0] = getConnection().encodeString(Integer.toString(offset + 1));
+        t[1] = v == null ? null : getConnection().encodeString(toString((ArrayDecoding.PgArrayList) v));
         rows.add(new Tuple(t));
       }
     }
 
-    BaseStatement stat = (BaseStatement) connection
+    BaseStatement stat = (BaseStatement) getConnection()
         .createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
     return stat.createDriverResultSet(fields, rows);
   }
 
-  public String toString() {
+  @SuppressWarnings("nullness")
+  public /* @Nullable */ String toString() {
     if (fieldString == null && fieldBytes != null) {
       try {
-        Object array = readBinaryArray(1, 0);
-
-        final PrimitiveArraySupport arraySupport = PrimitiveArraySupport.getArraySupport(array);
-        if (arraySupport != null) {
-          fieldString =
-            arraySupport.toArrayString(connection.getTypeInfo().getArrayDelimiter(oid), array);
-        } else {
-          java.sql.Array tmpArray = connection.createArrayOf(getBaseTypeName(), (Object[]) array);
-          fieldString = tmpArray.toString();
-        }
+        Object array = readBinaryArray(fieldBytes, 1, 0);
+
+        final ArrayEncoding.ArrayEncoder arraySupport = ArrayEncoding.getArrayEncoder(array);
+        assert arraySupport != null;
+        fieldString = arraySupport.toArrayString(connection.getTypeInfo().getArrayDelimiter(oid), array);
       } catch (SQLException e) {
         fieldString = "NULL"; // punt
       }
@@ -928,14 +430,14 @@ public class PgArray implements java.sql.Array {
   /**
    * Convert array list to PG String representation (e.g. {0,1,2}).
    */
-  private String toString(PgArrayList list) throws SQLException {
+  private String toString(ArrayDecoding.PgArrayList list) throws SQLException {
     if (list == null) {
       return "NULL";
     }
 
     StringBuilder b = new StringBuilder().append('{');
 
-    char delim = connection.getTypeInfo().getArrayDelimiter(oid);
+    char delim = getConnection().getTypeInfo().getArrayDelimiter(oid);
 
     for (int i = 0; i < list.size(); i++) {
       Object v = list.get(i);
@@ -946,8 +448,8 @@ public class PgArray implements java.sql.Array {
 
       if (v == null) {
         b.append("NULL");
-      } else if (v instanceof PgArrayList) {
-        b.append(toString((PgArrayList) v));
+      } else if (v instanceof ArrayDecoding.PgArrayList) {
+        b.append(toString((ArrayDecoding.PgArrayList) v));
       } else {
         escapeArrayElement(b, (String) v);
       }
@@ -975,7 +477,7 @@ public class PgArray implements java.sql.Array {
     return fieldBytes != null;
   }
 
-  public byte[] toBytes() {
+  public byte /* @Nullable */ [] toBytes() {
     return fieldBytes;
   }
 
diff --git a/src/main/java/org/postgresql/jdbc/PgCallableStatement.java b/src/main/java/org/postgresql/jdbc/PgCallableStatement.java
index 19076f4a0a82c5bf8d3be60276c5b98f3de09363..8a06931daf381476670ee1c54c57627e2d64e1f0 100644
--- a/src/main/java/org/postgresql/jdbc/PgCallableStatement.java
+++ b/src/main/java/org/postgresql/jdbc/PgCallableStatement.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.Driver;
 import org.postgresql.core.ParameterList;
 import org.postgresql.core.Query;
@@ -12,6 +14,9 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.index.qual.Positive;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.InputStream;
 import java.io.Reader;
 import java.math.BigDecimal;
@@ -33,15 +38,15 @@ import java.util.Map;
 
 class PgCallableStatement extends PgPreparedStatement implements CallableStatement {
   // Used by the callablestatement style methods
-  private boolean isFunction;
+  private final boolean isFunction;
   // functionReturnType contains the user supplied value to check
   // testReturn contains a modified version to make it easier to
   // check the getXXX methods..
-  private int[] functionReturnType;
-  private int[] testReturn;
+  private int /* @Nullable */ [] functionReturnType;
+  private int /* @Nullable */ [] testReturn;
   // returnTypeSet is true when a proper call to registerOutParameter has been made
   private boolean returnTypeSet;
-  protected Object[] callResult;
+  protected /* @Nullable */ Object /* @Nullable */ [] callResult;
   private int lastIndex = 0;
 
   PgCallableStatement(PgConnection connection, String sql, int rsType, int rsConcurrency,
@@ -64,18 +69,20 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
     return super.executeUpdate();
   }
 
-  public Object getObject(int i, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getObject(/* @Positive */ int i, /* @Nullable */ Map<String, Class<?>> map)
+      throws SQLException {
     return getObjectImpl(i, map);
   }
 
-  public Object getObject(String s, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getObject(String s, /* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     return getObjectImpl(s, map);
   }
 
   @Override
   public boolean executeWithFlags(int flags) throws SQLException {
     boolean hasResultSet = super.executeWithFlags(flags);
-    if (!isFunction || !returnTypeSet) {
+    int[] functionReturnType = this.functionReturnType;
+    if (!isFunction || !returnTypeSet || functionReturnType == null) {
       return hasResultSet;
     }
 
@@ -86,11 +93,7 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
           PSQLState.NO_DATA);
     }
 
-    ResultSet rs;
-    synchronized (this) {
-      checkClosed();
-      rs = result.getResultSet();
-    }
+    ResultSet rs = castNonNull(getResultSet());
     if (!rs.next()) {
       throw new PSQLException(GT.tr("A CallableStatement was executed with nothing returned."),
           PSQLState.NO_DATA);
@@ -111,7 +114,8 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
     lastIndex = 0;
 
     // allocate enough space for all possible parameters without regard to in/out
-    callResult = new Object[preparedParameters.getParameterCount() + 1];
+    /* @Nullable */ Object[] callResult = new Object[preparedParameters.getParameterCount() + 1];
+    this.callResult = callResult;
 
     // move them into the result set
     for (int i = 0, j = 0; i < cols; i++, j++) {
@@ -130,8 +134,9 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
         // this is here for the sole purpose of passing the cts
         if (columnType == Types.DOUBLE && functionReturnType[j] == Types.REAL) {
           // return it as a float
-          if (callResult[j] != null) {
-            callResult[j] = ((Double) callResult[j]).floatValue();
+          Object result = callResult[j];
+          if (result != null) {
+            callResult[j] = ((Double) result).floatValue();
           }
           //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
         } else if (columnType == Types.REF_CURSOR && functionReturnType[j] == Types.OTHER) {
@@ -172,7 +177,7 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
    * @throws SQLException if a database-access error occurs.
    */
   @Override
-  public void registerOutParameter(int parameterIndex, int sqlType)
+  public void registerOutParameter(/* @Positive */ int parameterIndex, int sqlType)
       throws SQLException {
     checkClosed();
     switch (sqlType) {
@@ -200,13 +205,14 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
       default:
         break;
     }
-    if (!isFunction) {
+    int[] functionReturnType = this.functionReturnType;
+    int[] testReturn = this.testReturn;
+    if (!isFunction || functionReturnType == null || testReturn == null) {
       throw new PSQLException(
           GT.tr(
               "This statement does not declare an OUT parameter.  Use '{' ?= call ... '}' to declare one."),
           PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL);
     }
-    checkIndex(parameterIndex, false);
 
     preparedParameters.registerOutParameter(parameterIndex, sqlType);
     // functionReturnType contains the user supplied value to check
@@ -225,127 +231,112 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
   }
 
   public boolean wasNull() throws SQLException {
-    if (lastIndex == 0) {
+    if (lastIndex == 0 || callResult == null) {
       throw new PSQLException(GT.tr("wasNull cannot be call before fetching a result."),
           PSQLState.OBJECT_NOT_IN_STATE);
     }
 
     // check to see if the last access threw an exception
-    return (callResult[lastIndex - 1] == null);
+    return callResult[lastIndex - 1] == null;
   }
 
-  public String getString(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.VARCHAR, "String");
-    return (String) callResult[parameterIndex - 1];
+  public /* @Nullable */ String getString(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.VARCHAR, "String");
+    return (String) result;
   }
 
-  public boolean getBoolean(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.BIT, "Boolean");
-    if (callResult[parameterIndex - 1] == null) {
+  public boolean getBoolean(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.BIT, "Boolean");
+    if (result == null) {
       return false;
     }
 
-    return (Boolean) callResult[parameterIndex - 1];
+    return (Boolean) result;
   }
 
-  public byte getByte(int parameterIndex) throws SQLException {
-    checkClosed();
+  public byte getByte(/* @Positive */ int parameterIndex) throws SQLException {
     // fake tiny int with smallint
-    checkIndex(parameterIndex, Types.SMALLINT, "Byte");
+    Object result = checkIndex(parameterIndex, Types.SMALLINT, "Byte");
 
-    if (callResult[parameterIndex - 1] == null) {
+    if (result == null) {
       return 0;
     }
 
-    return ((Integer) callResult[parameterIndex - 1]).byteValue();
+    return ((Integer) result).byteValue();
 
   }
 
-  public short getShort(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.SMALLINT, "Short");
-    if (callResult[parameterIndex - 1] == null) {
+  public short getShort(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.SMALLINT, "Short");
+    if (result == null) {
       return 0;
     }
-    return ((Integer) callResult[parameterIndex - 1]).shortValue();
+    return ((Integer) result).shortValue();
   }
 
-  public int getInt(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.INTEGER, "Int");
-    if (callResult[parameterIndex - 1] == null) {
+  public int getInt(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.INTEGER, "Int");
+    if (result == null) {
       return 0;
     }
 
-    return (Integer) callResult[parameterIndex - 1];
+    return (Integer) result;
   }
 
-  public long getLong(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.BIGINT, "Long");
-    if (callResult[parameterIndex - 1] == null) {
+  public long getLong(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.BIGINT, "Long");
+    if (result == null) {
       return 0;
     }
 
-    return (Long) callResult[parameterIndex - 1];
+    return (Long) result;
   }
 
-  public float getFloat(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.REAL, "Float");
-    if (callResult[parameterIndex - 1] == null) {
+  public float getFloat(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.REAL, "Float");
+    if (result == null) {
       return 0;
     }
 
-    return (Float) callResult[parameterIndex - 1];
+    return (Float) result;
   }
 
-  public double getDouble(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.DOUBLE, "Double");
-    if (callResult[parameterIndex - 1] == null) {
+  public double getDouble(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.DOUBLE, "Double");
+    if (result == null) {
       return 0;
     }
 
-    return (Double) callResult[parameterIndex - 1];
+    return (Double) result;
   }
 
-  public BigDecimal getBigDecimal(int parameterIndex, int scale) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.NUMERIC, "BigDecimal");
-    return ((BigDecimal) callResult[parameterIndex - 1]);
+  public /* @Nullable */ BigDecimal getBigDecimal(/* @Positive */ int parameterIndex, int scale) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.NUMERIC, "BigDecimal");
+    return (/* @Nullable */ BigDecimal) result;
   }
 
-  public byte[] getBytes(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.VARBINARY, Types.BINARY, "Bytes");
-    return ((byte[]) callResult[parameterIndex - 1]);
+  public byte /* @Nullable */ [] getBytes(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.VARBINARY, Types.BINARY, "Bytes");
+    return ((byte /* @Nullable */ []) result);
   }
 
-  public java.sql.Date getDate(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.DATE, "Date");
-    return (java.sql.Date) callResult[parameterIndex - 1];
+  public java.sql./* @Nullable */ Date getDate(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.DATE, "Date");
+    return (java.sql./* @Nullable */ Date) result;
   }
 
-  public java.sql.Time getTime(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.TIME, "Time");
-    return (java.sql.Time) callResult[parameterIndex - 1];
+  public java.sql./* @Nullable */ Time getTime(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.TIME, "Time");
+    return (java.sql./* @Nullable */ Time) result;
   }
 
-  public java.sql.Timestamp getTimestamp(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.TIMESTAMP, "Timestamp");
-    return (java.sql.Timestamp) callResult[parameterIndex - 1];
+  public java.sql./* @Nullable */ Timestamp getTimestamp(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.TIMESTAMP, "Timestamp");
+    return (java.sql./* @Nullable */ Timestamp) result;
   }
 
-  public Object getObject(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex);
-    return callResult[parameterIndex - 1];
+  public /* @Nullable */ Object getObject(/* @Positive */ int parameterIndex) throws SQLException {
+    return getCallResult(parameterIndex);
   }
 
   /**
@@ -358,17 +349,18 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
    * @param getName getter name
    * @throws SQLException if something goes wrong
    */
-  protected void checkIndex(int parameterIndex, int type1, int type2, String getName)
+  protected /* @Nullable */ Object checkIndex(/* @Positive */ int parameterIndex, int type1, int type2, String getName)
       throws SQLException {
-    checkIndex(parameterIndex);
-    if (type1 != this.testReturn[parameterIndex - 1]
-        && type2 != this.testReturn[parameterIndex - 1]) {
+    Object result = getCallResult(parameterIndex);
+    int testReturn = this.testReturn != null ? this.testReturn[parameterIndex - 1] : -1;
+    if (type1 != testReturn && type2 != testReturn) {
       throw new PSQLException(
           GT.tr("Parameter of type {0} was registered, but call to get{1} (sqltype={2}) was made.",
-                  "java.sql.Types=" + testReturn[parameterIndex - 1], getName,
+                  "java.sql.Types=" + testReturn, getName,
                   "java.sql.Types=" + type1),
           PSQLState.MOST_SPECIFIC_TYPE_DOES_NOT_MATCH);
     }
+    return result;
   }
 
   /**
@@ -379,28 +371,23 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
    * @param getName getter name
    * @throws SQLException if given index is not valid
    */
-  protected void checkIndex(int parameterIndex, int type, String getName) throws SQLException {
-    checkIndex(parameterIndex);
-    if (type != this.testReturn[parameterIndex - 1]) {
+  protected /* @Nullable */ Object checkIndex(/* @Positive */ int parameterIndex,
+      int type, String getName) throws SQLException {
+    Object result = getCallResult(parameterIndex);
+    int testReturn = this.testReturn != null ? this.testReturn[parameterIndex - 1] : -1;
+    if (type != testReturn) {
       throw new PSQLException(
           GT.tr("Parameter of type {0} was registered, but call to get{1} (sqltype={2}) was made.",
-              "java.sql.Types=" + testReturn[parameterIndex - 1], getName,
+              "java.sql.Types=" + testReturn, getName,
                   "java.sql.Types=" + type),
           PSQLState.MOST_SPECIFIC_TYPE_DOES_NOT_MATCH);
     }
+    return result;
   }
 
-  private void checkIndex(int parameterIndex) throws SQLException {
-    checkIndex(parameterIndex, true);
-  }
+  private /* @Nullable */ Object getCallResult(/* @Positive */ int parameterIndex) throws SQLException {
+    checkClosed();
 
-  /**
-   * Helper function for the getXXX calls to check isFunction and index == 1.
-   *
-   * @param parameterIndex index of getXXX (index) check to make sure is a function and index == 1
-   * @param fetchingData fetching data
-   */
-  private void checkIndex(int parameterIndex, boolean fetchingData) throws SQLException {
     if (!isFunction) {
       throw new PSQLException(
           GT.tr(
@@ -408,122 +395,117 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
           PSQLState.STATEMENT_NOT_ALLOWED_IN_FUNCTION_CALL);
     }
 
-    if (fetchingData) {
-      if (!returnTypeSet) {
-        throw new PSQLException(GT.tr("No function outputs were registered."),
-            PSQLState.OBJECT_NOT_IN_STATE);
-      }
-
-      if (callResult == null) {
-        throw new PSQLException(
-            GT.tr("Results cannot be retrieved from a CallableStatement before it is executed."),
-            PSQLState.NO_DATA);
-      }
+    if (!returnTypeSet) {
+      throw new PSQLException(GT.tr("No function outputs were registered."),
+          PSQLState.OBJECT_NOT_IN_STATE);
+    }
 
-      lastIndex = parameterIndex;
+    /* @Nullable */ Object /* @Nullable */ [] callResult = this.callResult;
+    if (callResult == null) {
+      throw new PSQLException(
+          GT.tr("Results cannot be retrieved from a CallableStatement before it is executed."),
+          PSQLState.NO_DATA);
     }
+
+    lastIndex = parameterIndex;
+    return callResult[parameterIndex - 1];
   }
 
   @Override
   protected BatchResultHandler createBatchHandler(Query[] queries,
-      ParameterList[] parameterLists) {
+      /* @Nullable */ ParameterList[] parameterLists) {
     return new CallableBatchResultHandler(this, queries, parameterLists);
   }
 
-  public java.sql.Array getArray(int i) throws SQLException {
-    checkClosed();
-    checkIndex(i, Types.ARRAY, "Array");
-    return (Array) callResult[i - 1];
+  public java.sql./* @Nullable */ Array getArray(int i) throws SQLException {
+    Object result = checkIndex(i, Types.ARRAY, "Array");
+    return (Array) result;
   }
 
-  public java.math.BigDecimal getBigDecimal(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.NUMERIC, "BigDecimal");
-    return ((BigDecimal) callResult[parameterIndex - 1]);
+  public java.math./* @Nullable */ BigDecimal getBigDecimal(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.NUMERIC, "BigDecimal");
+    return ((BigDecimal) result);
   }
 
-  public Blob getBlob(int i) throws SQLException {
+  public /* @Nullable */ Blob getBlob(int i) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getBlob(int)");
   }
 
-  public Clob getClob(int i) throws SQLException {
+  public /* @Nullable */ Clob getClob(int i) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getClob(int)");
   }
 
-  public Object getObjectImpl(int i, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getObjectImpl(int i, /* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     if (map == null || map.isEmpty()) {
       return getObject(i);
     }
     throw Driver.notImplemented(this.getClass(), "getObjectImpl(int,Map)");
   }
 
-  public Ref getRef(int i) throws SQLException {
+  public /* @Nullable */ Ref getRef(int i) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getRef(int)");
   }
 
-  public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException {
-    checkClosed();
-    checkIndex(i, Types.DATE, "Date");
+  public java.sql./* @Nullable */ Date getDate(int i, java.util./* @Nullable */ Calendar cal) throws SQLException {
+    Object result = checkIndex(i, Types.DATE, "Date");
 
-    if (callResult[i - 1] == null) {
+    if (result == null) {
       return null;
     }
 
-    String value = callResult[i - 1].toString();
+    String value = result.toString();
     return connection.getTimestampUtils().toDate(cal, value);
   }
 
-  public Time getTime(int i, java.util.Calendar cal) throws SQLException {
-    checkClosed();
-    checkIndex(i, Types.TIME, "Time");
+  public /* @Nullable */ Time getTime(int i, java.util./* @Nullable */ Calendar cal) throws SQLException {
+    Object result = checkIndex(i, Types.TIME, "Time");
 
-    if (callResult[i - 1] == null) {
+    if (result == null) {
       return null;
     }
 
-    String value = callResult[i - 1].toString();
+    String value = result.toString();
     return connection.getTimestampUtils().toTime(cal, value);
   }
 
-  public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException {
-    checkClosed();
-    checkIndex(i, Types.TIMESTAMP, "Timestamp");
+  public /* @Nullable */ Timestamp getTimestamp(int i, java.util./* @Nullable */ Calendar cal) throws SQLException {
+    Object result = checkIndex(i, Types.TIMESTAMP, "Timestamp");
 
-    if (callResult[i - 1] == null) {
+    if (result == null) {
       return null;
     }
 
-    String value = callResult[i - 1].toString();
+    String value = result.toString();
     return connection.getTimestampUtils().toTimestamp(cal, value);
   }
 
-  public void registerOutParameter(int parameterIndex, int sqlType, String typeName)
+  public void registerOutParameter(/* @Positive */ int parameterIndex, int sqlType, String typeName)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "registerOutParameter(int,int,String)");
   }
 
   //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
-  public void setObject(String parameterName, Object x, java.sql.SQLType targetSqlType,
+  public void setObject(String parameterName, /* @Nullable */ Object x, java.sql.SQLType targetSqlType,
       int scaleOrLength) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setObject");
   }
 
-  public void setObject(String parameterName, Object x, java.sql.SQLType targetSqlType)
+  public void setObject(String parameterName, /* @Nullable */ Object x, java.sql.SQLType targetSqlType)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setObject");
   }
 
-  public void registerOutParameter(int parameterIndex, java.sql.SQLType sqlType)
+  public void registerOutParameter(/* @Positive */ int parameterIndex, java.sql.SQLType sqlType)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "registerOutParameter");
   }
 
-  public void registerOutParameter(int parameterIndex, java.sql.SQLType sqlType, int scale)
+  public void registerOutParameter(/* @Positive */ int parameterIndex, java.sql.SQLType sqlType, int scale)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "registerOutParameter");
   }
 
-  public void registerOutParameter(int parameterIndex, java.sql.SQLType sqlType, String typeName)
+  public void registerOutParameter(/* @Positive */ int parameterIndex, java.sql.SQLType sqlType, String typeName)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "registerOutParameter");
   }
@@ -544,142 +526,142 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
   }
   //#endif
 
-  public RowId getRowId(int parameterIndex) throws SQLException {
+  public /* @Nullable */ RowId getRowId(/* @Positive */ int parameterIndex) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getRowId(int)");
   }
 
-  public RowId getRowId(String parameterName) throws SQLException {
+  public /* @Nullable */ RowId getRowId(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getRowId(String)");
   }
 
-  public void setRowId(String parameterName, RowId x) throws SQLException {
+  public void setRowId(String parameterName, /* @Nullable */ RowId x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setRowId(String, RowId)");
   }
 
-  public void setNString(String parameterName, String value) throws SQLException {
+  public void setNString(String parameterName, /* @Nullable */ String value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNString(String, String)");
   }
 
-  public void setNCharacterStream(String parameterName, Reader value, long length)
+  public void setNCharacterStream(String parameterName, /* @Nullable */ Reader value, long length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNCharacterStream(String, Reader, long)");
   }
 
-  public void setNCharacterStream(String parameterName, Reader value) throws SQLException {
+  public void setNCharacterStream(String parameterName, /* @Nullable */ Reader value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNCharacterStream(String, Reader)");
   }
 
-  public void setCharacterStream(String parameterName, Reader value, long length)
+  public void setCharacterStream(String parameterName, /* @Nullable */ Reader value, long length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setCharacterStream(String, Reader, long)");
   }
 
-  public void setCharacterStream(String parameterName, Reader value) throws SQLException {
+  public void setCharacterStream(String parameterName, /* @Nullable */ Reader value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setCharacterStream(String, Reader)");
   }
 
-  public void setBinaryStream(String parameterName, InputStream value, long length)
+  public void setBinaryStream(String parameterName, /* @Nullable */ InputStream value, long length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setBinaryStream(String, InputStream, long)");
   }
 
-  public void setBinaryStream(String parameterName, InputStream value) throws SQLException {
+  public void setBinaryStream(String parameterName, /* @Nullable */ InputStream value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setBinaryStream(String, InputStream)");
   }
 
-  public void setAsciiStream(String parameterName, InputStream value, long length)
+  public void setAsciiStream(String parameterName, /* @Nullable */ InputStream value, long length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setAsciiStream(String, InputStream, long)");
   }
 
-  public void setAsciiStream(String parameterName, InputStream value) throws SQLException {
+  public void setAsciiStream(String parameterName, /* @Nullable */ InputStream value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setAsciiStream(String, InputStream)");
   }
 
-  public void setNClob(String parameterName, NClob value) throws SQLException {
+  public void setNClob(String parameterName, /* @Nullable */ NClob value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNClob(String, NClob)");
   }
 
-  public void setClob(String parameterName, Reader reader, long length) throws SQLException {
+  public void setClob(String parameterName, /* @Nullable */ Reader reader, long length) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setClob(String, Reader, long)");
   }
 
-  public void setClob(String parameterName, Reader reader) throws SQLException {
+  public void setClob(String parameterName, /* @Nullable */ Reader reader) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setClob(String, Reader)");
   }
 
-  public void setBlob(String parameterName, InputStream inputStream, long length)
+  public void setBlob(String parameterName, /* @Nullable */ InputStream inputStream, long length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setBlob(String, InputStream, long)");
   }
 
-  public void setBlob(String parameterName, InputStream inputStream) throws SQLException {
+  public void setBlob(String parameterName, /* @Nullable */ InputStream inputStream) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setBlob(String, InputStream)");
   }
 
-  public void setBlob(String parameterName, Blob x) throws SQLException {
+  public void setBlob(String parameterName, /* @Nullable */ Blob x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setBlob(String, Blob)");
   }
 
-  public void setClob(String parameterName, Clob x) throws SQLException {
+  public void setClob(String parameterName, /* @Nullable */ Clob x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setClob(String, Clob)");
   }
 
-  public void setNClob(String parameterName, Reader reader, long length) throws SQLException {
+  public void setNClob(String parameterName, /* @Nullable */ Reader reader, long length) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNClob(String, Reader, long)");
   }
 
-  public void setNClob(String parameterName, Reader reader) throws SQLException {
+  public void setNClob(String parameterName, /* @Nullable */ Reader reader) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNClob(String, Reader)");
   }
 
-  public NClob getNClob(int parameterIndex) throws SQLException {
+  public /* @Nullable */ NClob getNClob(/* @Positive */ int parameterIndex) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getNClob(int)");
   }
 
-  public NClob getNClob(String parameterName) throws SQLException {
+  public /* @Nullable */ NClob getNClob(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getNClob(String)");
   }
 
-  public void setSQLXML(String parameterName, SQLXML xmlObject) throws SQLException {
+  public void setSQLXML(String parameterName, /* @Nullable */ SQLXML xmlObject) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setSQLXML(String, SQLXML)");
   }
 
-  public SQLXML getSQLXML(int parameterIndex) throws SQLException {
-    checkClosed();
-    checkIndex(parameterIndex, Types.SQLXML, "SQLXML");
-    return (SQLXML) callResult[parameterIndex - 1];
+  public /* @Nullable */ SQLXML getSQLXML(/* @Positive */ int parameterIndex) throws SQLException {
+    Object result = checkIndex(parameterIndex, Types.SQLXML, "SQLXML");
+    return (SQLXML) result;
   }
 
-  public SQLXML getSQLXML(String parameterIndex) throws SQLException {
+  public /* @Nullable */ SQLXML getSQLXML(String parameterIndex) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getSQLXML(String)");
   }
 
-  public String getNString(int parameterIndex) throws SQLException {
+  public String getNString(/* @Positive */ int parameterIndex) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getNString(int)");
   }
 
-  public String getNString(String parameterName) throws SQLException {
+  public /* @Nullable */ String getNString(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getNString(String)");
   }
 
-  public Reader getNCharacterStream(int parameterIndex) throws SQLException {
+  public /* @Nullable */ Reader getNCharacterStream(/* @Positive */ int parameterIndex) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getNCharacterStream(int)");
   }
 
-  public Reader getNCharacterStream(String parameterName) throws SQLException {
+  public /* @Nullable */ Reader getNCharacterStream(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getNCharacterStream(String)");
   }
 
-  public Reader getCharacterStream(int parameterIndex) throws SQLException {
+  public /* @Nullable */ Reader getCharacterStream(/* @Positive */ int parameterIndex) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getCharacterStream(int)");
   }
 
-  public Reader getCharacterStream(String parameterName) throws SQLException {
+  public /* @Nullable */ Reader getCharacterStream(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getCharacterStream(String)");
   }
 
-  public <T> T getObject(int parameterIndex, Class<T> type) throws SQLException {
+  public <T> /* @Nullable */ T getObject(/* @Positive */ int parameterIndex, Class<T> type)
+      throws SQLException {
     if (type == ResultSet.class) {
       return type.cast(getObject(parameterIndex));
     }
@@ -687,7 +669,7 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
             PSQLState.INVALID_PARAMETER_VALUE);
   }
 
-  public <T> T getObject(String parameterName, Class<T> type) throws SQLException {
+  public <T> /* @Nullable */ T getObject(String parameterName, Class<T> type) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getObject(String, Class<T>)");
   }
 
@@ -705,11 +687,11 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
     throw Driver.notImplemented(this.getClass(), "registerOutParameter(String,int,String)");
   }
 
-  public java.net.URL getURL(int parameterIndex) throws SQLException {
+  public java.net./* @Nullable */ URL getURL(/* @Positive */ int parameterIndex) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getURL(String)");
   }
 
-  public void setURL(String parameterName, java.net.URL val) throws SQLException {
+  public void setURL(String parameterName, java.net./* @Nullable */ URL val) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setURL(String,URL)");
   }
 
@@ -745,65 +727,65 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
     throw Driver.notImplemented(this.getClass(), "setDouble(String,double)");
   }
 
-  public void setBigDecimal(String parameterName, BigDecimal x) throws SQLException {
+  public void setBigDecimal(String parameterName, /* @Nullable */ BigDecimal x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setBigDecimal(String,BigDecimal)");
   }
 
-  public void setString(String parameterName, String x) throws SQLException {
+  public void setString(String parameterName, /* @Nullable */ String x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setString(String,String)");
   }
 
-  public void setBytes(String parameterName, byte[] x) throws SQLException {
+  public void setBytes(String parameterName, byte /* @Nullable */ [] x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setBytes(String,byte)");
   }
 
-  public void setDate(String parameterName, java.sql.Date x) throws SQLException {
+  public void setDate(String parameterName, java.sql./* @Nullable */ Date x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setDate(String,Date)");
   }
 
-  public void setTime(String parameterName, Time x) throws SQLException {
+  public void setTime(String parameterName, /* @Nullable */ Time x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setTime(String,Time)");
   }
 
-  public void setTimestamp(String parameterName, Timestamp x) throws SQLException {
+  public void setTimestamp(String parameterName, /* @Nullable */ Timestamp x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setTimestamp(String,Timestamp)");
   }
 
-  public void setAsciiStream(String parameterName, InputStream x, int length) throws SQLException {
+  public void setAsciiStream(String parameterName, /* @Nullable */ InputStream x, int length) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setAsciiStream(String,InputStream,int)");
   }
 
-  public void setBinaryStream(String parameterName, InputStream x, int length) throws SQLException {
+  public void setBinaryStream(String parameterName, /* @Nullable */ InputStream x, int length) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setBinaryStream(String,InputStream,int)");
   }
 
-  public void setObject(String parameterName, Object x, int targetSqlType, int scale)
+  public void setObject(String parameterName, /* @Nullable */ Object x, int targetSqlType, int scale)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setObject(String,Object,int,int)");
   }
 
-  public void setObject(String parameterName, Object x, int targetSqlType) throws SQLException {
+  public void setObject(String parameterName, /* @Nullable */ Object x, int targetSqlType) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setObject(String,Object,int)");
   }
 
-  public void setObject(String parameterName, Object x) throws SQLException {
+  public void setObject(String parameterName, /* @Nullable */ Object x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setObject(String,Object)");
   }
 
-  public void setCharacterStream(String parameterName, Reader reader, int length)
+  public void setCharacterStream(String parameterName, /* @Nullable */ Reader reader, int length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setCharacterStream(String,Reader,int)");
   }
 
-  public void setDate(String parameterName, java.sql.Date x, Calendar cal) throws SQLException {
+  public void setDate(String parameterName, java.sql./* @Nullable */ Date x, /* @Nullable */ Calendar cal) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setDate(String,Date,Calendar)");
   }
 
-  public void setTime(String parameterName, Time x, Calendar cal) throws SQLException {
+  public void setTime(String parameterName, /* @Nullable */ Time x, /* @Nullable */ Calendar cal) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setTime(String,Time,Calendar)");
   }
 
-  public void setTimestamp(String parameterName, Timestamp x, Calendar cal) throws SQLException {
+  public void setTimestamp(String parameterName, /* @Nullable */ Timestamp x, /* @Nullable */ Calendar cal) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setTimestamp(String,Timestamp,Calendar)");
   }
 
@@ -811,7 +793,7 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
     throw Driver.notImplemented(this.getClass(), "setNull(String,int,String)");
   }
 
-  public String getString(String parameterName) throws SQLException {
+  public /* @Nullable */ String getString(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getString(String)");
   }
 
@@ -843,11 +825,11 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
     throw Driver.notImplemented(this.getClass(), "getDouble(String)");
   }
 
-  public byte[] getBytes(String parameterName) throws SQLException {
+  public byte /* @Nullable */ [] getBytes(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getBytes(String)");
   }
 
-  public java.sql.Date getDate(String parameterName) throws SQLException {
+  public java.sql./* @Nullable */ Date getDate(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getDate(String)");
   }
 
@@ -855,55 +837,55 @@ class PgCallableStatement extends PgPreparedStatement implements CallableStateme
     throw Driver.notImplemented(this.getClass(), "getTime(String)");
   }
 
-  public Timestamp getTimestamp(String parameterName) throws SQLException {
+  public /* @Nullable */ Timestamp getTimestamp(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getTimestamp(String)");
   }
 
-  public Object getObject(String parameterName) throws SQLException {
+  public /* @Nullable */ Object getObject(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getObject(String)");
   }
 
-  public BigDecimal getBigDecimal(String parameterName) throws SQLException {
+  public /* @Nullable */ BigDecimal getBigDecimal(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getBigDecimal(String)");
   }
 
-  public Object getObjectImpl(String parameterName, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getObjectImpl(String parameterName, /* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getObject(String,Map)");
   }
 
-  public Ref getRef(String parameterName) throws SQLException {
+  public /* @Nullable */ Ref getRef(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getRef(String)");
   }
 
-  public Blob getBlob(String parameterName) throws SQLException {
+  public /* @Nullable */ Blob getBlob(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getBlob(String)");
   }
 
-  public Clob getClob(String parameterName) throws SQLException {
+  public /* @Nullable */ Clob getClob(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getClob(String)");
   }
 
-  public Array getArray(String parameterName) throws SQLException {
+  public /* @Nullable */ Array getArray(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getArray(String)");
   }
 
-  public java.sql.Date getDate(String parameterName, Calendar cal) throws SQLException {
+  public java.sql./* @Nullable */ Date getDate(String parameterName, /* @Nullable */ Calendar cal) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getDate(String,Calendar)");
   }
 
-  public Time getTime(String parameterName, Calendar cal) throws SQLException {
+  public /* @Nullable */ Time getTime(String parameterName, /* @Nullable */ Calendar cal) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getTime(String,Calendar)");
   }
 
-  public Timestamp getTimestamp(String parameterName, Calendar cal) throws SQLException {
+  public /* @Nullable */ Timestamp getTimestamp(String parameterName, /* @Nullable */ Calendar cal) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getTimestamp(String,Calendar)");
   }
 
-  public java.net.URL getURL(String parameterName) throws SQLException {
+  public java.net./* @Nullable */ URL getURL(String parameterName) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "getURL(String)");
   }
 
-  public void registerOutParameter(int parameterIndex, int sqlType, int scale) throws SQLException {
+  public void registerOutParameter(/* @Positive */ int parameterIndex, int sqlType, int scale) throws SQLException {
     // ignore scale for now
     registerOutParameter(parameterIndex, sqlType);
   }
diff --git a/src/main/java/org/postgresql/jdbc/PgClob.java b/src/main/java/org/postgresql/jdbc/PgClob.java
index 5d9fb5352731d00f58900c8b13e26dab25a6e649..b48ead45e45e1d29f5ff78fdd727587ae1309ab1 100644
--- a/src/main/java/org/postgresql/jdbc/PgClob.java
+++ b/src/main/java/org/postgresql/jdbc/PgClob.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import org.postgresql.largeobject.LargeObject;
+
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -54,8 +56,9 @@ public class PgClob extends AbstractBlobClob implements java.sql.Clob {
 
   public synchronized String getSubString(long i, int j) throws SQLException {
     assertPosition(i, j);
-    getLo(false).seek((int) i - 1);
-    return new String(getLo(false).read(j));
+    LargeObject lo = getLo(false);
+    lo.seek((int) i - 1);
+    return new String(lo.read(j));
   }
 
   /**
diff --git a/src/main/java/org/postgresql/jdbc/PgConnection.java b/src/main/java/org/postgresql/jdbc/PgConnection.java
index e7afc93180e57f646041bd3158b282e4d55ad221..ff4a0a2d6250d36cb7d6eaf5ffd2416b7b5ebc48 100644
--- a/src/main/java/org/postgresql/jdbc/PgConnection.java
+++ b/src/main/java/org/postgresql/jdbc/PgConnection.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.Driver;
 import org.postgresql.PGNotification;
 import org.postgresql.PGProperty;
@@ -41,6 +43,10 @@ import org.postgresql.xml.DefaultPGXmlFactoryFactory;
 import org.postgresql.xml.LegacyInsecurePGXmlFactoryFactory;
 import org.postgresql.xml.PGXmlFactoryFactory;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.checker.nullness.qual.PolyNull;
+// import org.checkerframework.dataflow.qual.Pure;
+
 import java.io.IOException;
 import java.sql.Array;
 import java.sql.Blob;
@@ -101,7 +107,7 @@ public class PgConnection implements BaseConnection {
 
   private final ReadOnlyBehavior readOnlyBehavior;
 
-  private Throwable openStackTrace;
+  private /* @Nullable */ Throwable openStackTrace;
 
   /* Actual network handler */
   private final QueryExecutor queryExecutor;
@@ -144,13 +150,13 @@ public class PgConnection implements BaseConnection {
   private final boolean bindStringAsVarchar;
 
   // Current warnings; there might be more on queryExecutor too.
-  private SQLWarning firstWarning = null;
+  private /* @Nullable */ SQLWarning firstWarning;
 
   // Timer for scheduling TimerTasks for this connection.
   // Only instantiated if a task is actually scheduled.
-  private volatile Timer cancelTimer = null;
+  private volatile /* @Nullable */ Timer cancelTimer;
 
-  private PreparedStatement checkConnectionQuery;
+  private /* @Nullable */ PreparedStatement checkConnectionQuery;
   /**
    * Replication protocol in current version postgresql(10devel) supports a limited number of
    * commands.
@@ -159,8 +165,8 @@ public class PgConnection implements BaseConnection {
 
   private final LruCache<FieldMetadata.Key, FieldMetadata> fieldMetadataCache;
 
-  private final String xmlFactoryFactoryClass;
-  private PGXmlFactoryFactory xmlFactoryFactory;
+  private final /* @Nullable */ String xmlFactoryFactoryClass;
+  private /* @Nullable */ PGXmlFactoryFactory xmlFactoryFactory;
 
   final CachedQuery borrowQuery(String sql) throws SQLException {
     return queryExecutor.borrowQuery(sql);
@@ -170,7 +176,8 @@ public class PgConnection implements BaseConnection {
     return queryExecutor.borrowCallableQuery(sql);
   }
 
-  private CachedQuery borrowReturningQuery(String sql, String[] columnNames) throws SQLException {
+  private CachedQuery borrowReturningQuery(String sql, String /* @Nullable */ [] columnNames)
+      throws SQLException {
     return queryExecutor.borrowReturningQuery(sql, columnNames);
   }
 
@@ -194,6 +201,7 @@ public class PgConnection implements BaseConnection {
   //
   // Ctor.
   //
+  @SuppressWarnings({"method.invocation.invalid", "argument.type.incompatible"})
   public PgConnection(HostSpec[] hostSpecs,
                       String user,
                       String database,
@@ -273,12 +281,13 @@ public class PgConnection implements BaseConnection {
     }
 
     // Initialize timestamp stuff
-    timestampUtils = new TimestampUtils(!queryExecutor.getIntegerDateTimes(), new Provider<TimeZone>() {
-      @Override
-      public TimeZone get() {
-        return queryExecutor.getTimeZone();
-      }
-    });
+    timestampUtils = new TimestampUtils(!queryExecutor.getIntegerDateTimes(),
+        new Provider</* @Nullable */ TimeZone>() {
+          @Override
+          public /* @Nullable */ TimeZone get() {
+            return queryExecutor.getTimeZone();
+          }
+        });
 
     // Initialize common queries.
     // isParameterized==true so full parse is performed and the engine knows the query
@@ -346,9 +355,11 @@ public class PgConnection implements BaseConnection {
         Oid.TIMETZ,
         Oid.TIMESTAMP,
         Oid.TIMESTAMPTZ,
+        Oid.BYTEA_ARRAY,
         Oid.INT2_ARRAY,
         Oid.INT4_ARRAY,
         Oid.INT8_ARRAY,
+        Oid.OID_ARRAY,
         Oid.FLOAT4_ARRAY,
         Oid.FLOAT8_ARRAY,
         Oid.VARCHAR_ARRAY,
@@ -366,9 +377,16 @@ public class PgConnection implements BaseConnection {
       binaryOids.addAll(SUPPORTED_BINARY_OIDS);
     }
 
-    binaryOids.addAll(getOidSet(PGProperty.BINARY_TRANSFER_ENABLE.get(info)));
-    binaryOids.removeAll(getOidSet(PGProperty.BINARY_TRANSFER_DISABLE.get(info)));
+    String oids = PGProperty.BINARY_TRANSFER_ENABLE.get(info);
+    if (oids != null) {
+      binaryOids.addAll(getOidSet(oids));
+    }
+    oids = PGProperty.BINARY_TRANSFER_DISABLE.get(info);
+    if (oids != null) {
+      binaryOids.removeAll(getOidSet(oids));
+    }
     binaryOids.retainAll(SUPPORTED_BINARY_OIDS);
+
     return binaryOids;
   }
 
@@ -478,7 +496,7 @@ public class PgConnection implements BaseConnection {
       addWarning(warnings);
     }
 
-    return stat.getResultSet();
+    return castNonNull(stat.getResultSet(), "hasResultSet==true, yet getResultSet()==null");
   }
 
   @Override
@@ -539,7 +557,7 @@ public class PgConnection implements BaseConnection {
    * @return the current cursor name
    * @throws SQLException if a database access error occurs
    */
-  public String getCursorName() throws SQLException {
+  public /* @Nullable */ String getCursorName() throws SQLException {
     checkClosed();
     return null;
   }
@@ -576,7 +594,7 @@ public class PgConnection implements BaseConnection {
   }
 
   // This holds a reference to the Fastpath API if already open
-  private Fastpath fastpath = null;
+  private /* @Nullable */ Fastpath fastpath;
 
   public LargeObjectManager getLargeObjectAPI() throws SQLException {
     checkClosed();
@@ -587,7 +605,7 @@ public class PgConnection implements BaseConnection {
   }
 
   // This holds a reference to the LargeObject API if already open
-  private LargeObjectManager largeobject = null;
+  private /* @Nullable */ LargeObjectManager largeobject;
 
   /*
    * This method is used internally to return an object based around org.postgresql's more unique
@@ -604,7 +622,8 @@ public class PgConnection implements BaseConnection {
    * @exception SQLException if value is not correct for this type
    */
   @Override
-  public Object getObject(String type, String value, byte[] byteValue) throws SQLException {
+  public Object getObject(String type, /* @Nullable */ String value, byte /* @Nullable */ [] byteValue)
+      throws SQLException {
     if (typemap != null) {
       Class<?> c = typemap.get(type);
       if (c != null) {
@@ -636,14 +655,14 @@ public class PgConnection implements BaseConnection {
           PGBinaryObject binObj = (PGBinaryObject) obj;
           binObj.setByteValue(byteValue, 0);
         } else {
-          obj.setValue(value);
+          obj.setValue(castNonNull(value));
         }
       } else {
         // If className is null, then the type is unknown.
         // so return a PGobject with the type set, and the value set
         obj = new PGobject();
         obj.setType(type);
-        obj.setValue(value);
+        obj.setValue(castNonNull(value));
       }
 
       return obj;
@@ -696,9 +715,9 @@ public class PgConnection implements BaseConnection {
     Enumeration<?> e = info.propertyNames();
     while (e.hasMoreElements()) {
       String propertyName = (String) e.nextElement();
-      if (propertyName.startsWith("datatype.")) {
+      if (propertyName != null && propertyName.startsWith("datatype.")) {
         String typeName = propertyName.substring(9);
-        String className = info.getProperty(propertyName);
+        String className = castNonNull(info.getProperty(propertyName));
         Class<?> klass;
 
         try {
@@ -742,12 +761,12 @@ public class PgConnection implements BaseConnection {
   }
 
   @Override
-  public synchronized SQLWarning getWarnings() throws SQLException {
+  public synchronized /* @Nullable */ SQLWarning getWarnings() throws SQLException {
     checkClosed();
     SQLWarning newWarnings = queryExecutor.getWarnings(); // NB: also clears them.
     if (firstWarning == null) {
       firstWarning = newWarnings;
-    } else {
+    } else if (newWarnings != null) {
       firstWarning.setNextWarning(newWarnings); // Chain them on.
     }
 
@@ -757,6 +776,7 @@ public class PgConnection implements BaseConnection {
   @Override
   public synchronized void clearWarnings() throws SQLException {
     checkClosed();
+    //noinspection ThrowableNotThrown
     queryExecutor.getWarnings(); // Clear and discard.
     firstWarning = null;
   }
@@ -941,7 +961,7 @@ public class PgConnection implements BaseConnection {
     LOGGER.log(Level.FINE, "  setTransactionIsolation = {0}", isolationLevelName);
   }
 
-  protected String getIsolationLevelName(int level) {
+  protected /* @Nullable */ String getIsolationLevelName(int level) {
     switch (level) {
       case Connection.TRANSACTION_READ_COMMITTED:
         return "READ COMMITTED";
@@ -1037,13 +1057,14 @@ public class PgConnection implements BaseConnection {
     return haveMinimumServerVersion(ver.getVersionNum());
   }
 
+  /* @Pure */
   @Override
   public Encoding getEncoding() {
     return queryExecutor.getEncoding();
   }
 
   @Override
-  public byte[] encodeString(String str) throws SQLException {
+  public byte /* @PolyNull */ [] encodeString(/* @PolyNull */ String str) throws SQLException {
     try {
       return getEncoding().encode(str);
     } catch (IOException ioe) {
@@ -1064,7 +1085,7 @@ public class PgConnection implements BaseConnection {
   }
 
   // This is a cache of the DatabaseMetaData instance for this connection
-  protected java.sql.DatabaseMetaData metadata;
+  protected java.sql./* @Nullable */ DatabaseMetaData metadata;
 
   @Override
   public boolean isClosed() throws SQLException {
@@ -1088,7 +1109,7 @@ public class PgConnection implements BaseConnection {
     getQueryExecutor().processNotifies(timeoutMillis);
     // Backwards-compatibility hand-holding.
     PGNotification[] notifications = queryExecutor.getNotifications();
-    return (notifications.length == 0 ? null : notifications);
+    return notifications;
   }
 
   /**
@@ -1152,7 +1173,7 @@ public class PgConnection implements BaseConnection {
     return bindStringAsVarchar;
   }
 
-  private CopyManager copyManager = null;
+  private /* @Nullable */ CopyManager copyManager;
 
   public CopyManager getCopyAPI() throws SQLException {
     checkClosed();
@@ -1248,33 +1269,6 @@ public class PgConnection implements BaseConnection {
     return new PGReplicationConnectionImpl(this);
   }
 
-  private static void appendArray(StringBuilder sb, Object elements, char delim) {
-    sb.append('{');
-
-    int nElements = java.lang.reflect.Array.getLength(elements);
-    for (int i = 0; i < nElements; i++) {
-      if (i > 0) {
-        sb.append(delim);
-      }
-
-      Object o = java.lang.reflect.Array.get(elements, i);
-      if (o == null) {
-        sb.append("NULL");
-      } else if (o.getClass().isArray()) {
-        final PrimitiveArraySupport arraySupport = PrimitiveArraySupport.getArraySupport(o);
-        if (arraySupport != null) {
-          arraySupport.appendArray(sb, delim, o);
-        } else {
-          appendArray(sb, o, delim);
-        }
-      } else {
-        String s = o.toString();
-        PgArray.escapeArrayElement(sb, s);
-      }
-    }
-    sb.append('}');
-  }
-
   // Parse a "dirty" integer surrounded by non-numeric characters
   private static int integerPart(String dirtyString) {
     int start = 0;
@@ -1333,7 +1327,7 @@ public class PgConnection implements BaseConnection {
     LOGGER.log(Level.FINE, "  setTypeMap = {0}", map);
   }
 
-  protected Array makeArray(int oid, String fieldString) throws SQLException {
+  protected Array makeArray(int oid, /* @Nullable */ String fieldString) throws SQLException {
     return new PgArray(this, oid, fieldString);
   }
 
@@ -1379,8 +1373,9 @@ public class PgConnection implements BaseConnection {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "createStruct(String, Object[])");
   }
 
+  @SuppressWarnings({ "rawtypes", "unchecked" })
   @Override
-  public Array createArrayOf(String typeName, Object elements) throws SQLException {
+  public Array createArrayOf(String typeName, /* @Nullable */ Object elements) throws SQLException {
     checkClosed();
 
     final TypeInfo typeInfo = getTypeInfo();
@@ -1397,52 +1392,19 @@ public class PgConnection implements BaseConnection {
       return makeArray(oid, null);
     }
 
-    final String arrayString;
-
-    final PrimitiveArraySupport arraySupport = PrimitiveArraySupport.getArraySupport(elements);
-
-    if (arraySupport != null) {
-      // if the oid for the given type matches the default type, we might be
-      // able to go straight to binary representation
-      if (oid == arraySupport.getDefaultArrayTypeOid(typeInfo) && arraySupport.supportBinaryRepresentation()
-          && getPreferQueryMode() != PreferQueryMode.SIMPLE) {
-        return new PgArray(this, oid, arraySupport.toBinaryRepresentation(this, elements));
-      }
-      arrayString = arraySupport.toArrayString(delim, elements);
-    } else {
-      final Class<?> clazz = elements.getClass();
-      if (!clazz.isArray()) {
-        throw new PSQLException(GT.tr("Invalid elements {0}", elements), PSQLState.INVALID_PARAMETER_TYPE);
-      }
-      StringBuilder sb = new StringBuilder();
-      appendArray(sb, elements, delim);
-      arrayString = sb.toString();
+    final ArrayEncoding.ArrayEncoder arraySupport = ArrayEncoding.getArrayEncoder(elements);
+    if (arraySupport.supportBinaryRepresentation(oid) && getPreferQueryMode() != PreferQueryMode.SIMPLE) {
+      return new PgArray(this, oid, arraySupport.toBinaryRepresentation(this, elements, oid));
     }
 
+    final String arrayString = arraySupport.toArrayString(delim, elements);
     return makeArray(oid, arrayString);
   }
 
   @Override
-  public Array createArrayOf(String typeName, Object[] elements) throws SQLException {
-    checkClosed();
-
-    int oid = getTypeInfo().getPGArrayType(typeName);
-
-    if (oid == Oid.UNSPECIFIED) {
-      throw new PSQLException(
-          GT.tr("Unable to find server array type for provided name {0}.", typeName),
-          PSQLState.INVALID_NAME);
-    }
-
-    if (elements == null) {
-      return makeArray(oid, null);
-    }
-
-    char delim = getTypeInfo().getArrayDelimiter(oid);
-    StringBuilder sb = new StringBuilder();
-    appendArray(sb, elements, delim);
-
-    return makeArray(oid, sb.toString());
+  public Array createArrayOf(String typeName, /* @Nullable */ Object /* @Nullable */ [] elements)
+      throws SQLException {
+    return createArrayOf(typeName, (Object) elements);
   }
 
   @Override
@@ -1484,7 +1446,7 @@ public class PgConnection implements BaseConnection {
   }
 
   @Override
-  public void setClientInfo(String name, String value) throws SQLClientInfoException {
+  public void setClientInfo(String name, /* @Nullable */ String value) throws SQLClientInfoException {
     try {
       checkClosed();
     } catch (final SQLException cause) {
@@ -1553,7 +1515,7 @@ public class PgConnection implements BaseConnection {
   }
 
   @Override
-  public String getClientInfo(String name) throws SQLException {
+  public /* @Nullable */ String getClientInfo(String name) throws SQLException {
     checkClosed();
     clientInfo.put("ApplicationName", queryExecutor.getApplicationName());
     return clientInfo.getProperty(name);
@@ -1586,7 +1548,7 @@ public class PgConnection implements BaseConnection {
     throw new SQLException("Cannot unwrap to " + iface.getName());
   }
 
-  public String getSchema() throws SQLException {
+  public /* @Nullable */ String getSchema() throws SQLException {
     checkClosed();
     Statement stmt = createStatement();
     try {
@@ -1604,7 +1566,7 @@ public class PgConnection implements BaseConnection {
     }
   }
 
-  public void setSchema(String schema) throws SQLException {
+  public void setSchema(/* @Nullable */ String schema) throws SQLException {
     checkClosed();
     Statement stmt = createStatement();
     try {
@@ -1643,7 +1605,8 @@ public class PgConnection implements BaseConnection {
     executor.execute(command);
   }
 
-  public void setNetworkTimeout(Executor executor /*not used*/, int milliseconds) throws SQLException {
+  public void setNetworkTimeout(/* @Nullable */ Executor executor /*not used*/, int milliseconds)
+      throws SQLException {
     checkClosed();
 
     if (milliseconds < 0) {
@@ -1789,7 +1752,7 @@ public class PgConnection implements BaseConnection {
   }
 
   @Override
-  public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
+  public PreparedStatement prepareStatement(String sql, int /* @Nullable */ [] columnIndexes) throws SQLException {
     if (columnIndexes != null && columnIndexes.length == 0) {
       return prepareStatement(sql);
     }
@@ -1800,7 +1763,7 @@ public class PgConnection implements BaseConnection {
   }
 
   @Override
-  public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
+  public PreparedStatement prepareStatement(String sql, String /* @Nullable */[] columnNames) throws SQLException {
     if (columnNames != null && columnNames.length == 0) {
       return prepareStatement(sql);
     }
@@ -1827,40 +1790,43 @@ public class PgConnection implements BaseConnection {
   }
 
   @Override
-  public final String getParameterStatus(String parameterName) {
+  public final /* @Nullable */ String getParameterStatus(String parameterName) {
     return queryExecutor.getParameterStatus(parameterName);
   }
 
   @Override
   public PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException {
-    if (xmlFactoryFactory == null) {
-      if (xmlFactoryFactoryClass == null || xmlFactoryFactoryClass.equals("")) {
-        xmlFactoryFactory = DefaultPGXmlFactoryFactory.INSTANCE;
-      } else if (xmlFactoryFactoryClass.equals("LEGACY_INSECURE")) {
-        xmlFactoryFactory = LegacyInsecurePGXmlFactoryFactory.INSTANCE;
-      } else {
-        Class<?> clazz;
-        try {
-          clazz = Class.forName(xmlFactoryFactoryClass);
-        } catch (ClassNotFoundException ex) {
-          throw new PSQLException(
-              GT.tr("Could not instantiate xmlFactoryFactory: {0}", xmlFactoryFactoryClass),
-              PSQLState.INVALID_PARAMETER_VALUE, ex);
-        }
-        if (!clazz.isAssignableFrom(PGXmlFactoryFactory.class)) {
-          throw new PSQLException(
-              GT.tr("Connection property xmlFactoryFactory must implement PGXmlFactoryFactory: {0}", xmlFactoryFactoryClass),
-              PSQLState.INVALID_PARAMETER_VALUE);
-        }
-        try {
-          xmlFactoryFactory = (PGXmlFactoryFactory) clazz.newInstance();
-        } catch (Exception ex) {
-          throw new PSQLException(
-              GT.tr("Could not instantiate xmlFactoryFactory: {0}", xmlFactoryFactoryClass),
-              PSQLState.INVALID_PARAMETER_VALUE, ex);
-        }
+    PGXmlFactoryFactory xmlFactoryFactory = this.xmlFactoryFactory;
+    if (xmlFactoryFactory != null) {
+      return xmlFactoryFactory;
+    }
+    if (xmlFactoryFactoryClass == null || xmlFactoryFactoryClass.equals("")) {
+      xmlFactoryFactory = DefaultPGXmlFactoryFactory.INSTANCE;
+    } else if (xmlFactoryFactoryClass.equals("LEGACY_INSECURE")) {
+      xmlFactoryFactory = LegacyInsecurePGXmlFactoryFactory.INSTANCE;
+    } else {
+      Class<?> clazz;
+      try {
+        clazz = Class.forName(xmlFactoryFactoryClass);
+      } catch (ClassNotFoundException ex) {
+        throw new PSQLException(
+            GT.tr("Could not instantiate xmlFactoryFactory: {0}", xmlFactoryFactoryClass),
+            PSQLState.INVALID_PARAMETER_VALUE, ex);
+      }
+      if (!clazz.isAssignableFrom(PGXmlFactoryFactory.class)) {
+        throw new PSQLException(
+            GT.tr("Connection property xmlFactoryFactory must implement PGXmlFactoryFactory: {0}", xmlFactoryFactoryClass),
+            PSQLState.INVALID_PARAMETER_VALUE);
+      }
+      try {
+        xmlFactoryFactory = (PGXmlFactoryFactory) clazz.newInstance();
+      } catch (Exception ex) {
+        throw new PSQLException(
+            GT.tr("Could not instantiate xmlFactoryFactory: {0}", xmlFactoryFactoryClass),
+            PSQLState.INVALID_PARAMETER_VALUE, ex);
       }
     }
+    this.xmlFactoryFactory = xmlFactoryFactory;
     return xmlFactoryFactory;
   }
 }
diff --git a/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java b/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
index f1b9c685e61af798ff41364eda5ee9d0b6205186..09217c33750ef91e9441dbb42007af1e48a5414d 100644
--- a/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
+++ b/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseStatement;
 import org.postgresql.core.Field;
 import org.postgresql.core.Oid;
@@ -17,6 +19,9 @@ import org.postgresql.util.JdbcBlackHole;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.KeyFor;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.math.BigInteger;
 import java.sql.Array;
 import java.sql.Connection;
@@ -42,7 +47,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     this.connection = conn;
   }
 
-  private String keywords;
+  private /* @Nullable */ String keywords;
 
   protected final PgConnection connection; // The connection association
 
@@ -257,6 +262,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
   @Override
   public String getSQLKeywords() throws SQLException {
     connection.checkClosed();
+    String keywords = this.keywords;
     if (keywords == null) {
       if (connection.haveMinimumServerVersion(ServerVersion.v9_0)) {
         // Exclude SQL:2003 keywords (https://github.com/ronsavage/SQL/blob/master/sql-2003-2.bnf)
@@ -344,6 +350,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
             + "statistics,stdin,stdout,storage,strict,sysid,tablespace,temp,template,truncate,trusted,"
             + "unencrypted,unlisten,until,vacuum,valid,validator,verbose,volatile";
       }
+      this.keywords = castNonNull(keywords);
     }
     return keywords;
   }
@@ -1025,7 +1032,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return sb.toString();
   }
 
-  public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern)
+  public ResultSet getProcedures(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String procedureNamePattern)
       throws SQLException {
     String sql;
     sql = "SELECT NULL AS PROCEDURE_CAT, n.nspname AS PROCEDURE_SCHEM, p.proname AS PROCEDURE_NAME, "
@@ -1058,8 +1066,9 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return createMetaDataStatement().executeQuery(sql);
   }
 
-  public ResultSet getProcedureColumns(String catalog, String schemaPattern,
-      String procedureNamePattern, String columnNamePattern) throws SQLException {
+  public ResultSet getProcedureColumns(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String procedureNamePattern, /* @Nullable */ String columnNamePattern)
+      throws SQLException {
     int columns = 20;
 
     Field[] f = new Field[columns];
@@ -1112,7 +1121,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       String returnTypeType = rs.getString("typtype");
       int returnTypeRelid = (int) rs.getLong("typrelid");
 
-      String strArgTypes = rs.getString("proargtypes");
+      String strArgTypes = castNonNull(rs.getString("proargtypes"));
       StringTokenizer st = new StringTokenizer(strArgTypes);
       List<Long> argTypes = new ArrayList<Long>();
       while (st.hasMoreTokens()) {
@@ -1141,9 +1150,9 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       }
 
       // decide if we are returning a single column result.
-      if (returnTypeType.equals("b") || returnTypeType.equals("d") || returnTypeType.equals("e")
-          || (returnTypeType.equals("p") && argModesArray == null)) {
-        byte[][] tuple = new byte[columns][];
+      if ("b".equals(returnTypeType) || "d".equals(returnTypeType) || "e".equals(returnTypeType)
+          || ("p".equals(returnTypeType) && argModesArray == null)) {
+        byte[] /* @Nullable */ [] tuple = new byte[columns][];
         tuple[0] = null;
         tuple[1] = schema;
         tuple[2] = procedureName;
@@ -1169,7 +1178,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
 
       // Add a row for each argument.
       for (int i = 0; i < numArgs; i++) {
-        byte[][] tuple = new byte[columns][];
+        byte[] /* @Nullable */ [] tuple = new byte[columns][];
         tuple[0] = null;
         tuple[1] = schema;
         tuple[2] = procedureName;
@@ -1199,7 +1208,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
         }
 
         tuple[5] =
-            connection.encodeString(Integer.toString(connection.getTypeInfo().getSQLType(argOid)));
+            connection.encodeString(Integer.toString(
+                castNonNull(connection.getTypeInfo().getSQLType(argOid))));
         tuple[6] = connection.encodeString(connection.getTypeInfo().getPGType(argOid));
         tuple[7] = null;
         tuple[8] = null;
@@ -1216,7 +1226,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       }
 
       // if we are returning a multi-column result.
-      if (returnTypeType.equals("c") || (returnTypeType.equals("p") && argModesArray != null)) {
+      if ("c".equals(returnTypeType) || ("p".equals(returnTypeType) && argModesArray != null)) {
         String columnsql = "SELECT a.attname,a.atttypid FROM pg_catalog.pg_attribute a "
                            + " WHERE a.attrelid = " + returnTypeRelid
                            + " AND NOT a.attisdropped AND a.attnum > 0 ORDER BY a.attnum ";
@@ -1224,7 +1234,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
         ResultSet columnrs = columnstmt.executeQuery(columnsql);
         while (columnrs.next()) {
           int columnTypeOid = (int) columnrs.getLong("atttypid");
-          byte[][] tuple = new byte[columns][];
+          byte[] /* @Nullable */ [] tuple = new byte[columns][];
           tuple[0] = null;
           tuple[1] = schema;
           tuple[2] = procedureName;
@@ -1258,8 +1268,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
   }
 
   @Override
-  public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern,
-                             String[] types) throws SQLException {
+  public ResultSet getTables(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String tableNamePattern, String /* @Nullable */ [] types) throws SQLException {
     String select;
     String orderby;
     String useSchemas = "SCHEMAS";
@@ -1425,7 +1435,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
   }
 
   @Override
-  public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException {
+  public ResultSet getSchemas(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern)
+      throws SQLException {
     String sql;
     sql = "SELECT nspname AS TABLE_SCHEM, NULL AS TABLE_CATALOG FROM pg_catalog.pg_namespace "
           + " WHERE nspname <> 'pg_toast' AND (nspname !~ '^pg_temp_' "
@@ -1451,7 +1462,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     Field[] f = new Field[1];
     List<Tuple> v = new ArrayList<Tuple>();
     f[0] = new Field("TABLE_CAT", Oid.VARCHAR);
-    byte[][] tuple = new byte[1][];
+    byte[] /* @Nullable */ [] tuple = new byte[1][];
     tuple[0] = connection.encodeString(connection.getCatalog());
     v.add(new Tuple(tuple));
 
@@ -1467,7 +1478,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     List<Tuple> v = new ArrayList<Tuple>();
     f[0] = new Field("TABLE_TYPE", Oid.VARCHAR);
     for (String type : types) {
-      byte[][] tuple = new byte[1][];
+      byte[] /* @Nullable */ [] tuple = new byte[1][];
       tuple[0] = connection.encodeString(type);
       v.add(new Tuple(tuple));
     }
@@ -1475,8 +1486,9 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return ((BaseStatement) createMetaDataStatement()).createDriverResultSet(f, v);
   }
 
-  public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern,
-                              String columnNamePattern) throws SQLException {
+  public ResultSet getColumns(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String tableNamePattern,
+      /* @Nullable */ String columnNamePattern) throws SQLException {
 
     int numberOfFields = 24; // JDBC4
     List<Tuple> v = new ArrayList<Tuple>(); // The new ResultSet tuple stuff
@@ -1564,7 +1576,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     Statement stmt = connection.createStatement();
     ResultSet rs = stmt.executeQuery(sql);
     while (rs.next()) {
-      byte[][] tuple = new byte[numberOfFields][];
+      byte[] /* @Nullable */ [] tuple = new byte[numberOfFields][];
       int typeOid = (int) rs.getLong("atttypid");
       int typeMod = rs.getInt("atttypmod");
 
@@ -1593,11 +1605,11 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       String defval = rs.getString("adsrc");
 
       if (defval != null) {
-        if (pgType.equals("int4")) {
+        if ("int4".equals(pgType)) {
           if (defval.contains("nextval(")) {
             tuple[5] = connection.encodeString("serial"); // Type name == serial
           }
-        } else if (pgType.equals("int8")) {
+        } else if ("int8".equals(pgType)) {
           if (defval.contains("nextval(")) {
             tuple[5] = connection.encodeString("bigserial"); // Type name == bigserial
           }
@@ -1650,7 +1662,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       // Everything is base 10 unless we override later.
       tuple[9] = connection.encodeString("10");
 
-      if (pgType.equals("bit") || pgType.equals("varbit")) {
+      if ("bit".equals(pgType) || "varbit".equals(pgType)) {
         tuple[9] = connection.encodeString("2");
       }
 
@@ -1689,8 +1701,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
   }
 
   @Override
-  public ResultSet getColumnPrivileges(String catalog, String schema, String table,
-      String columnNamePattern) throws SQLException {
+  public ResultSet getColumnPrivileges(/* @Nullable */ String catalog, /* @Nullable */ String schema,
+      String table, /* @Nullable */ String columnNamePattern) throws SQLException {
     Field[] f = new Field[8];
     List<Tuple> v = new ArrayList<Tuple>();
 
@@ -1732,28 +1744,28 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       byte[] schemaName = rs.getBytes("nspname");
       byte[] tableName = rs.getBytes("relname");
       byte[] column = rs.getBytes("attname");
-      String owner = rs.getString("rolname");
+      String owner = castNonNull(rs.getString("rolname"));
       String relAcl = rs.getString("relacl");
 
       // For instance: SELECT -> user1 -> list of [grantor, grantable]
-      Map<String, Map<String, List<String[]>>> permissions = parseACL(relAcl, owner);
+      Map<String, Map<String, List</* @Nullable */ String[]>>> permissions = parseACL(relAcl, owner);
 
       if (connection.haveMinimumServerVersion(ServerVersion.v8_4)) {
         String acl = rs.getString("attacl");
-        Map<String, Map<String, List<String[]>>> relPermissions = parseACL(acl, owner);
+        Map<String, Map<String, List</* @Nullable */ String[]>>> relPermissions = parseACL(acl, owner);
         permissions.putAll(relPermissions);
       }
-      String[] permNames = permissions.keySet().toArray(new String[0]);
+      /* @KeyFor("permissions") */ String[] permNames = permissions.keySet().toArray(new String[0]);
       Arrays.sort(permNames);
       for (String permName : permNames) {
         byte[] privilege = connection.encodeString(permName);
-        Map<String, List<String[]>> grantees = permissions.get(permName);
-        for (Map.Entry<String, List<String[]>> userToGrantable : grantees.entrySet()) {
-          List<String[]> grantor = userToGrantable.getValue();
+        Map<String, List</* @Nullable */ String[]>> grantees = permissions.get(permName);
+        for (Map.Entry<String, List</* @Nullable */ String[]>> userToGrantable : grantees.entrySet()) {
+          List</* @Nullable */ String[]> grantor = userToGrantable.getValue();
           String grantee = userToGrantable.getKey();
-          for (String[] grants : grantor) {
+          for (/* @Nullable */ String[] grants : grantor) {
             String grantable = owner.equals(grantee) ? "YES" : grants[1];
-            byte[][] tuple = new byte[8][];
+            byte[] /* @Nullable */ [] tuple = new byte[8][];
             tuple[0] = null;
             tuple[1] = schemaName;
             tuple[2] = tableName;
@@ -1774,8 +1786,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
   }
 
   @Override
-  public ResultSet getTablePrivileges(String catalog, String schemaPattern,
-      String tableNamePattern) throws SQLException {
+  public ResultSet getTablePrivileges(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String tableNamePattern) throws SQLException {
     Field[] f = new Field[7];
     List<Tuple> v = new ArrayList<Tuple>();
 
@@ -1808,23 +1820,23 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     while (rs.next()) {
       byte[] schema = rs.getBytes("nspname");
       byte[] table = rs.getBytes("relname");
-      String owner = rs.getString("rolname");
+      String owner = castNonNull(rs.getString("rolname"));
       String acl = rs.getString("relacl");
-      Map<String, Map<String, List<String[]>>> permissions = parseACL(acl, owner);
-      String[] permNames = permissions.keySet().toArray(new String[0]);
+      Map<String, Map<String, List</* @Nullable */ String[]>>> permissions = parseACL(acl, owner);
+      /* @KeyFor("permissions") */ String[] permNames = permissions.keySet().toArray(new String[0]);
       Arrays.sort(permNames);
       for (String permName : permNames) {
         byte[] privilege = connection.encodeString(permName);
-        Map<String, List<String[]>> grantees = permissions.get(permName);
-        for (Map.Entry<String, List<String[]>> userToGrantable : grantees.entrySet()) {
-          List<String[]> grants = userToGrantable.getValue();
+        Map<String, List</* @Nullable */ String[]>> grantees = permissions.get(permName);
+        for (Map.Entry<String, List</* @Nullable */ String[]>> userToGrantable : grantees.entrySet()) {
+          List</* @Nullable */ String[]> grants = userToGrantable.getValue();
           String granteeUser = userToGrantable.getKey();
-          for (String[] grantTuple : grants) {
+          for (/* @Nullable */ String[] grantTuple : grants) {
             // report the owner as grantor if it's missing
             String grantor = grantTuple[0] == null ? owner : grantTuple[0];
             // owner always has grant privileges
             String grantable = owner.equals(granteeUser) ? "YES" : grantTuple[1];
-            byte[][] tuple = new byte[7][];
+            byte[] /* @Nullable */ [] tuple = new byte[7][];
             tuple[0] = null;
             tuple[1] = schema;
             tuple[2] = table;
@@ -1884,7 +1896,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
    * Add the user described by the given acl to the Lists of users with the privileges described by
    * the acl.
    */
-  private static void addACLPrivileges(String acl, Map<String, Map<String, List<String[]>>> privileges) {
+  private static void addACLPrivileges(String acl,
+      Map<String, Map<String, List</* @Nullable */ String[]>>> privileges) {
     int equalIndex = acl.lastIndexOf("=");
     int slashIndex = acl.lastIndexOf("/");
     if (equalIndex == -1) {
@@ -1958,25 +1971,22 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
             sqlpriv = "UNKNOWN";
         }
 
-        Map<String, List<String[]>> usersWithPermission = privileges.get(sqlpriv);
-        String[] grant = {grantor, grantable};
-
+        Map<String, List</* @Nullable */ String[]>> usersWithPermission = privileges.get(sqlpriv);
+        //noinspection Java8MapApi
         if (usersWithPermission == null) {
-          usersWithPermission = new HashMap<String, List<String[]>>();
-          List<String[]> permissionByGrantor = new ArrayList<String[]>();
-          permissionByGrantor.add(grant);
-          usersWithPermission.put(user, permissionByGrantor);
+          usersWithPermission = new HashMap<String, List</* @Nullable */ String[]>>();
           privileges.put(sqlpriv, usersWithPermission);
-        } else {
-          List<String[]> permissionByGrantor = usersWithPermission.get(user);
-          if (permissionByGrantor == null) {
-            permissionByGrantor = new ArrayList<String[]>();
-            permissionByGrantor.add(grant);
-            usersWithPermission.put(user, permissionByGrantor);
-          } else {
-            permissionByGrantor.add(grant);
-          }
         }
+
+        List</* @Nullable */ String[]> permissionByGrantor = usersWithPermission.get(user);
+        //noinspection Java8MapApi
+        if (permissionByGrantor == null) {
+          permissionByGrantor = new ArrayList</* @Nullable */ String[]>();
+          usersWithPermission.put(user, permissionByGrantor);
+        }
+
+        /* @Nullable */ String[] grant = {grantor, grantable};
+        permissionByGrantor.add(grant);
       }
     }
   }
@@ -1990,7 +2000,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
    * @param owner owner
    * @return a Map mapping the SQL permission name
    */
-  public Map<String, Map<String, List<String[]>>> parseACL(String aclArray, String owner) {
+  public Map<String, Map<String, List</* @Nullable */ String[]>>> parseACL(/* @Nullable */ String aclArray,
+      String owner) {
     if (aclArray == null) {
       // arwdxt -- 8.2 Removed the separate RULE permission
       // arwdDxt -- 8.4 Added a separate TRUNCATE permission
@@ -2000,15 +2011,16 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     }
 
     List<String> acls = parseACLArray(aclArray);
-    Map<String, Map<String, List<String[]>>> privileges =
-        new HashMap<String, Map<String, List<String[]>>>();
+    Map<String, Map<String, List</* @Nullable */ String[]>>> privileges =
+        new HashMap<String, Map<String, List</* @Nullable */ String[]>>>();
     for (String acl : acls) {
       addACLPrivileges(acl, privileges);
     }
     return privileges;
   }
 
-  public ResultSet getBestRowIdentifier(String catalog, String schema, String table,
+  public ResultSet getBestRowIdentifier(
+      /* @Nullable */ String catalog, /* @Nullable */ String schema, String table,
       int scope, boolean nullable) throws SQLException {
     Field[] f = new Field[8];
     List<Tuple> v = new ArrayList<Tuple>(); // The new ResultSet tuple stuff
@@ -2049,7 +2061,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     Statement stmt = connection.createStatement();
     ResultSet rs = stmt.executeQuery(sql);
     while (rs.next()) {
-      byte[][] tuple = new byte[8][];
+      byte[] /* @Nullable */ [] tuple = new byte[8][];
       int typeOid = (int) rs.getLong("atttypid");
       int typeMod = rs.getInt("atttypmod");
       int decimalDigits = connection.getTypeInfo().getScale(typeOid, typeMod);
@@ -2075,7 +2087,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return ((BaseStatement) createMetaDataStatement()).createDriverResultSet(f, v);
   }
 
-  public ResultSet getVersionColumns(String catalog, String schema, String table)
+  public ResultSet getVersionColumns(
+      /* @Nullable */ String catalog, /* @Nullable */ String schema, String table)
       throws SQLException {
     Field[] f = new Field[8];
     List<Tuple> v = new ArrayList<Tuple>(); // The new ResultSet tuple stuff
@@ -2089,7 +2102,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     f[6] = new Field("DECIMAL_DIGITS", Oid.INT2);
     f[7] = new Field("PSEUDO_COLUMN", Oid.INT2);
 
-    byte[][] tuple = new byte[8][];
+    byte[] /* @Nullable */ [] tuple = new byte[8][];
 
     /*
      * Postgresql does not have any column types that are automatically updated like some databases'
@@ -2117,7 +2130,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return ((BaseStatement) createMetaDataStatement()).createDriverResultSet(f, v);
   }
 
-  public ResultSet getPrimaryKeys(String catalog, String schema, String table)
+  public ResultSet getPrimaryKeys(/* @Nullable */ String catalog, /* @Nullable */ String schema, String table)
       throws SQLException {
     String sql;
     sql = "SELECT NULL AS TABLE_CAT, n.nspname AS TABLE_SCHEM, "
@@ -2166,8 +2179,9 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
    * @return ResultSet
    * @throws SQLException if something wrong happens
    */
-  protected ResultSet getImportedExportedKeys(String primaryCatalog, String primarySchema,
-      String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable)
+  protected ResultSet getImportedExportedKeys(
+      /* @Nullable */ String primaryCatalog, /* @Nullable */ String primarySchema, /* @Nullable */ String primaryTable,
+      /* @Nullable */ String foreignCatalog, /* @Nullable */ String foreignSchema, /* @Nullable */ String foreignTable)
           throws SQLException {
 
     /*
@@ -2249,18 +2263,19 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return createMetaDataStatement().executeQuery(sql);
   }
 
-  public ResultSet getImportedKeys(String catalog, String schema, String table)
+  public ResultSet getImportedKeys(/* @Nullable */ String catalog, /* @Nullable */ String schema, String table)
       throws SQLException {
     return getImportedExportedKeys(null, null, null, catalog, schema, table);
   }
 
-  public ResultSet getExportedKeys(String catalog, String schema, String table)
+  public ResultSet getExportedKeys(/* @Nullable */ String catalog, /* @Nullable */ String schema, String table)
       throws SQLException {
     return getImportedExportedKeys(catalog, schema, table, null, null, null);
   }
 
-  public ResultSet getCrossReference(String primaryCatalog, String primarySchema,
-      String primaryTable, String foreignCatalog, String foreignSchema, String foreignTable)
+  public ResultSet getCrossReference(
+      /* @Nullable */ String primaryCatalog, /* @Nullable */ String primarySchema, String primaryTable,
+      /* @Nullable */ String foreignCatalog, /* @Nullable */ String foreignSchema, String foreignTable)
           throws SQLException {
     return getImportedExportedKeys(primaryCatalog, primarySchema, primaryTable, foreignCatalog,
         foreignSchema, foreignTable);
@@ -2321,8 +2336,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     }
 
     while (rs.next()) {
-      byte[][] tuple = new byte[19][];
-      String typname = rs.getString(1);
+      byte[] /* @Nullable */ [] tuple = new byte[19][];
+      String typname = castNonNull(rs.getString(1));
       int typeOid = (int) rs.getLong(2);
 
       tuple[0] = connection.encodeString(typname);
@@ -2361,13 +2376,13 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
 
       // add pseudo-type serial, bigserial
       if (typname.equals("int4")) {
-        byte[][] tuple1 = tuple.clone();
+        byte[] /* @Nullable */ [] tuple1 = tuple.clone();
 
         tuple1[0] = connection.encodeString("serial");
         tuple1[11] = bt;
         v.add(new Tuple(tuple1));
       } else if (typname.equals("int8")) {
-        byte[][] tuple1 = tuple.clone();
+        byte[] /* @Nullable */ [] tuple1 = tuple.clone();
 
         tuple1[0] = connection.encodeString("bigserial");
         tuple1[11] = bt;
@@ -2381,15 +2396,16 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     Collections.sort(v, new Comparator<Tuple>() {
       @Override
       public int compare(Tuple o1, Tuple o2) {
-        int i1 = ByteConverter.bytesToInt(o1.get(18));
-        int i2 = ByteConverter.bytesToInt(o2.get(18));
+        int i1 = ByteConverter.bytesToInt(castNonNull(o1.get(18)));
+        int i2 = ByteConverter.bytesToInt(castNonNull(o2.get(18)));
         return (i1 < i2) ? -1 : ((i1 == i2) ? 0 : 1);
       }
     });
     return ((BaseStatement) createMetaDataStatement()).createDriverResultSet(f, v);
   }
 
-  public ResultSet getIndexInfo(String catalog, String schema, String tableName,
+  public ResultSet getIndexInfo(
+      /* @Nullable */ String catalog, /* @Nullable */ String schema, String tableName,
       boolean unique, boolean approximate) throws SQLException {
     /*
      * This is a complicated function because we have three possible situations: <= 7.2 no schemas,
@@ -2578,8 +2594,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return true;
   }
 
-  public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern,
-      int[] types) throws SQLException {
+  public ResultSet getUDTs(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String typeNamePattern, int /* @Nullable */ [] types) throws SQLException {
     String sql = "select "
         + "null as type_cat, n.nspname as type_schem, t.typname as type_name,  null as class_name, "
         + "CASE WHEN t.typtype='c' then " + java.sql.Types.STRUCT + " else "
@@ -2698,7 +2714,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     List<Tuple> v = new ArrayList<Tuple>();
 
     if (connection.haveMinimumServerVersion(ServerVersion.v9_0)) {
-      byte[][] tuple = new byte[4][];
+      byte[] /* @Nullable */ [] tuple = new byte[4][];
       tuple[0] = connection.encodeString("ApplicationName");
       tuple[1] = connection.encodeString(Integer.toString(getMaxNameLength()));
       tuple[2] = connection.encodeString("");
@@ -2721,7 +2737,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     throw new SQLException("Cannot unwrap to " + iface.getName());
   }
 
-  public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern)
+  public ResultSet getFunctions(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String functionNamePattern)
       throws SQLException {
 
     // The pg_get_function_result only exists 8.4 or later
@@ -2773,8 +2790,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return createMetaDataStatement().executeQuery(sql);
   }
 
-  public ResultSet getFunctionColumns(String catalog, String schemaPattern,
-      String functionNamePattern, String columnNamePattern)
+  public ResultSet getFunctionColumns(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String functionNamePattern, /* @Nullable */ String columnNamePattern)
       throws SQLException {
     int columns = 17;
 
@@ -2825,7 +2842,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       String returnTypeType = rs.getString("typtype");
       int returnTypeRelid = (int) rs.getLong("typrelid");
 
-      String strArgTypes = rs.getString("proargtypes");
+      String strArgTypes = castNonNull(rs.getString("proargtypes"));
       StringTokenizer st = new StringTokenizer(strArgTypes);
       List<Long> argTypes = new ArrayList<Long>();
       while (st.hasMoreTokens()) {
@@ -2854,9 +2871,9 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       }
 
       // decide if we are returning a single column result.
-      if (returnTypeType.equals("b") || returnTypeType.equals("d") || returnTypeType.equals("e")
-          || (returnTypeType.equals("p") && argModesArray == null)) {
-        byte[][] tuple = new byte[columns][];
+      if ("b".equals(returnTypeType) || "d".equals(returnTypeType) || "e".equals(returnTypeType)
+          || ("p".equals(returnTypeType) && argModesArray == null)) {
+        byte[] /* @Nullable */ [] tuple = new byte[columns][];
         tuple[0] = null;
         tuple[1] = schema;
         tuple[2] = functionName;
@@ -2882,7 +2899,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
 
       // Add a row for each argument.
       for (int i = 0; i < numArgs; i++) {
-        byte[][] tuple = new byte[columns][];
+        byte[] /* @Nullable */ [] tuple = new byte[columns][];
         tuple[0] = null;
         tuple[1] = schema;
         tuple[2] = functionName;
@@ -2931,7 +2948,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
       }
 
       // if we are returning a multi-column result.
-      if (returnTypeType.equals("c") || (returnTypeType.equals("p") && argModesArray != null)) {
+      if ("c".equals(returnTypeType) || ("p".equals(returnTypeType) && argModesArray != null)) {
         String columnsql = "SELECT a.attname,a.atttypid FROM pg_catalog.pg_attribute a "
             + " WHERE a.attrelid = " + returnTypeRelid
             + " AND NOT a.attisdropped AND a.attnum > 0 ORDER BY a.attnum ";
@@ -2939,7 +2956,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
         ResultSet columnrs = columnstmt.executeQuery(columnsql);
         while (columnrs.next()) {
           int columnTypeOid = (int) columnrs.getLong("atttypid");
-          byte[][] tuple = new byte[columns][];
+          byte[] /* @Nullable */ [] tuple = new byte[columns][];
           tuple[0] = null;
           tuple[1] = schema;
           tuple[2] = functionName;
@@ -2972,8 +2989,9 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return ((BaseStatement) createMetaDataStatement()).createDriverResultSet(f, v);
   }
 
-  public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern,
-      String columnNamePattern) throws SQLException {
+  public ResultSet getPseudoColumns(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String tableNamePattern, /* @Nullable */ String columnNamePattern)
+      throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "getPseudoColumns(String, String, String, String)");
   }
@@ -3001,20 +3019,22 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     return true;
   }
 
-  public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern)
+  public ResultSet getSuperTypes(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String typeNamePattern)
       throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "getSuperTypes(String,String,String)");
   }
 
-  public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern)
+  public ResultSet getSuperTables(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String tableNamePattern)
       throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "getSuperTables(String,String,String,String)");
   }
 
-  public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern,
-      String attributeNamePattern) throws SQLException {
+  public ResultSet getAttributes(/* @Nullable */ String catalog, /* @Nullable */ String schemaPattern,
+      /* @Nullable */ String typeNamePattern, /* @Nullable */ String attributeNamePattern) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "getAttributes(String,String,String,String)");
   }
diff --git a/src/main/java/org/postgresql/jdbc/PgParameterMetaData.java b/src/main/java/org/postgresql/jdbc/PgParameterMetaData.java
index 8d977a7db346bb4051d35263cfd24f259f52ef2b..52cdbcfca19535782207c8be396b5b189fac40a4 100644
--- a/src/main/java/org/postgresql/jdbc/PgParameterMetaData.java
+++ b/src/main/java/org/postgresql/jdbc/PgParameterMetaData.java
@@ -5,11 +5,15 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseConnection;
 import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.index.qual.Positive;
+
 import java.sql.ParameterMetaData;
 import java.sql.SQLException;
 
@@ -24,7 +28,7 @@ public class PgParameterMetaData implements ParameterMetaData {
   }
 
   @Override
-  public String getParameterClassName(int param) throws SQLException {
+  public String getParameterClassName(/* @Positive */ int param) throws SQLException {
     checkParamIndex(param);
     return connection.getTypeInfo().getJavaClass(oids[param - 1]);
   }
@@ -52,7 +56,7 @@ public class PgParameterMetaData implements ParameterMetaData {
   @Override
   public String getParameterTypeName(int param) throws SQLException {
     checkParamIndex(param);
-    return connection.getTypeInfo().getPGType(oids[param - 1]);
+    return castNonNull(connection.getTypeInfo().getPGType(oids[param - 1]));
   }
 
   // we don't know this
diff --git a/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java b/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
index 32583bcffa0d4bc891a788935025eb35d493878a..adc189097820832d053ab294d03ac7a832c500b0 100644
--- a/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
+++ b/src/main/java/org/postgresql/jdbc/PgPreparedStatement.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.Driver;
 import org.postgresql.core.BaseConnection;
 import org.postgresql.core.CachedQuery;
@@ -29,6 +31,12 @@ import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 import org.postgresql.util.ReaderInputStream;
 
+// import org.checkerframework.checker.index.qual.NonNegative;
+// import org.checkerframework.checker.index.qual.Positive;
+// import org.checkerframework.checker.nullness.qual.NonNull;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.common.value.qual.IntRange;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -52,6 +60,7 @@ import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.RowId;
 import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
 import java.sql.SQLXML;
 import java.sql.Time;
 import java.sql.Timestamp;
@@ -63,16 +72,18 @@ import java.util.TimeZone;
 import java.util.UUID;
 
 class PgPreparedStatement extends PgStatement implements PreparedStatement {
+
   protected final CachedQuery preparedQuery; // Query fragments for prepared statement.
   protected final ParameterList preparedParameters; // Parameter values for prepared statement.
 
-  private TimeZone defaultTimeZone;
+  private /* @Nullable */ TimeZone defaultTimeZone;
 
   PgPreparedStatement(PgConnection connection, String sql, int rsType, int rsConcurrency,
       int rsHoldability) throws SQLException {
     this(connection, connection.borrowQuery(sql), rsType, rsConcurrency, rsHoldability);
   }
 
+  @SuppressWarnings("method.invocation.invalid")
   PgPreparedStatement(PgConnection connection, CachedQuery query, int rsType,
       int rsConcurrency, int rsHoldability) throws SQLException {
     super(connection, rsType, rsConcurrency, rsHoldability);
@@ -161,7 +172,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  protected boolean isOneShotQuery(CachedQuery cachedQuery) {
+  protected boolean isOneShotQuery(/* @Nullable */ CachedQuery cachedQuery) {
     if (cachedQuery == null) {
       cachedQuery = preparedQuery;
     }
@@ -256,17 +267,17 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     preparedParameters.setNull(parameterIndex, oid);
   }
 
-  public void setBoolean(int parameterIndex, boolean x) throws SQLException {
+  public void setBoolean(/* @Positive */ int parameterIndex, boolean x) throws SQLException {
     checkClosed();
     // The key words TRUE and FALSE are the preferred (SQL-compliant) usage.
     bindLiteral(parameterIndex, x ? "TRUE" : "FALSE", Oid.BOOL);
   }
 
-  public void setByte(int parameterIndex, byte x) throws SQLException {
+  public void setByte(/* @Positive */ int parameterIndex, byte x) throws SQLException {
     setShort(parameterIndex, x);
   }
 
-  public void setShort(int parameterIndex, short x) throws SQLException {
+  public void setShort(/* @Positive */ int parameterIndex, short x) throws SQLException {
     checkClosed();
     if (connection.binaryTransferSend(Oid.INT2)) {
       byte[] val = new byte[2];
@@ -277,7 +288,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     bindLiteral(parameterIndex, Integer.toString(x), Oid.INT2);
   }
 
-  public void setInt(int parameterIndex, int x) throws SQLException {
+  public void setInt(/* @Positive */ int parameterIndex, int x) throws SQLException {
     checkClosed();
     if (connection.binaryTransferSend(Oid.INT4)) {
       byte[] val = new byte[4];
@@ -288,7 +299,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     bindLiteral(parameterIndex, Integer.toString(x), Oid.INT4);
   }
 
-  public void setLong(int parameterIndex, long x) throws SQLException {
+  public void setLong(/* @Positive */ int parameterIndex, long x) throws SQLException {
     checkClosed();
     if (connection.binaryTransferSend(Oid.INT8)) {
       byte[] val = new byte[8];
@@ -299,7 +310,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     bindLiteral(parameterIndex, Long.toString(x), Oid.INT8);
   }
 
-  public void setFloat(int parameterIndex, float x) throws SQLException {
+  public void setFloat(/* @Positive */ int parameterIndex, float x) throws SQLException {
     checkClosed();
     if (connection.binaryTransferSend(Oid.FLOAT4)) {
       byte[] val = new byte[4];
@@ -310,7 +321,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     bindLiteral(parameterIndex, Float.toString(x), Oid.FLOAT8);
   }
 
-  public void setDouble(int parameterIndex, double x) throws SQLException {
+  public void setDouble(/* @Positive */ int parameterIndex, double x) throws SQLException {
     checkClosed();
     if (connection.binaryTransferSend(Oid.FLOAT8)) {
       byte[] val = new byte[8];
@@ -321,11 +332,12 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     bindLiteral(parameterIndex, Double.toString(x), Oid.FLOAT8);
   }
 
-  public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException {
+  public void setBigDecimal(/* @Positive */ int parameterIndex, /* @Nullable */ BigDecimal x)
+      throws SQLException {
     setNumber(parameterIndex, x);
   }
 
-  public void setString(int parameterIndex, String x) throws SQLException {
+  public void setString(/* @Positive */ int parameterIndex, /* @Nullable */ String x) throws SQLException {
     checkClosed();
     setString(parameterIndex, x, getStringType());
   }
@@ -334,7 +346,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     return (connection.getStringVarcharFlag() ? Oid.VARCHAR : Oid.UNSPECIFIED);
   }
 
-  protected void setString(int parameterIndex, String x, int oid) throws SQLException {
+  protected void setString(/* @Positive */ int parameterIndex,
+      /* @Nullable */ String x, int oid) throws SQLException {
     // if the passed string is null, then set this column to null
     checkClosed();
     if (x == null) {
@@ -344,7 +357,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  public void setBytes(int parameterIndex, byte[] x) throws SQLException {
+  public void setBytes(/* @Positive */ int parameterIndex, byte /* @Nullable */[] x) throws SQLException {
     checkClosed();
 
     if (null == x) {
@@ -358,23 +371,26 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     preparedParameters.setBytea(parameterIndex, copy, 0, x.length);
   }
 
-  private void setByteStreamWriter(int parameterIndex, ByteStreamWriter x) throws SQLException {
+  private void setByteStreamWriter(/* @Positive */ int parameterIndex,
+      ByteStreamWriter x) throws SQLException {
     preparedParameters.setBytea(parameterIndex, x);
   }
 
-  public void setDate(int parameterIndex, java.sql.Date x) throws SQLException {
+  public void setDate(/* @Positive */ int parameterIndex,
+      java.sql./* @Nullable */ Date x) throws SQLException {
     setDate(parameterIndex, x, null);
   }
 
-  public void setTime(int parameterIndex, Time x) throws SQLException {
+  public void setTime(/* @Positive */ int parameterIndex, /* @Nullable */ Time x) throws SQLException {
     setTime(parameterIndex, x, null);
   }
 
-  public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException {
+  public void setTimestamp(/* @Positive */ int parameterIndex, /* @Nullable */ Timestamp x) throws SQLException {
     setTimestamp(parameterIndex, x, null);
   }
 
-  private void setCharacterStreamPost71(int parameterIndex, InputStream x, int length,
+  private void setCharacterStreamPost71(/* @Positive */ int parameterIndex,
+      /* @Nullable */ InputStream x, int length,
       String encoding) throws SQLException {
 
     if (x == null) {
@@ -419,18 +435,21 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException {
+  public void setAsciiStream(/* @Positive */ int parameterIndex, /* @Nullable */ InputStream x,
+      /* @NonNegative */ int length) throws SQLException {
     checkClosed();
     setCharacterStreamPost71(parameterIndex, x, length, "ASCII");
   }
 
-  public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException {
+  public void setUnicodeStream(/* @Positive */ int parameterIndex, /* @Nullable */ InputStream x,
+      /* @NonNegative */ int length) throws SQLException {
     checkClosed();
 
     setCharacterStreamPost71(parameterIndex, x, length, "UTF-8");
   }
 
-  public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException {
+  public void setBinaryStream(/* @Positive */ int parameterIndex, /* @Nullable */ InputStream x,
+      /* @NonNegative */ int length) throws SQLException {
     checkClosed();
 
     if (x == null) {
@@ -456,7 +475,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
   }
 
   // Helper method for setting parameters to PGobject subclasses.
-  private void setPGobject(int parameterIndex, PGobject x) throws SQLException {
+  private void setPGobject(/* @Positive */ int parameterIndex, PGobject x) throws SQLException {
     String typename = x.getType();
     int oid = connection.getTypeInfo().getPGType(typename);
     if (oid == Oid.UNSPECIFIED) {
@@ -474,7 +493,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  private void setMap(int parameterIndex, Map<?, ?> x) throws SQLException {
+  private void setMap(/* @Positive */ int parameterIndex, Map<?, ?> x) throws SQLException {
     int oid = connection.getTypeInfo().getPGType("hstore");
     if (oid == Oid.UNSPECIFIED) {
       throw new PSQLException(GT.tr("No hstore extension installed."),
@@ -488,7 +507,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  private void setNumber(int parameterIndex, Number x) throws SQLException {
+  private void setNumber(/* @Positive */ int parameterIndex, /* @Nullable */ Number x) throws SQLException {
     checkClosed();
     if (x == null) {
       setNull(parameterIndex, Types.DECIMAL);
@@ -498,7 +517,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
   }
 
   @Override
-  public void setObject(int parameterIndex, Object in, int targetSqlType, int scale)
+  public void setObject(/* @Positive */ int parameterIndex, /* @Nullable */ Object in,
+      int targetSqlType, int scale)
       throws SQLException {
     checkClosed();
 
@@ -660,13 +680,14 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
       case Types.ARRAY:
         if (in instanceof Array) {
           setArray(parameterIndex, (Array) in);
-        } else if (PrimitiveArraySupport.isSupportedPrimitiveArray(in)) {
-          setPrimitiveArray(parameterIndex, in);
         } else {
-          throw new PSQLException(
-              GT.tr("Cannot cast an instance of {0} to type {1}",
-                  in.getClass().getName(), "Types.ARRAY"),
-              PSQLState.INVALID_PARAMETER_TYPE);
+          try {
+            setObjectArray(parameterIndex, in);
+          } catch (Exception e) {
+            throw new PSQLException(
+                GT.tr("Cannot cast an instance of {0} to type {1}", in.getClass().getName(), "Types.ARRAY"),
+                PSQLState.INVALID_PARAMETER_TYPE, e);
+          }
         }
         break;
       case Types.DISTINCT:
@@ -687,18 +708,24 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  private <A> void setPrimitiveArray(int parameterIndex, A in) throws SQLException {
-    final PrimitiveArraySupport<A> arrayToString = PrimitiveArraySupport.getArraySupport(in);
+  private <A extends /* @NonNull */ Object> void setObjectArray(int parameterIndex, A in) throws SQLException {
+    final ArrayEncoding.ArrayEncoder<A> arraySupport = ArrayEncoding.getArrayEncoder(in);
 
     final TypeInfo typeInfo = connection.getTypeInfo();
 
-    final int oid = arrayToString.getDefaultArrayTypeOid(typeInfo);
+    final int oid = arraySupport.getDefaultArrayTypeOid();
 
-    if (arrayToString.supportBinaryRepresentation() && connection.getPreferQueryMode() != PreferQueryMode.SIMPLE) {
-      bindBytes(parameterIndex, arrayToString.toBinaryRepresentation(connection, in), oid);
+    if (arraySupport.supportBinaryRepresentation(oid) && connection.getPreferQueryMode() != PreferQueryMode.SIMPLE) {
+      bindBytes(parameterIndex, arraySupport.toBinaryRepresentation(connection, in, oid), oid);
     } else {
-      final char delim = typeInfo.getArrayDelimiter(oid);
-      setString(parameterIndex, arrayToString.toArrayString(delim, in), oid);
+      if (oid == Oid.UNSPECIFIED) {
+        throw new SQLFeatureNotSupportedException();
+      }
+      final int baseOid = typeInfo.getPGArrayElement(oid);
+      final String baseType = castNonNull(typeInfo.getPGType(baseOid));
+
+      final Array array = getPGConnection().createArrayOf(baseType, in);
+      this.setArray(parameterIndex, array);
     }
   }
 
@@ -892,20 +919,21 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
   }
 
   private static PSQLException cannotCastException(final String fromType, final String toType,
-      final Exception cause) {
+      final /* @Nullable */ Exception cause) {
     return new PSQLException(
         GT.tr("Cannot convert an instance of {0} to type {1}", fromType, toType),
         PSQLState.INVALID_PARAMETER_TYPE, cause);
   }
 
-  public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException {
+  public void setObject(/* @Positive */ int parameterIndex, /* @Nullable */ Object x,
+      int targetSqlType) throws SQLException {
     setObject(parameterIndex, x, targetSqlType, -1);
   }
 
   /*
    * This stores an Object into a parameter.
    */
-  public void setObject(int parameterIndex, Object x) throws SQLException {
+  public void setObject(/* @Positive */ int parameterIndex, /* @Nullable */ Object x) throws SQLException {
     checkClosed();
     if (x == null) {
       setNull(parameterIndex, Types.OTHER);
@@ -965,8 +993,14 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
       setMap(parameterIndex, (Map<?, ?>) x);
     } else if (x instanceof Number) {
       setNumber(parameterIndex, (Number) x);
-    } else if (PrimitiveArraySupport.isSupportedPrimitiveArray(x)) {
-      setPrimitiveArray(parameterIndex, x);
+    } else if (x.getClass().isArray()) {
+      try {
+        setObjectArray(parameterIndex, x);
+      } catch (Exception e) {
+        throw new PSQLException(
+            GT.tr("Cannot cast an instance of {0} to type {1}", x.getClass().getName(), "Types.ARRAY"),
+            PSQLState.INVALID_PARAMETER_TYPE, e);
+      }
     } else {
       // Can't infer a type.
       throw new PSQLException(GT.tr(
@@ -998,11 +1032,13 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
    * @param oid type oid
    * @throws SQLException if something goes wrong
    */
-  protected void bindLiteral(int paramIndex, String s, int oid) throws SQLException {
+  protected void bindLiteral(/* @Positive */ int paramIndex,
+      String s, int oid) throws SQLException {
     preparedParameters.setLiteralParameter(paramIndex, s, oid);
   }
 
-  protected void bindBytes(int paramIndex, byte[] b, int oid) throws SQLException {
+  protected void bindBytes(/* @Positive */ int paramIndex,
+      byte[] b, int oid) throws SQLException {
     preparedParameters.setBinaryParameter(paramIndex, b, oid);
   }
 
@@ -1015,7 +1051,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
    * @param oid type oid
    * @throws SQLException if something goes wrong
    */
-  private void bindString(int paramIndex, String s, int oid) throws SQLException {
+  private void bindString(/* @Positive */ int paramIndex, String s, int oid) throws SQLException {
     preparedParameters.setStringParameter(paramIndex, s, oid);
   }
 
@@ -1037,9 +1073,13 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
   @Override
   public void addBatch() throws SQLException {
     checkClosed();
+    ArrayList<Query> batchStatements = this.batchStatements;
     if (batchStatements == null) {
-      batchStatements = new ArrayList<Query>();
-      batchParameters = new ArrayList<ParameterList>();
+      this.batchStatements = batchStatements = new ArrayList<Query>();
+    }
+    ArrayList</* @Nullable */ ParameterList> batchParameters = this.batchParameters;
+    if (batchParameters == null) {
+      this.batchParameters = batchParameters = new ArrayList</* @Nullable */ ParameterList>();
     }
     // we need to create copies of our parameters, otherwise the values can be changed
     batchParameters.add(preparedParameters.copy());
@@ -1049,7 +1089,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  public ResultSetMetaData getMetaData() throws SQLException {
+  public /* @Nullable */ ResultSetMetaData getMetaData() throws SQLException {
     checkClosed();
     ResultSet rs = getResultSet();
 
@@ -1077,7 +1117,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     return null;
   }
 
-  public void setArray(int i, java.sql.Array x) throws SQLException {
+  public void setArray(int i, java.sql./* @Nullable */ Array x) throws SQLException {
     checkClosed();
 
     if (null == x) {
@@ -1098,8 +1138,9 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
 
     if (x instanceof PgArray) {
       PgArray arr = (PgArray) x;
-      if (arr.isBinary()) {
-        bindBytes(i, arr.toBytes(), oid);
+      byte[] bytes = arr.toBytes();
+      if (bytes != null) {
+        bindBytes(i, bytes, oid);
         return;
       }
     }
@@ -1107,7 +1148,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     setString(i, x.toString(), oid);
   }
 
-  protected long createBlob(int i, InputStream inputStream, long length) throws SQLException {
+  protected long createBlob(int i, InputStream inputStream,
+      /* @NonNegative */ long length) throws SQLException {
     LargeObjectManager lom = connection.getLargeObjectAPI();
     long oid = lom.createLO();
     LargeObject lob = lom.open(oid);
@@ -1140,7 +1182,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     return oid;
   }
 
-  public void setBlob(int i, Blob x) throws SQLException {
+  public void setBlob(/* @Positive */ int i, /* @Nullable */ Blob x) throws SQLException {
     checkClosed();
 
     if (x == null) {
@@ -1178,7 +1220,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  public void setCharacterStream(int i, java.io.Reader x, int length) throws SQLException {
+  public void setCharacterStream(/* @Positive */ int i, java.io./* @Nullable */ Reader x,
+      /* @NonNegative */ int length) throws SQLException {
     checkClosed();
 
     if (x == null) {
@@ -1201,7 +1244,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
   }
 
   @Override
-  public void setClob(int i, Clob x) throws SQLException {
+  public void setClob(/* @Positive */ int i, /* @Nullable */ Clob x) throws SQLException {
     checkClosed();
 
     if (x == null) {
@@ -1237,7 +1280,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     setLong(i, oid);
   }
 
-  public void setNull(int parameterIndex, int t, String typeName) throws SQLException {
+  public void setNull(/* @Positive */ int parameterIndex, int t,
+      /* @Nullable */ String typeName) throws SQLException {
     if (typeName == null) {
       setNull(parameterIndex, t);
       return;
@@ -1251,11 +1295,12 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     preparedParameters.setNull(parameterIndex, oid);
   }
 
-  public void setRef(int i, Ref x) throws SQLException {
+  public void setRef(/* @Positive */ int i, /* @Nullable */ Ref x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setRef(int,Ref)");
   }
 
-  public void setDate(int i, java.sql.Date d, java.util.Calendar cal) throws SQLException {
+  public void setDate(/* @Positive */ int i, java.sql./* @Nullable */ Date d,
+      java.util./* @Nullable */ Calendar cal) throws SQLException {
     checkClosed();
 
     if (d == null) {
@@ -1296,7 +1341,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     bindString(i, connection.getTimestampUtils().toString(cal, d), Oid.UNSPECIFIED);
   }
 
-  public void setTime(int i, Time t, java.util.Calendar cal) throws SQLException {
+  public void setTime(/* @Positive */ int i, /* @Nullable */ Time t,
+      java.util./* @Nullable */ Calendar cal) throws SQLException {
     checkClosed();
 
     if (t == null) {
@@ -1323,7 +1369,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     bindString(i, connection.getTimestampUtils().toString(cal, t), oid);
   }
 
-  public void setTimestamp(int i, Timestamp t, java.util.Calendar cal) throws SQLException {
+  public void setTimestamp(/* @Positive */ int i, /* @Nullable */ Timestamp t,
+      java.util./* @Nullable */ Calendar cal) throws SQLException {
     checkClosed();
 
     if (t == null) {
@@ -1380,22 +1427,24 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
   }
 
   //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
-  private void setDate(int i, java.time.LocalDate localDate) throws SQLException {
+  private void setDate(/* @Positive */ int i, java.time.LocalDate localDate) throws SQLException {
     int oid = Oid.DATE;
     bindString(i, connection.getTimestampUtils().toString(localDate), oid);
   }
 
-  private void setTime(int i, java.time.LocalTime localTime) throws SQLException {
+  private void setTime(/* @Positive */ int i, java.time.LocalTime localTime) throws SQLException {
     int oid = Oid.TIME;
     bindString(i, connection.getTimestampUtils().toString(localTime), oid);
   }
 
-  private void setTimestamp(int i, java.time.LocalDateTime localDateTime) throws SQLException {
+  private void setTimestamp(/* @Positive */ int i, java.time.LocalDateTime localDateTime)
+      throws SQLException {
     int oid = Oid.TIMESTAMP;
     bindString(i, connection.getTimestampUtils().toString(localDateTime), oid);
   }
 
-  private void setTimestamp(int i, java.time.OffsetDateTime offsetDateTime) throws SQLException {
+  private void setTimestamp(/* @Positive */ int i, java.time.OffsetDateTime offsetDateTime)
+      throws SQLException {
     int oid = Oid.TIMESTAMPTZ;
     bindString(i, connection.getTimestampUtils().toString(offsetDateTime), oid);
   }
@@ -1407,40 +1456,45 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
   }
 
   //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
-  public void setObject(int parameterIndex, Object x, java.sql.SQLType targetSqlType,
+  public void setObject(/* @Positive */ int parameterIndex, /* @Nullable */ Object x,
+      java.sql.SQLType targetSqlType,
       int scaleOrLength) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setObject");
   }
 
-  public void setObject(int parameterIndex, Object x, java.sql.SQLType targetSqlType)
+  public void setObject(/* @Positive */ int parameterIndex, /* @Nullable */ Object x,
+      java.sql.SQLType targetSqlType)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setObject");
   }
   //#endif
 
-  public void setRowId(int parameterIndex, RowId x) throws SQLException {
+  public void setRowId(/* @Positive */ int parameterIndex, /* @Nullable */ RowId x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setRowId(int, RowId)");
   }
 
-  public void setNString(int parameterIndex, String value) throws SQLException {
+  public void setNString(/* @Positive */ int parameterIndex, /* @Nullable */ String value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNString(int, String)");
   }
 
-  public void setNCharacterStream(int parameterIndex, Reader value, long length)
+  public void setNCharacterStream(/* @Positive */ int parameterIndex, /* @Nullable */ Reader value, long length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNCharacterStream(int, Reader, long)");
   }
 
-  public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException {
+  public void setNCharacterStream(/* @Positive */ int parameterIndex,
+      /* @Nullable */ Reader value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNCharacterStream(int, Reader)");
   }
 
-  public void setCharacterStream(int parameterIndex, Reader value, long length)
+  public void setCharacterStream(/* @Positive */ int parameterIndex,
+      /* @Nullable */ Reader value, /* @NonNegative */ long length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setCharacterStream(int, Reader, long)");
   }
 
-  public void setCharacterStream(int parameterIndex, Reader value) throws SQLException {
+  public void setCharacterStream(/* @Positive */ int parameterIndex,
+      /* @Nullable */ Reader value) throws SQLException {
     if (connection.getPreferQueryMode() == PreferQueryMode.SIMPLE) {
       String s = (value != null) ? readerToString(value, Integer.MAX_VALUE) : null;
       setString(parameterIndex, s);
@@ -1450,41 +1504,58 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     setObject(parameterIndex, is, Types.LONGVARCHAR);
   }
 
-  public void setBinaryStream(int parameterIndex, InputStream value, long length)
+  public void setBinaryStream(/* @Positive */ int parameterIndex, /* @Nullable */ InputStream value,
+      /* @NonNegative */ /* @IntRange(from = 0, to = Integer.MAX_VALUE) */ long length)
       throws SQLException {
+    //noinspection ConstantConditions
     if (length > Integer.MAX_VALUE) {
       throw new PSQLException(GT.tr("Object is too large to send over the protocol."),
           PSQLState.NUMERIC_CONSTANT_OUT_OF_RANGE);
     }
-    preparedParameters.setBytea(parameterIndex, value, (int) length);
+    if (value == null) {
+      preparedParameters.setNull(parameterIndex, Oid.BYTEA);
+    } else {
+      preparedParameters.setBytea(parameterIndex, value, (int) length);
+    }
   }
 
-  public void setBinaryStream(int parameterIndex, InputStream value) throws SQLException {
-    preparedParameters.setBytea(parameterIndex, value);
+  public void setBinaryStream(/* @Positive */ int parameterIndex,
+      /* @Nullable */ InputStream value) throws SQLException {
+    if (value == null) {
+      preparedParameters.setNull(parameterIndex, Oid.BYTEA);
+    } else {
+      preparedParameters.setBytea(parameterIndex, value);
+    }
   }
 
-  public void setAsciiStream(int parameterIndex, InputStream value, long length)
+  public void setAsciiStream(/* @Positive */ int parameterIndex,
+      /* @Nullable */ InputStream value, /* @NonNegative */ long length)
       throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setAsciiStream(int, InputStream, long)");
   }
 
-  public void setAsciiStream(int parameterIndex, InputStream value) throws SQLException {
+  public void setAsciiStream(/* @Positive */ int parameterIndex,
+      /* @Nullable */ InputStream value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setAsciiStream(int, InputStream)");
   }
 
-  public void setNClob(int parameterIndex, NClob value) throws SQLException {
+  public void setNClob(/* @Positive */ int parameterIndex,
+      /* @Nullable */ NClob value) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNClob(int, NClob)");
   }
 
-  public void setClob(int parameterIndex, Reader reader, long length) throws SQLException {
+  public void setClob(/* @Positive */ int parameterIndex,
+      /* @Nullable */ Reader reader, /* @NonNegative */ long length) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setClob(int, Reader, long)");
   }
 
-  public void setClob(int parameterIndex, Reader reader) throws SQLException {
+  public void setClob(/* @Positive */ int parameterIndex,
+      /* @Nullable */ Reader reader) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setClob(int, Reader)");
   }
 
-  public void setBlob(int parameterIndex, InputStream inputStream, long length)
+  public void setBlob(/* @Positive */ int parameterIndex,
+      /* @Nullable */ InputStream inputStream, /* @NonNegative */ long length)
       throws SQLException {
     checkClosed();
 
@@ -1493,6 +1564,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
       return;
     }
 
+    //noinspection ConstantConditions
     if (length < 0) {
       throw new PSQLException(GT.tr("Invalid stream length {0}.", length),
           PSQLState.INVALID_PARAMETER_VALUE);
@@ -1502,7 +1574,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     setLong(parameterIndex, oid);
   }
 
-  public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException {
+  public void setBlob(/* @Positive */ int parameterIndex,
+      /* @Nullable */ InputStream inputStream) throws SQLException {
     checkClosed();
 
     if (inputStream == null) {
@@ -1514,15 +1587,18 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     setLong(parameterIndex, oid);
   }
 
-  public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException {
+  public void setNClob(/* @Positive */ int parameterIndex,
+      /* @Nullable */ Reader reader, /* @NonNegative */ long length) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNClob(int, Reader, long)");
   }
 
-  public void setNClob(int parameterIndex, Reader reader) throws SQLException {
+  public void setNClob(/* @Positive */ int parameterIndex,
+      /* @Nullable */ Reader reader) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setNClob(int, Reader)");
   }
 
-  public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException {
+  public void setSQLXML(/* @Positive */ int parameterIndex,
+      /* @Nullable */ SQLXML xmlObject) throws SQLException {
     checkClosed();
     String stringValue = xmlObject == null ? null : xmlObject.getString();
     if (stringValue == null) {
@@ -1532,7 +1608,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  private void setUuid(int parameterIndex, UUID uuid) throws SQLException {
+  private void setUuid(/* @Positive */ int parameterIndex, UUID uuid) throws SQLException {
     if (connection.binaryTransferSend(Oid.UUID)) {
       byte[] val = new byte[16];
       ByteConverter.int8(val, 0, uuid.getMostSignificantBits());
@@ -1543,7 +1619,7 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     }
   }
 
-  public void setURL(int parameterIndex, java.net.URL x) throws SQLException {
+  public void setURL(/* @Positive */ int parameterIndex, java.net./* @Nullable */ URL x) throws SQLException {
     throw Driver.notImplemented(this.getClass(), "setURL(int,URL)");
   }
 
@@ -1586,17 +1662,13 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
         flags);
 
     int[] oids = preparedParameters.getTypeOIDs();
-    if (oids != null) {
-      return createParameterMetaData(connection, oids);
-    }
-
-    return null;
-
+    return createParameterMetaData(connection, oids);
   }
 
   @Override
   protected void transformQueriesAndParameters() throws SQLException {
-    if (batchParameters.size() <= 1
+    ArrayList</* @Nullable */ ParameterList> batchParameters = this.batchParameters;
+    if (batchParameters == null || batchParameters.size() <= 1
         || !(preparedQuery.query instanceof BatchedQuery)) {
       return;
     }
@@ -1616,7 +1688,8 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
     final int partialValueBlocksCount = Integer.bitCount(unprocessedBatchCount % maxValueBlocks);
     final int count = fullValueBlocksCount + partialValueBlocksCount;
     ArrayList<Query> newBatchStatements = new ArrayList<Query>(count);
-    ArrayList<ParameterList> newBatchParameters = new ArrayList<ParameterList>(count);
+    ArrayList</* @Nullable */ ParameterList> newBatchParameters =
+        new ArrayList</* @Nullable */ ParameterList>(count);
     int offset = 0;
     for (int i = 0; i < count; i++) {
       int valueBlock;
@@ -1630,13 +1703,15 @@ class PgPreparedStatement extends PgStatement implements PreparedStatement {
       ParameterList newPl = bq.createParameterList();
       for (int j = 0; j < valueBlock; j++) {
         ParameterList pl = batchParameters.get(offset++);
-        newPl.appendAll(pl);
+        if (pl != null) {
+          newPl.appendAll(pl);
+        }
       }
       newBatchStatements.add(bq);
       newBatchParameters.add(newPl);
       unprocessedBatchCount -= valueBlock;
     }
-    batchStatements = newBatchStatements;
-    batchParameters = newBatchParameters;
+    this.batchStatements = newBatchStatements;
+    this.batchParameters = newBatchParameters;
   }
 }
diff --git a/src/main/java/org/postgresql/jdbc/PgResultSet.java b/src/main/java/org/postgresql/jdbc/PgResultSet.java
index 6ee87d3681002f62288145bf5372c487e0b29976..288a7429bbaaf5d1c4687378fdd553b87ae02053 100644
--- a/src/main/java/org/postgresql/jdbc/PgResultSet.java
+++ b/src/main/java/org/postgresql/jdbc/PgResultSet.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGResultSetMetaData;
 import org.postgresql.PGStatement;
 import org.postgresql.core.BaseConnection;
@@ -21,12 +23,21 @@ import org.postgresql.core.Utils;
 import org.postgresql.util.ByteConverter;
 import org.postgresql.util.GT;
 import org.postgresql.util.HStoreConverter;
+import org.postgresql.util.JdbcBlackHole;
 import org.postgresql.util.PGbytea;
 import org.postgresql.util.PGobject;
 import org.postgresql.util.PGtokenizer;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.index.qual.NonNegative;
+// import org.checkerframework.checker.index.qual.Positive;
+// import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.checker.nullness.qual.PolyNull;
+// import org.checkerframework.checker.nullness.qual.RequiresNonNull;
+// import org.checkerframework.dataflow.qual.Pure;
+
 import java.io.ByteArrayInputStream;
 import java.io.CharArrayReader;
 import java.io.IOException;
@@ -74,49 +85,47 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   // needed for updateable result set support
   private boolean updateable = false;
   private boolean doingUpdates = false;
-  private HashMap<String, Object> updateValues = null;
+  private /* @Nullable */ HashMap<String, Object> updateValues = null;
   private boolean usingOID = false; // are we using the OID for the primary key?
-  private List<PrimaryKey> primaryKeys; // list of primary keys
+  private /* @Nullable */ List<PrimaryKey> primaryKeys; // list of primary keys
   private boolean singleTable = false;
   private String onlyTable = "";
-  private String tableName = null;
-  private PreparedStatement updateStatement = null;
-  private PreparedStatement insertStatement = null;
-  private PreparedStatement deleteStatement = null;
-  private PreparedStatement selectStatement = null;
+  private /* @Nullable */ String tableName = null;
+  private /* @Nullable */ PreparedStatement deleteStatement = null;
   private final int resultsettype;
   private final int resultsetconcurrency;
   private int fetchdirection = ResultSet.FETCH_UNKNOWN;
-  private TimeZone defaultTimeZone;
+  private /* @Nullable */ TimeZone defaultTimeZone;
   protected final BaseConnection connection; // the connection we belong to
   protected final BaseStatement statement; // the statement we belong to
   protected final Field[] fields; // Field metadata for this resultset.
-  protected final Query originalQuery; // Query we originated from
+  protected final /* @Nullable */ Query originalQuery; // Query we originated from
 
   protected final int maxRows; // Maximum rows in this resultset (might be 0).
   protected final int maxFieldSize; // Maximum field size in this resultset (might be 0).
 
-  protected List<Tuple> rows; // Current page of results.
+  protected /* @Nullable */ List<Tuple> rows; // Current page of results.
   protected int currentRow = -1; // Index into 'rows' of our currrent row (0-based)
   protected int rowOffset; // Offset of row 0 in the actual resultset
-  protected Tuple thisRow; // copy of the current result row
-  protected SQLWarning warnings = null; // The warning chain
+  protected /* @Nullable */ Tuple thisRow; // copy of the current result row
+  protected /* @Nullable */ SQLWarning warnings = null; // The warning chain
   /**
    * True if the last obtained column value was SQL NULL as specified by {@link #wasNull}. The value
-   * is always updated by the {@link #checkResultSet} method.
+   * is always updated by the {@link #getRawValue} method.
    */
   protected boolean wasNullFlag = false;
   protected boolean onInsertRow = false;
   // are we on the insert row (for JDBC2 updatable resultsets)?
 
-  private Tuple rowBuffer = null; // updateable rowbuffer
+  private /* @Nullable */ Tuple rowBuffer = null; // updateable rowbuffer
 
   protected int fetchSize; // Current fetch size (might be 0).
-  protected ResultCursor cursor; // Cursor for fetching additional data.
+  protected /* @Nullable */ ResultCursor cursor; // Cursor for fetching additional data.
 
-  private Map<String, Integer> columnNameIndexMap; // Speed up findColumn by caching lookups
+  // Speed up findColumn by caching lookups
+  private /* @Nullable */ Map<String, Integer> columnNameIndexMap;
 
-  private ResultSetMetaData rsMetaData;
+  private /* @Nullable */ ResultSetMetaData rsMetaData;
 
   protected ResultSetMetaData createMetaData() throws SQLException {
     return new PgResultSetMetaData(connection, fields);
@@ -130,8 +139,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return rsMetaData;
   }
 
-  PgResultSet(Query originalQuery, BaseStatement statement, Field[] fields, List<Tuple> tuples,
-      ResultCursor cursor, int maxRows, int maxFieldSize, int rsType, int rsConcurrency,
+  PgResultSet(/* @Nullable */ Query originalQuery, BaseStatement statement,
+      Field[] fields, List<Tuple> tuples,
+      /* @Nullable */ ResultCursor cursor, int maxRows, int maxFieldSize, int rsType, int rsConcurrency,
       int rsHoldability) throws SQLException {
     // Fail-fast on invalid null inputs
     if (tuples == null) {
@@ -153,7 +163,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     this.resultsetconcurrency = rsConcurrency;
   }
 
-  public java.net.URL getURL(int columnIndex) throws SQLException {
+  public java.net.URL getURL(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getURL columnIndex: {0}", columnIndex);
     checkClosed();
     throw org.postgresql.Driver.notImplemented(this.getClass(), "getURL(int)");
@@ -163,7 +173,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return getURL(findColumn(columnName));
   }
 
-  protected Object internalGetObject(int columnIndex, Field field) throws SQLException {
+  /* @RequiresNonNull({"thisRow"}) */
+  protected /* @Nullable */ Object internalGetObject(/* @Positive */ int columnIndex, Field field) throws SQLException {
+    castNonNull(thisRow, "thisRow");
     switch (getSQLType(columnIndex)) {
       case Types.BOOLEAN:
       case Types.BIT:
@@ -216,15 +228,15 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
         if (type.equals("uuid")) {
           if (isBinary(columnIndex)) {
-            return getUUID(thisRow.get(columnIndex - 1));
+            return getUUID(castNonNull(thisRow.get(columnIndex - 1)));
           }
-          return getUUID(getString(columnIndex));
+          return getUUID(castNonNull(getString(columnIndex)));
         }
 
         // Specialized support for ref cursors is neater.
         if (type.equals("refcursor")) {
           // Fetch all results.
-          String cursorName = getString(columnIndex);
+          String cursorName = castNonNull(getString(columnIndex));
 
           StringBuilder sb = new StringBuilder("FETCH ALL IN ");
           Utils.escapeIdentifier(sb, cursorName);
@@ -256,9 +268,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
         }
         if ("hstore".equals(type)) {
           if (isBinary(columnIndex)) {
-            return HStoreConverter.fromBytes(thisRow.get(columnIndex - 1), connection.getEncoding());
+            return HStoreConverter.fromBytes(castNonNull(thisRow.get(columnIndex - 1)),
+                connection.getEncoding());
           }
-          return HStoreConverter.fromString(getString(columnIndex));
+          return HStoreConverter.fromString(castNonNull(getString(columnIndex)));
         }
 
         // Caller determines what to do (JDBC3 overrides in this case)
@@ -266,6 +279,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
+  /* @Pure */
+  /* @EnsuresNonNull("rows") */
   private void checkScrollable() throws SQLException {
     checkClosed();
     if (resultsettype == ResultSet.TYPE_FORWARD_ONLY) {
@@ -360,7 +375,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   }
 
   @Override
-  public Array getArray(String colName) throws SQLException {
+  public /* @Nullable */ Array getArray(String colName) throws SQLException {
     return getArray(findColumn(colName));
   }
 
@@ -372,29 +387,30 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return new PgArray(connection, oid, value);
   }
 
+  /* @Pure */
   @Override
-  public Array getArray(int i) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  public /* @Nullable */ Array getArray(int i) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
     int oid = fields[i - 1].getOID();
     if (isBinary(i)) {
-      return makeArray(oid, thisRow.get(i - 1));
+      return makeArray(oid, value);
     }
-    return makeArray(oid, getFixedString(i));
+    return makeArray(oid, castNonNull(getFixedString(i)));
   }
 
-  public java.math.BigDecimal getBigDecimal(int columnIndex) throws SQLException {
+  public java.math./* @Nullable */ BigDecimal getBigDecimal(/* @Positive */ int columnIndex) throws SQLException {
     return getBigDecimal(columnIndex, -1);
   }
 
-  public java.math.BigDecimal getBigDecimal(String columnName) throws SQLException {
+  public java.math./* @Nullable */ BigDecimal getBigDecimal(String columnName) throws SQLException {
     return getBigDecimal(findColumn(columnName));
   }
 
-  public Blob getBlob(String columnName) throws SQLException {
+  public /* @Nullable */ Blob getBlob(String columnName) throws SQLException {
     return getBlob(findColumn(columnName));
   }
 
@@ -402,22 +418,23 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return new PgBlob(connection, oid);
   }
 
-  public Blob getBlob(int i) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  /* @Pure */
+  public /* @Nullable */ Blob getBlob(int i) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
     return makeBlob(getLong(i));
   }
 
-  public java.io.Reader getCharacterStream(String columnName) throws SQLException {
+  public java.io./* @Nullable */ Reader getCharacterStream(String columnName) throws SQLException {
     return getCharacterStream(findColumn(columnName));
   }
 
-  public java.io.Reader getCharacterStream(int i) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  public java.io./* @Nullable */ Reader getCharacterStream(int i) throws SQLException {
+    String value = getString(i);
+    if (value == null) {
       return null;
     }
 
@@ -427,10 +444,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     // long string datatype, but with toast the text datatype is capable of
     // handling very large values. Thus the implementation ends up calling
     // getString() since there is no current way to stream the value from the server
-    return new CharArrayReader(getString(i).toCharArray());
+    return new CharArrayReader(value.toCharArray());
   }
 
-  public Clob getClob(String columnName) throws SQLException {
+  public /* @Nullable */ Clob getClob(String columnName) throws SQLException {
     return getClob(findColumn(columnName));
   }
 
@@ -438,9 +455,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return new PgClob(connection, oid);
   }
 
-  public Clob getClob(int i) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  /* @Pure */
+  public /* @Nullable */ Clob getClob(int i) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
@@ -453,9 +471,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   }
 
   @Override
-  public java.sql.Date getDate(int i, java.util.Calendar cal) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  public java.sql./* @Nullable */ Date getDate(
+      int i, java.util./* @Nullable */ Calendar cal) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
@@ -467,11 +486,11 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int oid = fields[col].getOID();
       TimeZone tz = cal.getTimeZone();
       if (oid == Oid.DATE) {
-        return connection.getTimestampUtils().toDateBin(tz, thisRow.get(col));
+        return connection.getTimestampUtils().toDateBin(tz, value);
       } else if (oid == Oid.TIMESTAMP || oid == Oid.TIMESTAMPTZ) {
         // If backend provides just TIMESTAMP, we use "cal" timezone
         // If backend provides TIMESTAMPTZ, we ignore "cal" as we know true instant value
-        Timestamp timestamp = getTimestamp(i, cal);
+        Timestamp timestamp = castNonNull(getTimestamp(i, cal));
         // Here we just truncate date to 00:00 in a given time zone
         return connection.getTimestampUtils().convertToDate(timestamp.getTime(), tz);
       } else {
@@ -482,13 +501,14 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       }
     }
 
-    return connection.getTimestampUtils().toDate(cal, getString(i));
+    return connection.getTimestampUtils().toDate(cal, castNonNull(getString(i)));
   }
 
   @Override
-  public Time getTime(int i, java.util.Calendar cal) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  public /* @Nullable */ Time getTime(
+      int i, java.util./* @Nullable */ Calendar cal) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
@@ -500,11 +520,14 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int oid = fields[col].getOID();
       TimeZone tz = cal.getTimeZone();
       if (oid == Oid.TIME || oid == Oid.TIMETZ) {
-        return connection.getTimestampUtils().toTimeBin(tz, thisRow.get(col));
+        return connection.getTimestampUtils().toTimeBin(tz, value);
       } else if (oid == Oid.TIMESTAMP || oid == Oid.TIMESTAMPTZ) {
         // If backend provides just TIMESTAMP, we use "cal" timezone
         // If backend provides TIMESTAMPTZ, we ignore "cal" as we know true instant value
         Timestamp timestamp = getTimestamp(i, cal);
+        if (timestamp == null) {
+          return null;
+        }
         long timeMillis = timestamp.getTime();
         if (oid == Oid.TIMESTAMPTZ) {
           // time zone == UTC since BINARY "timestamp with time zone" is always sent in UTC
@@ -526,9 +549,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   }
 
   //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
-  private java.time.LocalTime getLocalTime(int i) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  private java.time./* @Nullable */ LocalTime getLocalTime(int i) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
@@ -536,7 +559,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int col = i - 1;
       int oid = fields[col].getOID();
       if (oid == Oid.TIME) {
-        return connection.getTimestampUtils().toLocalTimeBin(thisRow.get(col));
+        return connection.getTimestampUtils().toLocalTimeBin(value);
       } else {
         throw new PSQLException(
             GT.tr("Cannot convert the column of type {0} to requested type {1}.",
@@ -550,10 +573,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   }
   //#endif
 
+  /* @Pure */
   @Override
-  public Timestamp getTimestamp(int i, java.util.Calendar cal) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  public /* @Nullable */ Timestamp getTimestamp(
+      int i, java.util./* @Nullable */ Calendar cal) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
@@ -566,14 +591,22 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       if (oid == Oid.TIMESTAMPTZ || oid == Oid.TIMESTAMP) {
         boolean hasTimeZone = oid == Oid.TIMESTAMPTZ;
         TimeZone tz = cal.getTimeZone();
-        return connection.getTimestampUtils().toTimestampBin(tz, thisRow.get(col), hasTimeZone);
+        return connection.getTimestampUtils().toTimestampBin(tz, value, hasTimeZone);
       } else {
         // JDBC spec says getTimestamp of Time and Date must be supported
         long millis;
         if (oid == Oid.TIME || oid == Oid.TIMETZ) {
-          millis = getTime(i, cal).getTime();
+          Time time = getTime(i, cal);
+          if (time == null) {
+            return null;
+          }
+          millis = time.getTime();
         } else if (oid == Oid.DATE) {
-          millis = getDate(i, cal).getTime();
+          Date date = getDate(i, cal);
+          if (date == null) {
+            return null;
+          }
+          millis = date.getTime();
         } else {
           throw new PSQLException(
               GT.tr("Cannot convert the column of type {0} to requested type {1}.",
@@ -587,7 +620,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     // 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);
+    String string = castNonNull(getString(i));
     if (oid == Oid.TIME || oid == Oid.TIMETZ) {
       // If server sends us a TIME, we ensure java counterpart has date of 1970-01-01
       return new Timestamp(connection.getTimestampUtils().toTime(cal, string).getTime());
@@ -596,9 +629,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   }
 
   //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
-  private java.time.OffsetDateTime getOffsetDateTime(int i) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  private java.time./* @Nullable */ OffsetDateTime getOffsetDateTime(int i) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
@@ -607,10 +640,13 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
     if (isBinary(i)) {
       if (oid == Oid.TIMESTAMPTZ || oid == Oid.TIMESTAMP) {
-        return connection.getTimestampUtils().toOffsetDateTimeBin(thisRow.get(col));
+        return connection.getTimestampUtils().toOffsetDateTimeBin(value);
       } else if (oid == Oid.TIMETZ) {
         // JDBC spec says timetz must be supported
         Time time = getTime(i);
+        if (time == null) {
+          return null;
+        }
         return connection.getTimestampUtils().toOffsetDateTime(time);
       } else {
         throw new PSQLException(
@@ -623,7 +659,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     // 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);
+    String string = castNonNull(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
@@ -634,9 +670,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return connection.getTimestampUtils().toOffsetDateTime(string);
   }
 
-  private java.time.LocalDateTime getLocalDateTime(int i) throws SQLException {
-    checkResultSet(i);
-    if (wasNullFlag) {
+  private java.time./* @Nullable */ LocalDateTime getLocalDateTime(int i) throws SQLException {
+    byte[] value = getRawValue(i);
+    if (value == null) {
       return null;
     }
 
@@ -649,23 +685,26 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
               PSQLState.DATA_TYPE_MISMATCH);
     }
     if (isBinary(i)) {
-      return connection.getTimestampUtils().toLocalDateTimeBin(thisRow.get(col));
+      return connection.getTimestampUtils().toLocalDateTimeBin(value);
     }
 
-    String string = getString(i);
+    String string = castNonNull(getString(i));
     return connection.getTimestampUtils().toLocalDateTime(string);
   }
   //#endif
 
-  public java.sql.Date getDate(String c, java.util.Calendar cal) throws SQLException {
+  public java.sql./* @Nullable */ Date getDate(
+      String c, java.util./* @Nullable */ Calendar cal) throws SQLException {
     return getDate(findColumn(c), cal);
   }
 
-  public Time getTime(String c, java.util.Calendar cal) throws SQLException {
+  public /* @Nullable */ Time getTime(
+      String c, java.util./* @Nullable */ Calendar cal) throws SQLException {
     return getTime(findColumn(c), cal);
   }
 
-  public Timestamp getTimestamp(String c, java.util.Calendar cal) throws SQLException {
+  public /* @Nullable */ Timestamp getTimestamp(
+      String c, java.util./* @Nullable */ Calendar cal) throws SQLException {
     return getTimestamp(findColumn(c), cal);
   }
 
@@ -674,7 +713,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return fetchdirection;
   }
 
-  public Object getObjectImpl(String columnName, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getObjectImpl(
+      String columnName, /* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     return getObjectImpl(findColumn(columnName), map);
   }
 
@@ -682,7 +722,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * This checks against map for the type of column i, and if found returns an object based on that
    * mapping. The class must implement the SQLData interface.
    */
-  public Object getObjectImpl(int i, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getObjectImpl(
+      int i, /* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     checkClosed();
     if (map == null || map.isEmpty()) {
       return getObject(i);
@@ -690,11 +731,11 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     throw org.postgresql.Driver.notImplemented(this.getClass(), "getObjectImpl(int,Map)");
   }
 
-  public Ref getRef(String columnName) throws SQLException {
+  public /* @Nullable */ Ref getRef(String columnName) throws SQLException {
     return getRef(findColumn(columnName));
   }
 
-  public Ref getRef(int i) throws SQLException {
+  public /* @Nullable */ Ref getRef(int i) throws SQLException {
     checkClosed();
     // The backend doesn't yet have SQL3 REF types
     throw org.postgresql.Driver.notImplemented(this.getClass(), "getRef(int)");
@@ -728,6 +769,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return resultsettype;
   }
 
+  /* @Pure */
   @Override
   public boolean isAfterLast() throws SQLException {
     checkClosed();
@@ -735,6 +777,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       return false;
     }
 
+    castNonNull(rows, "rows");
     final int rows_size = rows.size();
     if (rowOffset + rows_size == 0) {
       return false;
@@ -742,6 +785,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return (currentRow >= rows_size);
   }
 
+  /* @Pure */
   @Override
   public boolean isBeforeFirst() throws SQLException {
     checkClosed();
@@ -749,7 +793,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       return false;
     }
 
-    return ((rowOffset + currentRow) < 0 && !rows.isEmpty());
+    return ((rowOffset + currentRow) < 0 && !castNonNull(rows, "rows").isEmpty());
   }
 
   @Override
@@ -774,6 +818,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       return false;
     }
 
+    List<Tuple> rows = castNonNull(this.rows, "rows");
     final int rows_size = rows.size();
 
     if (rows_size == 0) {
@@ -786,6 +831,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
     // We are on the last row of the current block.
 
+    ResultCursor cursor = this.cursor;
     if (cursor == null) {
       // This is the last block and therefore the last row.
       return true;
@@ -819,8 +865,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     // Do the actual fetch.
     connection.getQueryExecutor().fetch(cursor, new CursorResultHandler(), fetchRows);
 
+    rows = castNonNull(this.rows, "rows");
     // Now prepend our one saved row and move to it.
-    rows.add(0, thisRow);
+    rows.add(0, castNonNull(thisRow));
     currentRow = 0;
 
     // Finally, now we can tell if we're the last row or not.
@@ -830,7 +877,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   @Override
   public boolean last() throws SQLException {
     checkScrollable();
-
+    List<Tuple> rows = castNonNull(this.rows, "rows");
     final int rows_size = rows.size();
     if (rows_size <= 0) {
       return false;
@@ -933,11 +980,13 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
               "Currently positioned after the end of the ResultSet.  You cannot call deleteRow() here."),
           PSQLState.INVALID_CURSOR_STATE);
     }
+    List<Tuple> rows = castNonNull(this.rows, "rows");
     if (rows.isEmpty()) {
       throw new PSQLException(GT.tr("There are no rows in this ResultSet."),
           PSQLState.INVALID_CURSOR_STATE);
     }
 
+    List<PrimaryKey> primaryKeys = castNonNull(this.primaryKeys, "primaryKeys");
     int numKeys = primaryKeys.size();
     if (deleteStatement == null) {
       StringBuilder deleteSQL =
@@ -969,38 +1018,44 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   @Override
   public synchronized void insertRow() throws SQLException {
     checkUpdateable();
-
+    castNonNull(rows, "rows");
     if (!onInsertRow) {
       throw new PSQLException(GT.tr("Not on the insert row."), PSQLState.INVALID_CURSOR_STATE);
-    } else if (updateValues.isEmpty()) {
+    }
+    HashMap<String, Object> updateValues = this.updateValues;
+    if (updateValues == null || updateValues.isEmpty()) {
       throw new PSQLException(GT.tr("You must specify at least one column value to insert a row."),
           PSQLState.INVALID_PARAMETER_VALUE);
-    } else {
-
-      // loop through the keys in the insertTable and create the sql statement
-      // we have to create the sql every time since the user could insert different
-      // columns each time
+    }
 
-      StringBuilder insertSQL = new StringBuilder("INSERT INTO ").append(tableName).append(" (");
-      StringBuilder paramSQL = new StringBuilder(") values (");
+    // loop through the keys in the insertTable and create the sql statement
+    // we have to create the sql every time since the user could insert different
+    // columns each time
 
-      Iterator<String> columnNames = updateValues.keySet().iterator();
-      int numColumns = updateValues.size();
+    StringBuilder insertSQL = new StringBuilder("INSERT INTO ").append(tableName).append(" (");
+    StringBuilder paramSQL = new StringBuilder(") values (");
 
-      for (int i = 0; columnNames.hasNext(); i++) {
-        String columnName = columnNames.next();
+    Iterator<String> columnNames = updateValues.keySet().iterator();
+    int numColumns = updateValues.size();
 
-        Utils.escapeIdentifier(insertSQL, columnName);
-        if (i < numColumns - 1) {
-          insertSQL.append(", ");
-          paramSQL.append("?,");
-        } else {
-          paramSQL.append("?)");
-        }
+    for (int i = 0; columnNames.hasNext(); i++) {
+      String columnName = columnNames.next();
 
+      Utils.escapeIdentifier(insertSQL, columnName);
+      if (i < numColumns - 1) {
+        insertSQL.append(", ");
+        paramSQL.append("?,");
+      } else {
+        paramSQL.append("?)");
       }
 
-      insertSQL.append(paramSQL.toString());
+    }
+
+    insertSQL.append(paramSQL.toString());
+    PreparedStatement insertStatement = null;
+
+    Tuple rowBuffer = castNonNull(this.rowBuffer);
+    try {
       insertStatement = connection.prepareStatement(insertSQL.toString(), Statement.RETURN_GENERATED_KEYS);
 
       Iterator<Object> values = updateValues.values().iterator();
@@ -1021,22 +1076,25 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       }
 
       // update the underlying row to the new inserted data
-      updateRowBuffer(true);
+      updateRowBuffer(insertStatement, rowBuffer, castNonNull(updateValues));
+    } finally {
+      JdbcBlackHole.close(insertStatement);
+    }
 
-      rows.add(rowBuffer);
+    castNonNull(rows).add(rowBuffer);
 
-      // we should now reflect the current data in thisRow
-      // that way getXXX will get the newly inserted data
-      thisRow = rowBuffer;
+    // we should now reflect the current data in thisRow
+    // that way getXXX will get the newly inserted data
+    thisRow = rowBuffer;
 
-      // need to clear this in case of another insert
-      clearRowBuffer(false);
-    }
+    // need to clear this in case of another insert
+    clearRowBuffer(false);
   }
 
   @Override
   public synchronized void moveToCurrentRow() throws SQLException {
     checkUpdateable();
+    castNonNull(rows, "rows");
 
     if (currentRow < 0 || currentRow >= rows.size()) {
       thisRow = null;
@@ -1053,10 +1111,6 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   public synchronized void moveToInsertRow() throws SQLException {
     checkUpdateable();
 
-    if (insertStatement != null) {
-      insertStatement = null;
-    }
-
     // make sure the underlying data is null
     clearRowBuffer(false);
 
@@ -1066,16 +1120,18 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
   // rowBuffer is the temporary storage for the row
   private synchronized void clearRowBuffer(boolean copyCurrentRow) throws SQLException {
-
     // inserts want an empty array while updates want a copy of the current row
     if (copyCurrentRow) {
-      rowBuffer = thisRow.updateableCopy();
+      rowBuffer = castNonNull(thisRow, "thisRow").updateableCopy();
     } else {
       rowBuffer = new Tuple(fields.length);
     }
 
     // clear the updateValues hash map for the next set of updates
-    updateValues.clear();
+    HashMap<String, Object> updateValues = this.updateValues;
+    if (updateValues != null) {
+      updateValues.clear();
+    }
   }
 
   public boolean rowDeleted() throws SQLException {
@@ -1093,7 +1149,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return false;
   }
 
-  public synchronized void updateAsciiStream(int columnIndex, java.io.InputStream x, int length)
+  public synchronized void updateAsciiStream(/* @Positive */ int columnIndex,
+      java.io./* @Nullable */ InputStream x, int length)
       throws SQLException {
     if (x == null) {
       updateNull(columnIndex);
@@ -1125,12 +1182,13 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  public synchronized void updateBigDecimal(int columnIndex, java.math.BigDecimal x)
+  public synchronized void updateBigDecimal(/* @Positive */ int columnIndex, java.math./* @Nullable */ BigDecimal x)
       throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateBinaryStream(int columnIndex, java.io.InputStream x, int length)
+  public synchronized void updateBinaryStream(/* @Positive */ int columnIndex,
+      java.io./* @Nullable */ InputStream x, int length)
       throws SQLException {
     if (x == null) {
       updateNull(columnIndex);
@@ -1167,19 +1225,20 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  public synchronized void updateBoolean(int columnIndex, boolean x) throws SQLException {
+  public synchronized void updateBoolean(/* @Positive */ int columnIndex, boolean x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateByte(int columnIndex, byte x) throws SQLException {
+  public synchronized void updateByte(/* @Positive */ int columnIndex, byte x) throws SQLException {
     updateValue(columnIndex, String.valueOf(x));
   }
 
-  public synchronized void updateBytes(int columnIndex, byte[] x) throws SQLException {
+  public synchronized void updateBytes(/* @Positive */ int columnIndex, byte /* @Nullable */ [] x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateCharacterStream(int columnIndex, java.io.Reader x, int length)
+  public synchronized void updateCharacterStream(/* @Positive */ int columnIndex,
+      java.io./* @Nullable */ Reader x, int length)
       throws SQLException {
     if (x == null) {
       updateNull(columnIndex);
@@ -1207,37 +1266,40 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  public synchronized void updateDate(int columnIndex, java.sql.Date x) throws SQLException {
+  public synchronized void updateDate(/* @Positive */ int columnIndex,
+      java.sql./* @Nullable */ Date x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateDouble(int columnIndex, double x) throws SQLException {
+  public synchronized void updateDouble(/* @Positive */ int columnIndex, double x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateFloat(int columnIndex, float x) throws SQLException {
+  public synchronized void updateFloat(/* @Positive */ int columnIndex, float x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateInt(int columnIndex, int x) throws SQLException {
+  public synchronized void updateInt(/* @Positive */ int columnIndex, int x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateLong(int columnIndex, long x) throws SQLException {
+  public synchronized void updateLong(/* @Positive */ int columnIndex, long x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateNull(int columnIndex) throws SQLException {
+  public synchronized void updateNull(/* @Positive */ int columnIndex) throws SQLException {
     checkColumnIndex(columnIndex);
     String columnTypeName = getPGType(columnIndex);
     updateValue(columnIndex, new NullObject(columnTypeName));
   }
 
-  public synchronized void updateObject(int columnIndex, Object x) throws SQLException {
+  public synchronized void updateObject(
+      int columnIndex, /* @Nullable */ Object x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateObject(int columnIndex, Object x, int scale) throws SQLException {
+  public synchronized void updateObject(
+      int columnIndex, /* @Nullable */ Object x, int scale) throws SQLException {
     this.updateObject(columnIndex, x);
   }
 
@@ -1249,7 +1311,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
           PSQLState.INVALID_CURSOR_STATE);
     }
 
-    if (isBeforeFirst() || isAfterLast() || rows.isEmpty()) {
+    if (isBeforeFirst() || isAfterLast() || castNonNull(rows, "rows").isEmpty()) {
       return;
     }
 
@@ -1265,6 +1327,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
     selectSQL.append(" from ").append(onlyTable).append(tableName).append(" where ");
 
+    List<PrimaryKey> primaryKeys = castNonNull(this.primaryKeys, "primaryKeys");
     int numKeys = primaryKeys.size();
 
     for (int i = 0; i < numKeys; i++) {
@@ -1282,27 +1345,30 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
     // because updateable result sets do not yet support binary transfers we must request refresh
     // with updateable result set to get field data in correct format
-    selectStatement = connection.prepareStatement(sqlText,
-        ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
+    PreparedStatement selectStatement = null;
+    try {
+      selectStatement = connection.prepareStatement(sqlText,
+          ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_UPDATABLE);
 
-    for (int j = 0, i = 1; j < numKeys; j++, i++) {
-      selectStatement.setObject(i, primaryKeys.get(j).getValue());
-    }
+      for (int j = 0, i = 1; j < numKeys; j++, i++) {
+        selectStatement.setObject(i, primaryKeys.get(j).getValue());
+      }
 
-    PgResultSet rs = (PgResultSet) selectStatement.executeQuery();
+      PgResultSet rs = (PgResultSet) selectStatement.executeQuery();
 
-    if (rs.next()) {
-      rowBuffer = rs.thisRow;
-    }
+      if (rs.next()) {
+        rowBuffer = rs.thisRow;
+      }
 
-    rows.set(currentRow, rowBuffer);
-    thisRow = rowBuffer;
+      castNonNull(rows).set(currentRow, castNonNull(rowBuffer));
+      thisRow = rowBuffer;
 
-    connection.getLogger().log(Level.FINE, "done updates");
+      connection.getLogger().log(Level.FINE, "done updates");
 
-    rs.close();
-    selectStatement.close();
-    selectStatement = null;
+      rs.close();
+    } finally {
+      JdbcBlackHole.close(selectStatement);
+    }
   }
 
   @Override
@@ -1314,6 +1380,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
           PSQLState.INVALID_CURSOR_STATE);
     }
 
+    List<Tuple> rows = castNonNull(this.rows, "rows");
     if (isBeforeFirst() || isAfterLast() || rows.isEmpty()) {
       throw new PSQLException(
           GT.tr(
@@ -1327,6 +1394,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
     StringBuilder updateSQL = new StringBuilder("UPDATE " + onlyTable + tableName + " SET  ");
 
+    HashMap<String, Object> updateValues = castNonNull(this.updateValues);
     int numColumns = updateValues.size();
     Iterator<String> columns = updateValues.keySet().iterator();
 
@@ -1342,6 +1410,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
     updateSQL.append(" WHERE ");
 
+    List<PrimaryKey> primaryKeys = castNonNull(this.primaryKeys, "primaryKeys");
     int numKeys = primaryKeys.size();
 
     for (int i = 0; i < numKeys; i++) {
@@ -1358,24 +1427,28 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     if (connection.getLogger().isLoggable(Level.FINE)) {
       connection.getLogger().log(Level.FINE, "updating {0}", sqlText);
     }
-    updateStatement = connection.prepareStatement(sqlText);
+    PreparedStatement updateStatement = null;
+    try {
+      updateStatement = connection.prepareStatement(sqlText);
 
-    int i = 0;
-    Iterator<Object> iterator = updateValues.values().iterator();
-    for (; iterator.hasNext(); i++) {
-      Object o = iterator.next();
-      updateStatement.setObject(i + 1, o);
-    }
+      int i = 0;
+      Iterator<Object> iterator = updateValues.values().iterator();
+      for (; iterator.hasNext(); i++) {
+        Object o = iterator.next();
+        updateStatement.setObject(i + 1, o);
+      }
 
-    for (int j = 0; j < numKeys; j++, i++) {
-      updateStatement.setObject(i + 1, primaryKeys.get(j).getValue());
-    }
+      for (int j = 0; j < numKeys; j++, i++) {
+        updateStatement.setObject(i + 1, primaryKeys.get(j).getValue());
+      }
 
-    updateStatement.executeUpdate();
-    updateStatement.close();
-    updateStatement = null;
+      updateStatement.executeUpdate();
+    } finally {
+      JdbcBlackHole.close(updateStatement);
+    }
 
-    updateRowBuffer(false);
+    Tuple rowBuffer = castNonNull(this.rowBuffer, "rowBuffer");
+    updateRowBuffer(null, rowBuffer, updateValues);
 
     connection.getLogger().log(Level.FINE, "copying data");
     thisRow = rowBuffer.readOnlyCopy();
@@ -1386,19 +1459,20 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     doingUpdates = false;
   }
 
-  public synchronized void updateShort(int columnIndex, short x) throws SQLException {
+  public synchronized void updateShort(/* @Positive */ int columnIndex, short x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateString(int columnIndex, String x) throws SQLException {
+  public synchronized void updateString(/* @Positive */ int columnIndex, /* @Nullable */ String x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateTime(int columnIndex, Time x) throws SQLException {
+  public synchronized void updateTime(/* @Positive */ int columnIndex, /* @Nullable */ Time x) throws SQLException {
     updateValue(columnIndex, x);
   }
 
-  public synchronized void updateTimestamp(int columnIndex, Timestamp x) throws SQLException {
+  public synchronized void updateTimestamp(
+      int columnIndex, /* @Nullable */ Timestamp x) throws SQLException {
     updateValue(columnIndex, x);
 
   }
@@ -1435,52 +1509,63 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     updateDouble(findColumn(columnName), x);
   }
 
-  public synchronized void updateBigDecimal(String columnName, BigDecimal x) throws SQLException {
+  public synchronized void updateBigDecimal(
+      String columnName, /* @Nullable */ BigDecimal x) throws SQLException {
     updateBigDecimal(findColumn(columnName), x);
   }
 
-  public synchronized void updateString(String columnName, String x) throws SQLException {
+  public synchronized void updateString(
+      String columnName, /* @Nullable */ String x) throws SQLException {
     updateString(findColumn(columnName), x);
   }
 
-  public synchronized void updateBytes(String columnName, byte[] x) throws SQLException {
+  public synchronized void updateBytes(
+      String columnName, byte /* @Nullable */ [] x) throws SQLException {
     updateBytes(findColumn(columnName), x);
   }
 
-  public synchronized void updateDate(String columnName, java.sql.Date x) throws SQLException {
+  public synchronized void updateDate(
+      String columnName, java.sql./* @Nullable */ Date x) throws SQLException {
     updateDate(findColumn(columnName), x);
   }
 
-  public synchronized void updateTime(String columnName, java.sql.Time x) throws SQLException {
+  public synchronized void updateTime(
+      String columnName, java.sql./* @Nullable */ Time x) throws SQLException {
     updateTime(findColumn(columnName), x);
   }
 
-  public synchronized void updateTimestamp(String columnName, java.sql.Timestamp x)
+  public synchronized void updateTimestamp(
+      String columnName, java.sql./* @Nullable */ Timestamp x)
       throws SQLException {
     updateTimestamp(findColumn(columnName), x);
   }
 
-  public synchronized void updateAsciiStream(String columnName, java.io.InputStream x, int length)
+  public synchronized void updateAsciiStream(
+      String columnName, java.io./* @Nullable */ InputStream x, int length)
       throws SQLException {
     updateAsciiStream(findColumn(columnName), x, length);
   }
 
-  public synchronized void updateBinaryStream(String columnName, java.io.InputStream x, int length)
+  public synchronized void updateBinaryStream(
+      String columnName, java.io./* @Nullable */ InputStream x, int length)
       throws SQLException {
     updateBinaryStream(findColumn(columnName), x, length);
   }
 
-  public synchronized void updateCharacterStream(String columnName, java.io.Reader reader,
+  public synchronized void updateCharacterStream(
+      String columnName, java.io./* @Nullable */ Reader reader,
       int length) throws SQLException {
     updateCharacterStream(findColumn(columnName), reader, length);
   }
 
-  public synchronized void updateObject(String columnName, Object x, int scale)
+  public synchronized void updateObject(
+      String columnName, /* @Nullable */ Object x, int scale)
       throws SQLException {
     updateObject(findColumn(columnName), x);
   }
 
-  public synchronized void updateObject(String columnName, Object x) throws SQLException {
+  public synchronized void updateObject(
+      String columnName, /* @Nullable */ Object x) throws SQLException {
     updateObject(findColumn(columnName), x);
   }
 
@@ -1505,70 +1590,80 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
     parseQuery();
 
+    if (tableName == null) {
+      connection.getLogger().log(Level.FINE, "tableName is not found");
+      return false;
+    }
+
     if (!singleTable) {
       connection.getLogger().log(Level.FINE, "not a single table");
       return false;
     }
 
+    usingOID = false;
+
     connection.getLogger().log(Level.FINE, "getting primary keys");
 
     //
     // Contains the primary key?
     //
 
-    primaryKeys = new ArrayList<PrimaryKey>();
-
-    // this is not strictly jdbc spec, but it will make things much faster if used
-    // the user has to select oid, * from table and then we will just use oid
-    // with oids has been removed in version 12
-    // FIXME: with oids does not automatically create an index, should check for primary keys first
-
-    usingOID = false;
-    int oidIndex = findColumnIndex("oid"); // 0 if not present
+    List<PrimaryKey> primaryKeys = new ArrayList<PrimaryKey>();
+    this.primaryKeys = primaryKeys;
 
     int i = 0;
     int numPKcolumns = 0;
 
-    // if we find the oid then just use it
+    // otherwise go and get the primary keys and create a list of keys
+    /* @Nullable */ String[] s = quotelessTableName(castNonNull(tableName));
+    String quotelessTableName = castNonNull(s[0]);
+    /* @Nullable */ String quotelessSchemaName = s[1];
+    java.sql.ResultSet rs = connection.getMetaData().getPrimaryKeys("",
+        quotelessSchemaName, quotelessTableName);
 
-    // oidIndex will be >0 if the oid was in the select list
-    if (oidIndex > 0) {
-      i++;
+    while (rs.next()) {
       numPKcolumns++;
-      primaryKeys.add(new PrimaryKey(oidIndex, "oid"));
-      usingOID = true;
-    } else {
-      // otherwise go and get the primary keys and create a list of keys
-      String[] s = quotelessTableName(tableName);
-      String quotelessTableName = s[0];
-      String quotelessSchemaName = s[1];
-      java.sql.ResultSet rs = connection.getMetaData().getPrimaryKeys("",
-          quotelessSchemaName, quotelessTableName);
-      while (rs.next()) {
-        numPKcolumns++;
-        String columnName = rs.getString(4); // get the columnName
-        int index = findColumnIndex(columnName);
-
-        if (index > 0) {
-          i++;
-          primaryKeys.add(new PrimaryKey(index, columnName)); // get the primary key information
-        }
-      }
+      String columnName = castNonNull(rs.getString(4)); // get the columnName
+      int index = findColumnIndex(columnName);
 
-      rs.close();
+      /* make sure that the user has included the primary key in the resultset */
+      if (index > 0) {
+        i++;
+        primaryKeys.add(new PrimaryKey(index, columnName)); // get the primary key information
+      }
     }
 
+    rs.close();
     connection.getLogger().log(Level.FINE, "no of keys={0}", i);
 
-    if (i < 1) {
-      throw new PSQLException(GT.tr("No primary key found for table {0}.", tableName),
-          PSQLState.DATA_ERROR);
-    }
-
+    /*
+    it is only updatable if the primary keys are available in the resultset
+     */
     updateable = (i == numPKcolumns);
 
     connection.getLogger().log(Level.FINE, "checking primary key {0}", updateable);
 
+    /*
+      if we haven't found a primary key we can check to see if the query includes the oid
+      This is now a questionable check as oid's have been deprecated. Might still be useful for
+      catalog tables, but again the query would have to include the oid.
+     */
+    if (!updateable) {
+      int oidIndex = findColumnIndex("oid"); // 0 if not present
+
+      // oidIndex will be >0 if the oid was in the select list
+      if (oidIndex > 0) {
+        primaryKeys.add(new PrimaryKey(oidIndex, "oid"));
+        usingOID = true;
+        updateable = true;
+      }
+    }
+
+    if (!updateable) {
+      throw new PSQLException(GT.tr("No primary key found for table {0}.", tableName),
+          PSQLState.INVALID_CURSOR_STATE);
+    }
+
     return updateable;
   }
 
@@ -1602,9 +1697,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * @return String array with element zero always being the tablename and element 1 the schema name
    *         which may be a zero length string.
    */
-  public static String[] quotelessTableName(String fullname) {
+  public static /* @Nullable */ String[] quotelessTableName(String fullname) {
 
-    String[] parts = new String[]{null, ""};
+    /* @Nullable */ String[] parts = new String[]{null, ""};
     StringBuilder acc = new StringBuilder();
     boolean betweenQuotes = false;
     for (int i = 0; i < fullname.length(); i++) {
@@ -1638,6 +1733,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   }
 
   private void parseQuery() {
+    Query originalQuery = this.originalQuery;
+    if (originalQuery == null) {
+      return;
+    }
     String sql = originalQuery.toString(null);
     StringTokenizer st = new StringTokenizer(sql, " \r\t\n");
     boolean tableFound = false;
@@ -1659,18 +1758,23 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  private void setRowBufferColumn(int columnIndex, Object valueObject) throws SQLException {
+  private void setRowBufferColumn(Tuple rowBuffer,
+      int columnIndex, /* @Nullable */ Object valueObject) throws SQLException {
     if (valueObject instanceof PGobject) {
       String value = ((PGobject) valueObject).getValue();
       rowBuffer.set(columnIndex, (value == null) ? null : connection.encodeString(value));
     } else {
+      if (valueObject == null) {
+        rowBuffer.set(columnIndex, null);
+        return;
+      }
       switch (getSQLType(columnIndex + 1)) {
 
         // boolean needs to be formatted as t or f instead of true or false
         case Types.BIT:
         case Types.BOOLEAN:
           rowBuffer.set(columnIndex, connection
-              .encodeString(((Boolean) valueObject).booleanValue() ? "t" : "f"));
+              .encodeString((Boolean) valueObject ? "t" : "f"));
           break;
         //
         // toString() isn't enough for date and time types; we must format it correctly
@@ -1708,10 +1812,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
           } else {
             try {
               rowBuffer.set(columnIndex,
-                  PGbytea.toPGString((byte[]) valueObject).getBytes("ISO-8859-1"));
+                  PGbytea.toPGString((byte[]) valueObject).getBytes(connection.getEncoding().name()));
             } catch (UnsupportedEncodingException e) {
               throw new PSQLException(
-                  GT.tr("The JVM claims not to support the encoding: {0}", "ISO-8859-1"),
+                  GT.tr("The JVM claims not to support the encoding: {0}", connection.getEncoding().name()),
                   PSQLState.UNEXPECTED_ERROR, e);
             }
           }
@@ -1725,30 +1829,32 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  private void updateRowBuffer(boolean isInsert) throws SQLException {
-
+  private void updateRowBuffer(/* @Nullable */ PreparedStatement insertStatement,
+      Tuple rowBuffer, HashMap<String, Object> updateValues) throws SQLException {
     for (Map.Entry<String, Object> entry : updateValues.entrySet()) {
       int columnIndex = findColumn(entry.getKey()) - 1;
       Object valueObject = entry.getValue();
-      setRowBufferColumn(columnIndex, valueObject);
+      setRowBufferColumn(rowBuffer, columnIndex, valueObject);
     }
 
-    if (isInsert) {
-      final ResultSet generatedKeys = insertStatement.getGeneratedKeys();
-      try {
-        generatedKeys.next();
+    if (insertStatement == null) {
+      return;
+    }
+    final ResultSet generatedKeys = insertStatement.getGeneratedKeys();
+    try {
+      generatedKeys.next();
 
-        int numKeys = primaryKeys.size();
+      List<PrimaryKey> primaryKeys = castNonNull(this.primaryKeys);
+      int numKeys = primaryKeys.size();
 
-        for (int i = 0; i < numKeys; i++) {
-          final PrimaryKey key = primaryKeys.get(i);
-          int columnIndex = key.index - 1;
-          Object valueObject = generatedKeys.getObject(key.name);
-          setRowBufferColumn(columnIndex, valueObject);
-        }
-      } finally {
-        generatedKeys.close();
+      for (int i = 0; i < numKeys; i++) {
+        final PrimaryKey key = primaryKeys.get(i);
+        int columnIndex = key.index - 1;
+        Object valueObject = generatedKeys.getObject(key.name);
+        setRowBufferColumn(rowBuffer, columnIndex, valueObject);
       }
+    } finally {
+      generatedKeys.close();
     }
   }
 
@@ -1756,7 +1862,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
     @Override
     public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
-        ResultCursor cursor) {
+        /* @Nullable */ ResultCursor cursor) {
       PgResultSet.this.rows = tuples;
       PgResultSet.this.cursor = cursor;
     }
@@ -1785,9 +1891,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   // Backwards compatibility with PGRefCursorResultSet
   //
 
-  private String refCursorName;
+  private /* @Nullable */ String refCursorName;
 
-  public String getRefCursor() {
+  public /* @Nullable */ String getRefCursor() {
     // Can't check this because the PGRefCursorResultSet
     // interface doesn't allow throwing a SQLException
     //
@@ -1816,6 +1922,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   @Override
   public boolean next() throws SQLException {
     checkClosed();
+    castNonNull(rows, "rows");
 
     if (onInsertRow) {
       throw new PSQLException(GT.tr("Can''t use relative move methods while on the insert row."),
@@ -1823,6 +1930,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
 
     if (currentRow + 1 >= rows.size()) {
+      ResultCursor cursor = this.cursor;
       if (cursor == null || (maxRows > 0 && rowOffset + rows.size() >= maxRows)) {
         currentRow = rows.size();
         thisRow = null;
@@ -1847,7 +1955,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       currentRow = 0;
 
       // Test the new rows array.
-      if (rows.isEmpty()) {
+      if (rows == null || rows.isEmpty()) {
         thisRow = null;
         rowBuffer = null;
         return false;
@@ -1877,6 +1985,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   protected void closeInternally() throws SQLException {
     // release resources held (memory for tuples)
     rows = null;
+    JdbcBlackHole.close(deleteStatement);
+    deleteStatement = null;
     if (cursor != null) {
       cursor.close();
       cursor = null;
@@ -1888,11 +1998,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return wasNullFlag;
   }
 
+  /* @Pure */
   @Override
-  public String getString(int columnIndex) throws SQLException {
+  public /* @Nullable */ String getString(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getString columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return null;
     }
 
@@ -1923,7 +2034,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
     Encoding encoding = connection.getEncoding();
     try {
-      return trimString(columnIndex, encoding.decode(thisRow.get(columnIndex - 1)));
+      return trimString(columnIndex, encoding.decode(value));
     } catch (IOException ioe) {
       throw new PSQLException(
           GT.tr(
@@ -1954,35 +2065,37 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * @see <a href="https://www.postgresql.org/docs/current/static/datatype-boolean.html">PostgreSQL
    * Boolean Type</a>
    */
+  /* @Pure */
   @Override
-  public boolean getBoolean(int columnIndex) throws SQLException {
+  public boolean getBoolean(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getBoolean columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
-      return false; // SQL NULL
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
+      return false;
     }
 
     int col = columnIndex - 1;
     if (Oid.BOOL == fields[col].getOID()) {
-      final byte[] v = thisRow.get(col);
+      final byte[] v = value;
       return (1 == v.length) && (116 == v[0]); // 116 = 't'
     }
 
     if (isBinary(columnIndex)) {
-      return BooleanTypeUtil.castToBoolean(readDoubleValue(thisRow.get(col), fields[col].getOID(), "boolean"));
+      return BooleanTypeUtil.castToBoolean(readDoubleValue(value, fields[col].getOID(), "boolean"));
     }
 
-    return BooleanTypeUtil.castToBoolean(getString(columnIndex));
+    String stringValue = castNonNull(getString(columnIndex));
+    return BooleanTypeUtil.castToBoolean(stringValue);
   }
 
   private static final BigInteger BYTEMAX = new BigInteger(Byte.toString(Byte.MAX_VALUE));
   private static final BigInteger BYTEMIN = new BigInteger(Byte.toString(Byte.MIN_VALUE));
 
   @Override
-  public byte getByte(int columnIndex) throws SQLException {
+  public byte getByte(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getByte columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return 0; // SQL NULL
     }
 
@@ -1990,7 +2103,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int col = columnIndex - 1;
       // there is no Oid for byte so must always do conversion from
       // some other numeric type
-      return (byte) readLongValue(thisRow.get(col), fields[col].getOID(), Byte.MIN_VALUE,
+      return (byte) readLongValue(value, fields[col].getOID(), Byte.MIN_VALUE,
           Byte.MAX_VALUE, "byte");
     }
 
@@ -2028,10 +2141,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   }
 
   @Override
-  public short getShort(int columnIndex) throws SQLException {
+  public short getShort(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getShort columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return 0; // SQL NULL
     }
 
@@ -2039,19 +2152,20 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int col = columnIndex - 1;
       int oid = fields[col].getOID();
       if (oid == Oid.INT2) {
-        return ByteConverter.int2(thisRow.get(col), 0);
+        return ByteConverter.int2(value, 0);
       }
-      return (short) readLongValue(thisRow.get(col), oid, Short.MIN_VALUE, Short.MAX_VALUE, "short");
+      return (short) readLongValue(value, oid, Short.MIN_VALUE, Short.MAX_VALUE, "short");
     }
 
     return toShort(getFixedString(columnIndex));
   }
 
+  /* @Pure */
   @Override
-  public int getInt(int columnIndex) throws SQLException {
+  public int getInt(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getInt columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return 0; // SQL NULL
     }
 
@@ -2059,26 +2173,27 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int col = columnIndex - 1;
       int oid = fields[col].getOID();
       if (oid == Oid.INT4) {
-        return ByteConverter.int4(thisRow.get(col), 0);
+        return ByteConverter.int4(value, 0);
       }
-      return (int) readLongValue(thisRow.get(col), oid, Integer.MIN_VALUE, Integer.MAX_VALUE, "int");
+      return (int) readLongValue(value, oid, Integer.MIN_VALUE, Integer.MAX_VALUE, "int");
     }
 
     Encoding encoding = connection.getEncoding();
     if (encoding.hasAsciiNumbers()) {
       try {
-        return getFastInt(columnIndex);
-      } catch (NumberFormatException ex) {
+        return getFastInt(value);
+      } catch (NumberFormatException ignored) {
       }
     }
     return toInt(getFixedString(columnIndex));
   }
 
+  /* @Pure */
   @Override
-  public long getLong(int columnIndex) throws SQLException {
+  public long getLong(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getLong columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return 0; // SQL NULL
     }
 
@@ -2086,16 +2201,16 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int col = columnIndex - 1;
       int oid = fields[col].getOID();
       if (oid == Oid.INT8) {
-        return ByteConverter.int8(thisRow.get(col), 0);
+        return ByteConverter.int8(value, 0);
       }
-      return readLongValue(thisRow.get(col), oid, Long.MIN_VALUE, Long.MAX_VALUE, "long");
+      return readLongValue(value, oid, Long.MIN_VALUE, Long.MAX_VALUE, "long");
     }
 
     Encoding encoding = connection.getEncoding();
     if (encoding.hasAsciiNumbers()) {
       try {
-        return getFastLong(columnIndex);
-      } catch (NumberFormatException ex) {
+        return getFastLong(value);
+      } catch (NumberFormatException ignored) {
       }
     }
     return toLong(getFixedString(columnIndex));
@@ -2123,16 +2238,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * Optimised byte[] to number parser. This code does not handle null values, so the caller must do
    * checkResultSet and handle null values prior to calling this function.
    *
-   * @param columnIndex The column to parse.
+   * @param bytes integer represented as a sequence of ASCII bytes
    * @return The parsed number.
-   * @throws SQLException If an error occurs while fetching column.
    * @throws NumberFormatException If the number is invalid or the out of range for fast parsing.
    *         The value must then be parsed by {@link #toLong(String)}.
    */
-  private long getFastLong(int columnIndex) throws SQLException, NumberFormatException {
-
-    byte[] bytes = thisRow.get(columnIndex - 1);
-
+  private long getFastLong(byte[] bytes) throws NumberFormatException {
     if (bytes.length == 0) {
       throw FAST_NUMBER_FAILED;
     }
@@ -2175,16 +2286,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * Optimised byte[] to number parser. This code does not handle null values, so the caller must do
    * checkResultSet and handle null values prior to calling this function.
    *
-   * @param columnIndex The column to parse.
+   * @param bytes integer represented as a sequence of ASCII bytes
    * @return The parsed number.
-   * @throws SQLException If an error occurs while fetching column.
    * @throws NumberFormatException If the number is invalid or the out of range for fast parsing.
    *         The value must then be parsed by {@link #toInt(String)}.
    */
-  private int getFastInt(int columnIndex) throws SQLException, NumberFormatException {
-
-    byte[] bytes = thisRow.get(columnIndex - 1);
-
+  private int getFastInt(byte[] bytes) throws NumberFormatException {
     if (bytes.length == 0) {
       throw FAST_NUMBER_FAILED;
     }
@@ -2227,16 +2334,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * Optimised byte[] to number parser. This code does not handle null values, so the caller must do
    * checkResultSet and handle null values prior to calling this function.
    *
-   * @param columnIndex The column to parse.
+   * @param bytes integer represented as a sequence of ASCII bytes
    * @return The parsed number.
-   * @throws SQLException If an error occurs while fetching column.
    * @throws NumberFormatException If the number is invalid or the out of range for fast parsing.
    *         The value must then be parsed by {@link #toBigDecimal(String, int)}.
    */
-  private BigDecimal getFastBigDecimal(int columnIndex) throws SQLException, NumberFormatException {
-
-    byte[] bytes = thisRow.get(columnIndex - 1);
-
+  private BigDecimal getFastBigDecimal(byte[] bytes) throws NumberFormatException {
     if (bytes.length == 0) {
       throw FAST_NUMBER_FAILED;
     }
@@ -2287,11 +2390,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return BigDecimal.valueOf(val, scale);
   }
 
+  /* @Pure */
   @Override
-  public float getFloat(int columnIndex) throws SQLException {
+  public float getFloat(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getFloat columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return 0; // SQL NULL
     }
 
@@ -2299,19 +2403,20 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int col = columnIndex - 1;
       int oid = fields[col].getOID();
       if (oid == Oid.FLOAT4) {
-        return ByteConverter.float4(thisRow.get(col), 0);
+        return ByteConverter.float4(value, 0);
       }
-      return (float) readDoubleValue(thisRow.get(col), oid, "float");
+      return (float) readDoubleValue(value, oid, "float");
     }
 
     return toFloat(getFixedString(columnIndex));
   }
 
+  /* @Pure */
   @Override
-  public double getDouble(int columnIndex) throws SQLException {
+  public double getDouble(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getDouble columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return 0; // SQL NULL
     }
 
@@ -2319,22 +2424,25 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       int col = columnIndex - 1;
       int oid = fields[col].getOID();
       if (oid == Oid.FLOAT8) {
-        return ByteConverter.float8(thisRow.get(col), 0);
+        return ByteConverter.float8(value, 0);
       }
-      return readDoubleValue(thisRow.get(col), oid, "double");
+      return readDoubleValue(value, oid, "double");
     }
 
     return toDouble(getFixedString(columnIndex));
   }
 
-  public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException {
+  public /* @Nullable */ BigDecimal getBigDecimal(
+      int columnIndex, int scale) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getBigDecimal columnIndex: {0}", columnIndex);
     return (BigDecimal) getNumeric(columnIndex, scale, false);
   }
 
-  private Number getNumeric(int columnIndex, int scale, boolean allowNaN) throws SQLException {
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+  /* @Pure */
+  private /* @Nullable */ Number getNumeric(
+      int columnIndex, int scale, boolean allowNaN) throws SQLException {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return null;
     }
 
@@ -2352,7 +2460,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
         }
         return toBigDecimal(trimMoney(String.valueOf(obj)), scale);
       } else {
-        Number num = ByteConverter.numeric(thisRow.get(columnIndex - 1));
+        Number num = ByteConverter.numeric(value);
         if (allowNaN && Double.isNaN(num.doubleValue())) {
           return Double.NaN;
         }
@@ -2364,7 +2472,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     Encoding encoding = connection.getEncoding();
     if (encoding.hasAsciiNumbers()) {
       try {
-        BigDecimal res = getFastBigDecimal(columnIndex);
+        BigDecimal res = getFastBigDecimal(value);
         res = scaleBigDecimal(res, scale);
         return res;
       } catch (NumberFormatException ignore) {
@@ -2387,44 +2495,49 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    *
    * <p><b>Be warned</b> If the large object is huge, then you may run out of memory.</p>
    */
+  /* @Pure */
   @Override
-  public byte[] getBytes(int columnIndex) throws SQLException {
+  public byte /* @Nullable */ [] getBytes(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getBytes columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return null;
     }
 
     if (isBinary(columnIndex)) {
       // If the data is already binary then just return it
-      return thisRow.get(columnIndex - 1);
+      return value;
     }
     if (fields[columnIndex - 1].getOID() == Oid.BYTEA) {
-      return trimBytes(columnIndex, PGbytea.toBytes(thisRow.get(columnIndex - 1)));
+      return trimBytes(columnIndex, PGbytea.toBytes(value));
     } else {
-      return trimBytes(columnIndex, thisRow.get(columnIndex - 1));
+      return trimBytes(columnIndex, value);
     }
   }
 
-  public java.sql.Date getDate(int columnIndex) throws SQLException {
+  /* @Pure */
+  public java.sql./* @Nullable */ Date getDate(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getDate columnIndex: {0}", columnIndex);
     return getDate(columnIndex, null);
   }
 
-  public Time getTime(int columnIndex) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ Time getTime(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getTime columnIndex: {0}", columnIndex);
     return getTime(columnIndex, null);
   }
 
-  public Timestamp getTimestamp(int columnIndex) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ Timestamp getTimestamp(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getTimestamp columnIndex: {0}", columnIndex);
     return getTimestamp(columnIndex, null);
   }
 
-  public InputStream getAsciiStream(int columnIndex) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ InputStream getAsciiStream(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getAsciiStream columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return null;
     }
 
@@ -2435,17 +2548,19 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     // handling very large values. Thus the implementation ends up calling
     // getString() since there is no current way to stream the value from the server
     try {
-      return new ByteArrayInputStream(getString(columnIndex).getBytes("ASCII"));
+      String stringValue = castNonNull(getString(columnIndex));
+      return new ByteArrayInputStream(stringValue.getBytes("ASCII"));
     } catch (UnsupportedEncodingException l_uee) {
       throw new PSQLException(GT.tr("The JVM claims not to support the encoding: {0}", "ASCII"),
           PSQLState.UNEXPECTED_ERROR, l_uee);
     }
   }
 
-  public InputStream getUnicodeStream(int columnIndex) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ InputStream getUnicodeStream(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getUnicodeStream columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return null;
     }
 
@@ -2456,17 +2571,19 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     // handling very large values. Thus the implementation ends up calling
     // getString() since there is no current way to stream the value from the server
     try {
-      return new ByteArrayInputStream(getString(columnIndex).getBytes("UTF-8"));
+      String stringValue = castNonNull(getString(columnIndex));
+      return new ByteArrayInputStream(stringValue.getBytes("UTF-8"));
     } catch (UnsupportedEncodingException l_uee) {
       throw new PSQLException(GT.tr("The JVM claims not to support the encoding: {0}", "UTF-8"),
           PSQLState.UNEXPECTED_ERROR, l_uee);
     }
   }
 
-  public InputStream getBinaryStream(int columnIndex) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ InputStream getBinaryStream(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getBinaryStream columnIndex: {0}", columnIndex);
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return null;
     }
 
@@ -2483,73 +2600,89 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return null;
   }
 
-  public String getString(String columnName) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ String getString(String columnName) throws SQLException {
     return getString(findColumn(columnName));
   }
 
+  /* @Pure */
   @Override
   public boolean getBoolean(String columnName) throws SQLException {
     return getBoolean(findColumn(columnName));
   }
 
+  /* @Pure */
   public byte getByte(String columnName) throws SQLException {
-
     return getByte(findColumn(columnName));
   }
 
+  /* @Pure */
   public short getShort(String columnName) throws SQLException {
     return getShort(findColumn(columnName));
   }
 
+  /* @Pure */
   public int getInt(String columnName) throws SQLException {
     return getInt(findColumn(columnName));
   }
 
+  /* @Pure */
   public long getLong(String columnName) throws SQLException {
     return getLong(findColumn(columnName));
   }
 
+  /* @Pure */
   public float getFloat(String columnName) throws SQLException {
     return getFloat(findColumn(columnName));
   }
 
+  /* @Pure */
   public double getDouble(String columnName) throws SQLException {
     return getDouble(findColumn(columnName));
   }
 
-  public BigDecimal getBigDecimal(String columnName, int scale) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ BigDecimal getBigDecimal(String columnName, int scale) throws SQLException {
     return getBigDecimal(findColumn(columnName), scale);
   }
 
-  public byte[] getBytes(String columnName) throws SQLException {
+  /* @Pure */
+  public byte /* @Nullable */ [] getBytes(String columnName) throws SQLException {
     return getBytes(findColumn(columnName));
   }
 
-  public java.sql.Date getDate(String columnName) throws SQLException {
+  /* @Pure */
+  public java.sql./* @Nullable */ Date getDate(String columnName) throws SQLException {
     return getDate(findColumn(columnName), null);
   }
 
-  public Time getTime(String columnName) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ Time getTime(String columnName) throws SQLException {
     return getTime(findColumn(columnName), null);
   }
 
-  public Timestamp getTimestamp(String columnName) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ Timestamp getTimestamp(String columnName) throws SQLException {
     return getTimestamp(findColumn(columnName), null);
   }
 
-  public InputStream getAsciiStream(String columnName) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ InputStream getAsciiStream(String columnName) throws SQLException {
     return getAsciiStream(findColumn(columnName));
   }
 
-  public InputStream getUnicodeStream(String columnName) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ InputStream getUnicodeStream(String columnName) throws SQLException {
     return getUnicodeStream(findColumn(columnName));
   }
 
-  public InputStream getBinaryStream(String columnName) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ InputStream getBinaryStream(String columnName) throws SQLException {
     return getBinaryStream(findColumn(columnName));
   }
 
-  public SQLWarning getWarnings() throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ SQLWarning getWarnings() throws SQLException {
     checkClosed();
     return warnings;
   }
@@ -2567,18 +2700,18 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  public String getCursorName() throws SQLException {
+  public /* @Nullable */ String getCursorName() throws SQLException {
     checkClosed();
     return null;
   }
 
   @Override
-  public Object getObject(int columnIndex) throws SQLException {
+  public /* @Nullable */ Object getObject(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getObject columnIndex: {0}", columnIndex);
     Field field;
 
-    checkResultSet(columnIndex);
-    if (wasNullFlag) {
+    byte[] value = getRawValue(columnIndex);
+    if (value == null) {
       return null;
     }
 
@@ -2596,16 +2729,17 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
 
     if (isBinary(columnIndex)) {
-      return connection.getObject(getPGType(columnIndex), null, thisRow.get(columnIndex - 1));
+      return connection.getObject(getPGType(columnIndex), null, value);
     }
-    return connection.getObject(getPGType(columnIndex), getString(columnIndex), null);
+    String stringValue = castNonNull(getString(columnIndex));
+    return connection.getObject(getPGType(columnIndex), stringValue, null);
   }
 
-  public Object getObject(String columnName) throws SQLException {
+  public /* @Nullable */ Object getObject(String columnName) throws SQLException {
     return getObject(findColumn(columnName));
   }
 
-  public int findColumn(String columnName) throws SQLException {
+  public /* @NonNegative */ int findColumn(String columnName) throws SQLException {
     checkClosed();
 
     int col = findColumnIndex(columnName);
@@ -2634,7 +2768,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return columnNameIndexMap;
   }
 
-  private int findColumnIndex(String columnName) {
+  private /* @NonNegative */ int findColumnIndex(String columnName) {
     if (columnNameIndexMap == null) {
       if (originalQuery != null) {
         columnNameIndexMap = originalQuery.getResultSetColumnNameIndexMap();
@@ -2683,11 +2817,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * @return numeric-parsable representation of money string literal
    * @throws SQLException if something wrong happens
    */
-  public String getFixedString(int col) throws SQLException {
-    return trimMoney(getString(col));
+  public /* @Nullable */ String getFixedString(int col) throws SQLException {
+    String stringValue = castNonNull(getString(col));
+    return trimMoney(stringValue);
   }
 
-  private String trimMoney(String s) {
+  private /* @PolyNull */ String trimMoney(/* @PolyNull */ String s) {
     if (s == null) {
       return null;
     }
@@ -2717,30 +2852,34 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return s;
   }
 
-  protected String getPGType(int column) throws SQLException {
+  /* @Pure */
+  protected String getPGType(/* @Positive */ int column) throws SQLException {
     Field field = fields[column - 1];
     initSqlType(field);
     return field.getPGType();
   }
 
-  protected int getSQLType(int column) throws SQLException {
+  /* @Pure */
+  protected int getSQLType(/* @Positive */ int column) throws SQLException {
     Field field = fields[column - 1];
     initSqlType(field);
     return field.getSQLType();
   }
 
+  /* @Pure */
   private void initSqlType(Field field) throws SQLException {
     if (field.isTypeInitialized()) {
       return;
     }
     TypeInfo typeInfo = connection.getTypeInfo();
     int oid = field.getOID();
-    String pgType = typeInfo.getPGType(oid);
+    String pgType = castNonNull(typeInfo.getPGType(oid));
     int sqlType = typeInfo.getSQLType(pgType);
     field.setSQLType(sqlType);
     field.setPGType(pgType);
   }
 
+  /* @EnsuresNonNull({"updateValues", "rows"}) */
   private void checkUpdateable() throws SQLException {
     checkClosed();
 
@@ -2755,8 +2894,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       // allow every column to be updated without a rehash.
       updateValues = new HashMap<String, Object>((int) (fields.length / 0.75), 0.75f);
     }
+    castNonNull(updateValues, "updateValues");
+    castNonNull(rows, "rows");
   }
 
+  /* @Pure */
+  /* @EnsuresNonNull("rows") */
   protected void checkClosed() throws SQLException {
     if (rows == null) {
       throw new PSQLException(GT.tr("This ResultSet is closed."), PSQLState.OBJECT_NOT_IN_STATE);
@@ -2770,7 +2913,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return rows == null;
   }
 
-  protected void checkColumnIndex(int column) throws SQLException {
+  /* @Pure */
+  protected void checkColumnIndex(/* @Positive */ int column) throws SQLException {
     if (column < 1 || column > fields.length) {
       throw new PSQLException(
           GT.tr("The column index is out of range: {0}, number of columns: {1}.",
@@ -2784,9 +2928,11 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * column number is valid. Also updates the {@link #wasNullFlag} to correct value.
    *
    * @param column The column number to check. Range starts from 1.
+   * @return raw value or null
    * @throws SQLException If state or column is invalid.
    */
-  protected void checkResultSet(int column) throws SQLException {
+  /* @EnsuresNonNull("thisRow") */
+  protected byte /* @Nullable */ [] getRawValue(/* @Positive */ int column) throws SQLException {
     checkClosed();
     if (thisRow == null) {
       throw new PSQLException(
@@ -2794,7 +2940,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
           PSQLState.INVALID_CURSOR_STATE);
     }
     checkColumnIndex(column);
-    wasNullFlag = (thisRow.get(column - 1) == null);
+    byte[] bytes = thisRow.get(column - 1);
+    wasNullFlag = bytes == null;
+    return bytes;
   }
 
   /**
@@ -2803,7 +2951,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * @param column The column to check. Range starts from 1.
    * @return True if the column is in binary format.
    */
-  protected boolean isBinary(int column) {
+  /* @Pure */
+  protected boolean isBinary(/* @Positive */ int column) {
     return fields[column - 1].getFormat() == Field.BINARY_FORMAT;
   }
 
@@ -2812,7 +2961,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   private static final BigInteger SHORTMAX = new BigInteger(Short.toString(Short.MAX_VALUE));
   private static final BigInteger SHORTMIN = new BigInteger(Short.toString(Short.MIN_VALUE));
 
-  public static short toShort(String s) throws SQLException {
+  public static short toShort(/* @Nullable */ String s) throws SQLException {
     if (s != null) {
       try {
         s = s.trim();
@@ -2842,7 +2991,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   private static final BigInteger INTMAX = new BigInteger(Integer.toString(Integer.MAX_VALUE));
   private static final BigInteger INTMIN = new BigInteger(Integer.toString(Integer.MIN_VALUE));
 
-  public static int toInt(String s) throws SQLException {
+  public static int toInt(/* @Nullable */ String s) throws SQLException {
     if (s != null) {
       try {
         s = s.trim();
@@ -2873,7 +3022,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
   private static final BigInteger LONGMAX = new BigInteger(Long.toString(Long.MAX_VALUE));
   private static final BigInteger LONGMIN = new BigInteger(Long.toString(Long.MIN_VALUE));
 
-  public static long toLong(String s) throws SQLException {
+  public static long toLong(/* @Nullable */ String s) throws SQLException {
     if (s != null) {
       try {
         s = s.trim();
@@ -2899,7 +3048,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return 0; // SQL NULL
   }
 
-  public static BigDecimal toBigDecimal(String s) throws SQLException {
+  public static /* @PolyNull */ BigDecimal toBigDecimal(/* @PolyNull */ String s) throws SQLException {
     if (s == null) {
       return null;
     }
@@ -2912,7 +3061,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  public BigDecimal toBigDecimal(String s, int scale) throws SQLException {
+  public /* @PolyNull */ BigDecimal toBigDecimal(/* @PolyNull */ String s, int scale) throws SQLException {
     if (s == null) {
       return null;
     }
@@ -2933,7 +3082,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  public static float toFloat(String s) throws SQLException {
+  public static float toFloat(/* @Nullable */ String s) throws SQLException {
     if (s != null) {
       try {
         s = s.trim();
@@ -2946,7 +3095,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return 0; // SQL NULL
   }
 
-  public static double toDouble(String s) throws SQLException {
+  public static double toDouble(/* @Nullable */ String s) throws SQLException {
     if (s != null) {
       try {
         s = s.trim();
@@ -2959,8 +3108,9 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return 0; // SQL NULL
   }
 
+  /* @RequiresNonNull("rows") */
   private void initRowBuffer() {
-    thisRow = rows.get(currentRow);
+    thisRow = castNonNull(rows, "rows").get(currentRow);
     // We only need a copy of the current row if we're going to
     // modify it via an updatable resultset.
     if (resultsetconcurrency == ResultSet.CONCUR_UPDATABLE) {
@@ -2970,7 +3120,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  private boolean isColumnTrimmable(int columnIndex) throws SQLException {
+  private boolean isColumnTrimmable(/* @Positive */ int columnIndex) throws SQLException {
     switch (getSQLType(columnIndex)) {
       case Types.CHAR:
       case Types.VARCHAR:
@@ -2983,7 +3133,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return false;
   }
 
-  private byte[] trimBytes(int columnIndex, byte[] bytes) throws SQLException {
+  private byte[] trimBytes(/* @Positive */ int columnIndex, byte[] bytes) throws SQLException {
     // we need to trim if maxsize is set and the length is greater than maxsize and the
     // type of this column is a candidate for trimming
     if (maxFieldSize > 0 && bytes.length > maxFieldSize && isColumnTrimmable(columnIndex)) {
@@ -2995,7 +3145,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     }
   }
 
-  private String trimString(int columnIndex, String string) throws SQLException {
+  private String trimString(/* @Positive */ int columnIndex, String string) throws SQLException {
     // we need to trim if maxsize is set and the length is greater than maxsize and the
     // type of this column is a candidate for trimming
     if (maxFieldSize > 0 && string.length() > maxFieldSize && isColumnTrimmable(columnIndex)) {
@@ -3054,6 +3204,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * @throws PSQLException If the field type is not supported numeric type or if the value is out of
    *         range.
    */
+  /* @Pure */
   private long readLongValue(byte[] bytes, int oid, long minVal, long maxVal, String targetType)
       throws PSQLException {
     long val;
@@ -3095,10 +3246,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return val;
   }
 
-  protected void updateValue(int columnIndex, Object value) throws SQLException {
+  protected void updateValue(/* @Positive */ int columnIndex, /* @Nullable */ Object value) throws SQLException {
     checkUpdateable();
 
-    if (!onInsertRow && (isBeforeFirst() || isAfterLast() || rows.isEmpty())) {
+    if (!onInsertRow && (isBeforeFirst() || isAfterLast() || castNonNull(rows, "rows").isEmpty())) {
       throw new PSQLException(
           GT.tr(
               "Cannot update the ResultSet because it is either before the start or after the end of the results."),
@@ -3112,10 +3263,12 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       updateNull(columnIndex);
     } else {
       PGResultSetMetaData md = (PGResultSetMetaData) getMetaData();
-      updateValues.put(md.getBaseColumnName(columnIndex), value);
+      castNonNull(updateValues, "updateValues")
+          .put(md.getBaseColumnName(columnIndex), value);
     }
   }
 
+  /* @Pure */
   protected Object getUUID(String data) throws SQLException {
     UUID uuid;
     try {
@@ -3127,6 +3280,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return uuid;
   }
 
+  /* @Pure */
   protected Object getUUID(byte[] data) throws SQLException {
     return new UUID(ByteConverter.int8(data, 0), ByteConverter.int8(data, 8));
   }
@@ -3140,7 +3294,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       this.name = name;
     }
 
-    Object getValue() throws SQLException {
+    /* @Nullable */ Object getValue() throws SQLException {
       return getObject(index);
     }
   }
@@ -3153,10 +3307,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
   static class NullObject extends PGobject {
     NullObject(String type) {
-      setType(type);
+      this.type = type;
     }
 
-    public String getValue() {
+    public /* @Nullable */ String getValue() {
       return null;
     }
   }
@@ -3166,42 +3320,42 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
    * Currently only used for assembling generated keys from batch statement execution.
    */
   void addRows(List<Tuple> tuples) {
-    rows.addAll(tuples);
+    castNonNull(rows, "rows").addAll(tuples);
   }
 
-  public void updateRef(int columnIndex, Ref x) throws SQLException {
+  public void updateRef(/* @Positive */ int columnIndex, /* @Nullable */ Ref x) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateRef(int,Ref)");
   }
 
-  public void updateRef(String columnName, Ref x) throws SQLException {
+  public void updateRef(String columnName, /* @Nullable */ Ref x) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateRef(String,Ref)");
   }
 
-  public void updateBlob(int columnIndex, Blob x) throws SQLException {
+  public void updateBlob(/* @Positive */ int columnIndex, /* @Nullable */ Blob x) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateBlob(int,Blob)");
   }
 
-  public void updateBlob(String columnName, Blob x) throws SQLException {
+  public void updateBlob(String columnName, /* @Nullable */ Blob x) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateBlob(String,Blob)");
   }
 
-  public void updateClob(int columnIndex, Clob x) throws SQLException {
+  public void updateClob(/* @Positive */ int columnIndex, /* @Nullable */ Clob x) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateClob(int,Clob)");
   }
 
-  public void updateClob(String columnName, Clob x) throws SQLException {
+  public void updateClob(String columnName, /* @Nullable */ Clob x) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateClob(String,Clob)");
   }
 
-  public void updateArray(int columnIndex, Array x) throws SQLException {
+  public void updateArray(/* @Positive */ int columnIndex, /* @Nullable */ Array x) throws SQLException {
     updateObject(columnIndex, x);
   }
 
-  public void updateArray(String columnName, Array x) throws SQLException {
+  public void updateArray(String columnName, /* @Nullable */ Array x) throws SQLException {
     updateArray(findColumn(columnName), x);
   }
 
-  public <T> T getObject(int columnIndex, Class<T> type) throws SQLException {
+  public <T> /* @Nullable */ T getObject(/* @Positive */ int columnIndex, Class<T> type) throws SQLException {
     if (type == null) {
       throw new SQLException("type is null");
     }
@@ -3329,7 +3483,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
       //#endif
       ) {
         Timestamp timestampValue = getTimestamp(columnIndex);
-        if (wasNull()) {
+        if (timestampValue == null) {
           return null;
         }
         Calendar calendar = Calendar.getInstance(getDefaultCalendar().getTimeZone());
@@ -3356,7 +3510,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     } else if (type == java.util.Date.class) {
       if (sqlType == Types.TIMESTAMP) {
         Timestamp timestamp = getTimestamp(columnIndex);
-        if (wasNull()) {
+        if (timestamp == null) {
           return null;
         }
         return type.cast(new java.util.Date(timestamp.getTime()));
@@ -3396,7 +3550,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     } else if (type == java.time.LocalDate.class) {
       if (sqlType == Types.DATE) {
         Date dateValue = getDate(columnIndex);
-        if (wasNull()) {
+        if (dateValue == null) {
           return null;
         }
         long time = dateValue.getTime();
@@ -3409,7 +3563,7 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
         return type.cast(dateValue.toLocalDate());
       } else if (sqlType == Types.TIMESTAMP) {
         java.time.LocalDateTime localDateTimeValue = getLocalDateTime(columnIndex);
-        if (wasNull()) {
+        if (localDateTimeValue == null) {
           return null;
         }
         return type.cast(localDateTimeValue.toLocalDate());
@@ -3443,7 +3597,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     } else if (PGobject.class.isAssignableFrom(type)) {
       Object object;
       if (isBinary(columnIndex)) {
-        object = connection.getObject(getPGType(columnIndex), null, thisRow.get(columnIndex - 1));
+        byte[] byteValue = castNonNull(thisRow, "thisRow").get(columnIndex - 1);
+        object = connection.getObject(getPGType(columnIndex), null, byteValue);
       } else {
         object = connection.getObject(getPGType(columnIndex), getString(columnIndex), null);
       }
@@ -3453,54 +3608,54 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
             PSQLState.INVALID_PARAMETER_VALUE);
   }
 
-  public <T> T getObject(String columnLabel, Class<T> type) throws SQLException {
+  public <T> /* @Nullable */ T getObject(String columnLabel, Class<T> type) throws SQLException {
     return getObject(findColumn(columnLabel), type);
   }
 
-  public Object getObject(String s, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getObject(String s, /* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     return getObjectImpl(s, map);
   }
 
-  public Object getObject(int i, Map<String, Class<?>> map) throws SQLException {
+  public /* @Nullable */ Object getObject(/* @Positive */ int i, /* @Nullable */ Map<String, Class<?>> map) throws SQLException {
     return getObjectImpl(i, map);
   }
 
   //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
-  public void updateObject(int columnIndex, Object x, java.sql.SQLType targetSqlType,
+  public void updateObject(/* @Positive */ int columnIndex, /* @Nullable */ Object x, java.sql.SQLType targetSqlType,
       int scaleOrLength) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateObject");
   }
 
-  public void updateObject(String columnLabel, Object x, java.sql.SQLType targetSqlType,
+  public void updateObject(String columnLabel, /* @Nullable */ Object x, java.sql.SQLType targetSqlType,
       int scaleOrLength) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateObject");
   }
 
-  public void updateObject(int columnIndex, Object x, java.sql.SQLType targetSqlType)
+  public void updateObject(/* @Positive */ int columnIndex, /* @Nullable */ Object x, java.sql.SQLType targetSqlType)
       throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateObject");
   }
 
-  public void updateObject(String columnLabel, Object x, java.sql.SQLType targetSqlType)
+  public void updateObject(String columnLabel, /* @Nullable */ Object x, java.sql.SQLType targetSqlType)
       throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateObject");
   }
   //#endif
 
-  public RowId getRowId(int columnIndex) throws SQLException {
+  public /* @Nullable */ RowId getRowId(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getRowId columnIndex: {0}", columnIndex);
     throw org.postgresql.Driver.notImplemented(this.getClass(), "getRowId(int)");
   }
 
-  public RowId getRowId(String columnName) throws SQLException {
+  public /* @Nullable */ RowId getRowId(String columnName) throws SQLException {
     return getRowId(findColumn(columnName));
   }
 
-  public void updateRowId(int columnIndex, RowId x) throws SQLException {
+  public void updateRowId(/* @Positive */ int columnIndex, /* @Nullable */ RowId x) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateRowId(int, RowId)");
   }
 
-  public void updateRowId(String columnName, RowId x) throws SQLException {
+  public void updateRowId(String columnName, /* @Nullable */ RowId x) throws SQLException {
     updateRowId(findColumn(columnName), x);
   }
 
@@ -3512,83 +3667,84 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return (rows == null);
   }
 
-  public void updateNString(int columnIndex, String nString) throws SQLException {
+  public void updateNString(/* @Positive */ int columnIndex, /* @Nullable */ String nString) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateNString(int, String)");
   }
 
-  public void updateNString(String columnName, String nString) throws SQLException {
+  public void updateNString(String columnName, /* @Nullable */ String nString) throws SQLException {
     updateNString(findColumn(columnName), nString);
   }
 
-  public void updateNClob(int columnIndex, NClob nClob) throws SQLException {
+  public void updateNClob(/* @Positive */ int columnIndex, /* @Nullable */ NClob nClob) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateNClob(int, NClob)");
   }
 
-  public void updateNClob(String columnName, NClob nClob) throws SQLException {
+  public void updateNClob(String columnName, /* @Nullable */ NClob nClob) throws SQLException {
     updateNClob(findColumn(columnName), nClob);
   }
 
-  public void updateNClob(int columnIndex, Reader reader) throws SQLException {
+  public void updateNClob(/* @Positive */ int columnIndex, /* @Nullable */ Reader reader) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateNClob(int, Reader)");
   }
 
-  public void updateNClob(String columnName, Reader reader) throws SQLException {
+  public void updateNClob(String columnName, /* @Nullable */ Reader reader) throws SQLException {
     updateNClob(findColumn(columnName), reader);
   }
 
-  public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException {
+  public void updateNClob(/* @Positive */ int columnIndex, /* @Nullable */ Reader reader, long length) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateNClob(int, Reader, long)");
   }
 
-  public void updateNClob(String columnName, Reader reader, long length) throws SQLException {
+  public void updateNClob(String columnName, /* @Nullable */ Reader reader, long length) throws SQLException {
     updateNClob(findColumn(columnName), reader, length);
   }
 
-  public NClob getNClob(int columnIndex) throws SQLException {
+  public /* @Nullable */ NClob getNClob(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getNClob columnIndex: {0}", columnIndex);
     throw org.postgresql.Driver.notImplemented(this.getClass(), "getNClob(int)");
   }
 
-  public NClob getNClob(String columnName) throws SQLException {
+  public /* @Nullable */ NClob getNClob(String columnName) throws SQLException {
     return getNClob(findColumn(columnName));
   }
 
-  public void updateBlob(int columnIndex, InputStream inputStream, long length)
+  public void updateBlob(/* @Positive */ int columnIndex, /* @Nullable */ InputStream inputStream, long length)
       throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateBlob(int, InputStream, long)");
   }
 
-  public void updateBlob(String columnName, InputStream inputStream, long length)
+  public void updateBlob(String columnName, /* @Nullable */ InputStream inputStream, long length)
       throws SQLException {
     updateBlob(findColumn(columnName), inputStream, length);
   }
 
-  public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException {
+  public void updateBlob(/* @Positive */ int columnIndex, /* @Nullable */ InputStream inputStream) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateBlob(int, InputStream)");
   }
 
-  public void updateBlob(String columnName, InputStream inputStream) throws SQLException {
+  public void updateBlob(String columnName, /* @Nullable */ InputStream inputStream) throws SQLException {
     updateBlob(findColumn(columnName), inputStream);
   }
 
-  public void updateClob(int columnIndex, Reader reader, long length) throws SQLException {
+  public void updateClob(/* @Positive */ int columnIndex, /* @Nullable */ Reader reader, long length) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateClob(int, Reader, long)");
   }
 
-  public void updateClob(String columnName, Reader reader, long length) throws SQLException {
+  public void updateClob(String columnName, /* @Nullable */ Reader reader, long length) throws SQLException {
     updateClob(findColumn(columnName), reader, length);
   }
 
-  public void updateClob(int columnIndex, Reader reader) throws SQLException {
+  public void updateClob(/* @Positive */ int columnIndex, /* @Nullable */ Reader reader) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(), "updateClob(int, Reader)");
   }
 
-  public void updateClob(String columnName, Reader reader) throws SQLException {
+  public void updateClob(String columnName, /* @Nullable */ Reader reader) throws SQLException {
     updateClob(findColumn(columnName), reader);
   }
 
-  public SQLXML getSQLXML(int columnIndex) throws SQLException {
+  /* @Pure */
+  public /* @Nullable */ SQLXML getSQLXML(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getSQLXML columnIndex: {0}", columnIndex);
     String data = getString(columnIndex);
     if (data == null) {
@@ -3598,120 +3754,138 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
     return new PgSQLXML(connection, data);
   }
 
-  public SQLXML getSQLXML(String columnName) throws SQLException {
+  public /* @Nullable */ SQLXML getSQLXML(String columnName) throws SQLException {
     return getSQLXML(findColumn(columnName));
   }
 
-  public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException {
+  public void updateSQLXML(/* @Positive */ int columnIndex, /* @Nullable */ SQLXML xmlObject) throws SQLException {
     updateValue(columnIndex, xmlObject);
   }
 
-  public void updateSQLXML(String columnName, SQLXML xmlObject) throws SQLException {
+  public void updateSQLXML(String columnName, /* @Nullable */ SQLXML xmlObject) throws SQLException {
     updateSQLXML(findColumn(columnName), xmlObject);
   }
 
-  public String getNString(int columnIndex) throws SQLException {
+  public /* @Nullable */ String getNString(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getNString columnIndex: {0}", columnIndex);
     throw org.postgresql.Driver.notImplemented(this.getClass(), "getNString(int)");
   }
 
-  public String getNString(String columnName) throws SQLException {
+  public /* @Nullable */ String getNString(String columnName) throws SQLException {
     return getNString(findColumn(columnName));
   }
 
-  public Reader getNCharacterStream(int columnIndex) throws SQLException {
+  public /* @Nullable */ Reader getNCharacterStream(/* @Positive */ int columnIndex) throws SQLException {
     connection.getLogger().log(Level.FINEST, "  getNCharacterStream columnIndex: {0}", columnIndex);
     throw org.postgresql.Driver.notImplemented(this.getClass(), "getNCharacterStream(int)");
   }
 
-  public Reader getNCharacterStream(String columnName) throws SQLException {
+  public /* @Nullable */ Reader getNCharacterStream(String columnName) throws SQLException {
     return getNCharacterStream(findColumn(columnName));
   }
 
-  public void updateNCharacterStream(int columnIndex, Reader x, int length) throws SQLException {
+  public void updateNCharacterStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ Reader x, int length) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateNCharacterStream(int, Reader, int)");
   }
 
-  public void updateNCharacterStream(String columnName, Reader x, int length) throws SQLException {
+  public void updateNCharacterStream(String columnName,
+      /* @Nullable */ Reader x, int length) throws SQLException {
     updateNCharacterStream(findColumn(columnName), x, length);
   }
 
-  public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException {
+  public void updateNCharacterStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ Reader x) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateNCharacterStream(int, Reader)");
   }
 
-  public void updateNCharacterStream(String columnName, Reader x) throws SQLException {
+  public void updateNCharacterStream(String columnName,
+      /* @Nullable */ Reader x) throws SQLException {
     updateNCharacterStream(findColumn(columnName), x);
   }
 
-  public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException {
+  public void updateNCharacterStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ Reader x, long length) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateNCharacterStream(int, Reader, long)");
   }
 
-  public void updateNCharacterStream(String columnName, Reader x, long length) throws SQLException {
+  public void updateNCharacterStream(String columnName,
+      /* @Nullable */ Reader x, long length) throws SQLException {
     updateNCharacterStream(findColumn(columnName), x, length);
   }
 
-  public void updateCharacterStream(int columnIndex, Reader reader, long length)
+  public void updateCharacterStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ Reader reader, long length)
       throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateCharaceterStream(int, Reader, long)");
   }
 
-  public void updateCharacterStream(String columnName, Reader reader, long length)
+  public void updateCharacterStream(String columnName,
+      /* @Nullable */ Reader reader, long length)
       throws SQLException {
     updateCharacterStream(findColumn(columnName), reader, length);
   }
 
-  public void updateCharacterStream(int columnIndex, Reader reader) throws SQLException {
+  public void updateCharacterStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ Reader reader) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateCharaceterStream(int, Reader)");
   }
 
-  public void updateCharacterStream(String columnName, Reader reader) throws SQLException {
+  public void updateCharacterStream(String columnName,
+      /* @Nullable */ Reader reader) throws SQLException {
     updateCharacterStream(findColumn(columnName), reader);
   }
 
-  public void updateBinaryStream(int columnIndex, InputStream inputStream, long length)
+  public void updateBinaryStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ InputStream inputStream, long length)
       throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateBinaryStream(int, InputStream, long)");
   }
 
-  public void updateBinaryStream(String columnName, InputStream inputStream, long length)
+  public void updateBinaryStream(String columnName,
+      /* @Nullable */ InputStream inputStream, long length)
       throws SQLException {
     updateBinaryStream(findColumn(columnName), inputStream, length);
   }
 
-  public void updateBinaryStream(int columnIndex, InputStream inputStream) throws SQLException {
+  public void updateBinaryStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ InputStream inputStream) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateBinaryStream(int, InputStream)");
   }
 
-  public void updateBinaryStream(String columnName, InputStream inputStream) throws SQLException {
+  public void updateBinaryStream(String columnName,
+      /* @Nullable */ InputStream inputStream) throws SQLException {
     updateBinaryStream(findColumn(columnName), inputStream);
   }
 
-  public void updateAsciiStream(int columnIndex, InputStream inputStream, long length)
+  public void updateAsciiStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ InputStream inputStream, long length)
       throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateAsciiStream(int, InputStream, long)");
   }
 
-  public void updateAsciiStream(String columnName, InputStream inputStream, long length)
+  public void updateAsciiStream(String columnName,
+      /* @Nullable */ InputStream inputStream, long length)
       throws SQLException {
     updateAsciiStream(findColumn(columnName), inputStream, length);
   }
 
-  public void updateAsciiStream(int columnIndex, InputStream inputStream) throws SQLException {
+  public void updateAsciiStream(/* @Positive */ int columnIndex,
+      /* @Nullable */ InputStream inputStream) throws SQLException {
     throw org.postgresql.Driver.notImplemented(this.getClass(),
         "updateAsciiStream(int, InputStream)");
   }
 
-  public void updateAsciiStream(String columnName, InputStream inputStream) throws SQLException {
+  public void updateAsciiStream(String columnName,
+      /* @Nullable */ InputStream inputStream) throws SQLException {
     updateAsciiStream(findColumn(columnName), inputStream);
   }
 
diff --git a/src/main/java/org/postgresql/jdbc/PgResultSetMetaData.java b/src/main/java/org/postgresql/jdbc/PgResultSetMetaData.java
index 1d9f5bd0cd1acacfe8eeb476376b4a4a4f01053f..4636cfbcdc25ab41734508f50926ca0a94a43a68 100644
--- a/src/main/java/org/postgresql/jdbc/PgResultSetMetaData.java
+++ b/src/main/java/org/postgresql/jdbc/PgResultSetMetaData.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGResultSetMetaData;
 import org.postgresql.core.BaseConnection;
 import org.postgresql.core.Field;
@@ -16,6 +18,8 @@ import org.postgresql.util.JdbcBlackHole;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
@@ -106,7 +110,7 @@ public class PgResultSetMetaData implements ResultSetMetaData, PGResultSetMetaDa
   public boolean isCurrency(int column) throws SQLException {
     String typeName = getPGType(column);
 
-    return typeName.equals("cash") || typeName.equals("money");
+    return "cash".equals(typeName) || "money".equals(typeName);
   }
 
   @Override
@@ -248,9 +252,9 @@ public class PgResultSetMetaData implements ResultSetMetaData, PGResultSetMetaDa
       while (rs.next()) {
         int table = (int) rs.getLong(1);
         int column = (int) rs.getLong(2);
-        String columnName = rs.getString(3);
-        String tableName = rs.getString(4);
-        String schemaName = rs.getString(5);
+        String columnName = castNonNull(rs.getString(3));
+        String tableName = castNonNull(rs.getString(4));
+        String schemaName = castNonNull(rs.getString(5));
         int nullable =
             rs.getBoolean(6) ? ResultSetMetaData.columnNoNulls : ResultSetMetaData.columnNullable;
         boolean autoIncrement = rs.getBoolean(7);
@@ -327,7 +331,7 @@ public class PgResultSetMetaData implements ResultSetMetaData, PGResultSetMetaDa
       }
     }
 
-    return type;
+    return castNonNull(type);
   }
 
   /**
@@ -397,7 +401,7 @@ public class PgResultSetMetaData implements ResultSetMetaData, PGResultSetMetaDa
     return fields[columnIndex - 1];
   }
 
-  protected String getPGType(int columnIndex) throws SQLException {
+  protected /* @Nullable */ String getPGType(int columnIndex) throws SQLException {
     return connection.getTypeInfo().getPGType(getField(columnIndex).getOID());
   }
 
diff --git a/src/main/java/org/postgresql/jdbc/PgSQLXML.java b/src/main/java/org/postgresql/jdbc/PgSQLXML.java
index f919d7fd9611c9caa43a7f5d80cb70d5e45d672e..7acc1515edd07db9e66a4e8415ccf16668b3cf0c 100644
--- a/src/main/java/org/postgresql/jdbc/PgSQLXML.java
+++ b/src/main/java/org/postgresql/jdbc/PgSQLXML.java
@@ -12,6 +12,7 @@ import org.postgresql.util.PSQLState;
 import org.postgresql.xml.DefaultPGXmlFactoryFactory;
 import org.postgresql.xml.PGXmlFactoryFactory;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
 import org.xml.sax.InputSource;
 import org.xml.sax.XMLReader;
 
@@ -52,24 +53,24 @@ import javax.xml.transform.stream.StreamSource;
 public class PgSQLXML implements SQLXML {
 
   private final BaseConnection conn;
-  private String data; // The actual data contained.
+  private /* @Nullable */ String data; // The actual data contained.
   private boolean initialized; // Has someone assigned the data for this object?
   private boolean active; // Is anyone in the process of loading data into us?
   private boolean freed;
 
-  private ByteArrayOutputStream byteArrayOutputStream;
-  private StringWriter stringWriter;
-  private DOMResult domResult;
+  private /* @Nullable */ ByteArrayOutputStream byteArrayOutputStream;
+  private /* @Nullable */ StringWriter stringWriter;
+  private /* @Nullable */ DOMResult domResult;
 
   public PgSQLXML(BaseConnection conn) {
     this(conn, null, false);
   }
 
-  public PgSQLXML(BaseConnection conn, String data) {
+  public PgSQLXML(BaseConnection conn, /* @Nullable */ String data) {
     this(conn, data, true);
   }
 
-  private PgSQLXML(BaseConnection conn, String data, boolean initialized) {
+  private PgSQLXML(BaseConnection conn, /* @Nullable */ String data, boolean initialized) {
     this.conn = conn;
     this.data = data;
     this.initialized = initialized;
@@ -91,7 +92,7 @@ public class PgSQLXML implements SQLXML {
   }
 
   @Override
-  public synchronized InputStream getBinaryStream() throws SQLException {
+  public synchronized /* @Nullable */ InputStream getBinaryStream() throws SQLException {
     checkFreed();
     ensureInitialized();
 
@@ -111,7 +112,7 @@ public class PgSQLXML implements SQLXML {
   }
 
   @Override
-  public synchronized Reader getCharacterStream() throws SQLException {
+  public synchronized /* @Nullable */ Reader getCharacterStream() throws SQLException {
     checkFreed();
     ensureInitialized();
 
@@ -129,10 +130,12 @@ public class PgSQLXML implements SQLXML {
   // ensure they are the same.
   //
   @Override
-  public synchronized <T extends Source> T getSource(Class<T> sourceClass) throws SQLException {
+  public synchronized <T extends Source> /* @Nullable */ T getSource(/* @Nullable */ Class<T> sourceClass)
+      throws SQLException {
     checkFreed();
     ensureInitialized();
 
+    String data = this.data;
     if (data == null) {
       return null;
     }
@@ -141,17 +144,19 @@ public class PgSQLXML implements SQLXML {
       if (sourceClass == null || DOMSource.class.equals(sourceClass)) {
         DocumentBuilder builder = getXmlFactoryFactory().newDocumentBuilder();
         InputSource input = new InputSource(new StringReader(data));
-        return (T) new DOMSource(builder.parse(input));
+        DOMSource domSource = new DOMSource(builder.parse(input));
+        //noinspection unchecked
+        return (T) domSource;
       } else if (SAXSource.class.equals(sourceClass)) {
         XMLReader reader = getXmlFactoryFactory().createXMLReader();
         InputSource is = new InputSource(new StringReader(data));
-        return (T) new SAXSource(reader, is);
+        return sourceClass.cast(new SAXSource(reader, is));
       } else if (StreamSource.class.equals(sourceClass)) {
-        return (T) new StreamSource(new StringReader(data));
+        return sourceClass.cast(new StreamSource(new StringReader(data)));
       } else if (StAXSource.class.equals(sourceClass)) {
         XMLInputFactory xif = getXmlFactoryFactory().newXMLInputFactory();
         XMLStreamReader xsr = xif.createXMLStreamReader(new StringReader(data));
-        return (T) new StAXSource(xsr);
+        return sourceClass.cast(new StAXSource(xsr));
       }
     } catch (Exception e) {
       throw new PSQLException(GT.tr("Unable to decode xml data."), PSQLState.DATA_ERROR, e);
@@ -162,7 +167,7 @@ public class PgSQLXML implements SQLXML {
   }
 
   @Override
-  public synchronized String getString() throws SQLException {
+  public synchronized /* @Nullable */ String getString() throws SQLException {
     checkFreed();
     ensureInitialized();
     return data;
@@ -194,6 +199,7 @@ public class PgSQLXML implements SQLXML {
     if (resultClass == null || DOMResult.class.equals(resultClass)) {
       domResult = new DOMResult();
       active = true;
+      //noinspection unchecked
       return (T) domResult;
     } else if (SAXResult.class.equals(resultClass)) {
       try {
@@ -202,7 +208,7 @@ public class PgSQLXML implements SQLXML {
         stringWriter = new StringWriter();
         transformerHandler.setResult(new StreamResult(stringWriter));
         active = true;
-        return (T) new SAXResult(transformerHandler);
+        return resultClass.cast(new SAXResult(transformerHandler));
       } catch (TransformerException te) {
         throw new PSQLException(GT.tr("Unable to create SAXResult for SQLXML."),
             PSQLState.UNEXPECTED_ERROR, te);
@@ -210,14 +216,15 @@ public class PgSQLXML implements SQLXML {
     } else if (StreamResult.class.equals(resultClass)) {
       stringWriter = new StringWriter();
       active = true;
-      return (T) new StreamResult(stringWriter);
+      return resultClass.cast(new StreamResult(stringWriter));
     } else if (StAXResult.class.equals(resultClass)) {
-      stringWriter = new StringWriter();
+      StringWriter stringWriter = new StringWriter();
+      this.stringWriter = stringWriter;
       try {
         XMLOutputFactory xof = getXmlFactoryFactory().newXMLOutputFactory();
         XMLStreamWriter xsw = xof.createXMLStreamWriter(stringWriter);
         active = true;
-        return (T) new StAXResult(xsw);
+        return resultClass.cast(new StAXResult(xsw));
       } catch (XMLStreamException xse) {
         throw new PSQLException(GT.tr("Unable to create StAXResult for SQLXML"),
             PSQLState.UNEXPECTED_ERROR, xse);
@@ -273,6 +280,7 @@ public class PgSQLXML implements SQLXML {
       stringWriter = null;
       active = false;
     } else if (domResult != null) {
+      DOMResult domResult = this.domResult;
       // Copy the content from the result to a source
       // and use the identify transform to get it into a
       // friendlier result format.
diff --git a/src/main/java/org/postgresql/jdbc/PgStatement.java b/src/main/java/org/postgresql/jdbc/PgStatement.java
index 00c089c3a47ab85cedff3fc6f458cbe2f1821ce0..cd2d095c98b5d7036ee4c1363bd57d1051e248fa 100644
--- a/src/main/java/org/postgresql/jdbc/PgStatement.java
+++ b/src/main/java/org/postgresql/jdbc/PgStatement.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.Driver;
 import org.postgresql.core.BaseConnection;
 import org.postgresql.core.BaseStatement;
@@ -21,6 +23,11 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.index.qual.NonNegative;
+// import org.checkerframework.checker.lock.qual.GuardedBy;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.checker.nullness.qual.RequiresNonNull;
+
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -43,8 +50,8 @@ public class PgStatement implements Statement, BaseStatement {
   // only for testing purposes. even single shot statements will use binary transfers
   private boolean forceBinaryTransfers = DEFAULT_FORCE_BINARY_TRANSFERS;
 
-  protected ArrayList<Query> batchStatements = null;
-  protected ArrayList<ParameterList> batchParameters = null;
+  protected /* @Nullable */ ArrayList<Query> batchStatements = null;
+  protected /* @Nullable */ ArrayList</* @Nullable */ ParameterList> batchParameters = null;
   protected final int resultsettype; // the resultset type to return (ResultSet.TYPE_xxx)
   protected final int concurrency; // is it updateable or not? (ResultSet.CONCUR_xxx)
   private final int rsHoldability;
@@ -60,9 +67,10 @@ public class PgStatement implements Statement, BaseStatement {
    * cancelTask was created. Note: the field must be set/get/compareAndSet via
    * {@link #CANCEL_TIMER_UPDATER} as per {@link AtomicReferenceFieldUpdater} javadoc.
    */
-  private volatile TimerTask cancelTimerTask = null;
-  private static final AtomicReferenceFieldUpdater<PgStatement, TimerTask> CANCEL_TIMER_UPDATER =
-      AtomicReferenceFieldUpdater.newUpdater(PgStatement.class, TimerTask.class, "cancelTimerTask");
+  private volatile /* @Nullable */ TimerTask cancelTimerTask = null;
+  private static final AtomicReferenceFieldUpdater<PgStatement, /* @Nullable */ TimerTask> CANCEL_TIMER_UPDATER =
+      AtomicReferenceFieldUpdater.<PgStatement, /* @Nullable */ TimerTask>newUpdater(
+          PgStatement.class, TimerTask.class, "cancelTimerTask");
 
   /**
    * Protects statement from out-of-order cancels. It protects from both
@@ -99,7 +107,7 @@ public class PgStatement implements Statement, BaseStatement {
   /**
    * The warnings chain.
    */
-  protected volatile PSQLWarningWrapper warnings = null;
+  protected volatile /* @Nullable */ PSQLWarningWrapper warnings = null;
 
   /**
    * Maximum number of rows to return, 0 = unlimited.
@@ -121,22 +129,23 @@ public class PgStatement implements Statement, BaseStatement {
   /**
    * The current results.
    */
-  protected ResultWrapper result = null;
+  protected /* @Nullable */ ResultWrapper result = null;
 
   /**
    * The first unclosed result.
    */
-  protected volatile ResultWrapper firstUnclosedResult = null;
+  protected /* @Nullable */ /* @GuardedBy("<self>") */ ResultWrapper firstUnclosedResult = null;
 
   /**
    * Results returned by a statement that wants generated keys.
    */
-  protected ResultWrapper generatedKeys = null;
+  protected /* @Nullable */ ResultWrapper generatedKeys = null;
 
   protected int mPrepareThreshold; // Reuse threshold to enable use of PREPARE
 
   protected int maxFieldSize = 0;
 
+  @SuppressWarnings("method.invocation.invalid")
   PgStatement(PgConnection c, int rsType, int rsConcurrency, int rsHoldability)
       throws SQLException {
     this.connection = c;
@@ -148,8 +157,9 @@ public class PgStatement implements Statement, BaseStatement {
     this.rsHoldability = rsHoldability;
   }
 
-  public ResultSet createResultSet(Query originalQuery, Field[] fields, List<Tuple> tuples,
-      ResultCursor cursor) throws SQLException {
+  @SuppressWarnings("method.invocation.invalid")
+  public ResultSet createResultSet(/* @Nullable */ Query originalQuery, Field[] fields, List<Tuple> tuples,
+      /* @Nullable */ ResultCursor cursor) throws SQLException {
     PgResultSet newResult = new PgResultSet(originalQuery, this, fields, tuples, cursor,
         getMaxRows(), getMaxFieldSize(), getResultSetType(), getResultSetConcurrency(),
         getResultSetHoldability());
@@ -162,11 +172,11 @@ public class PgStatement implements Statement, BaseStatement {
     return connection;
   }
 
-  public String getFetchingCursorName() {
+  public /* @Nullable */ String getFetchingCursorName() {
     return null;
   }
 
-  public int getFetchSize() {
+  public /* @NonNegative */ int getFetchSize() {
     return fetchSize;
   }
 
@@ -183,10 +193,10 @@ public class PgStatement implements Statement, BaseStatement {
    * ResultHandler implementations for updates, queries, and either-or.
    */
   public class StatementResultHandler extends ResultHandlerBase {
-    private ResultWrapper results;
-    private ResultWrapper lastResult;
+    private /* @Nullable */ ResultWrapper results;
+    private /* @Nullable */ ResultWrapper lastResult;
 
-    ResultWrapper getResults() {
+    /* @Nullable */ ResultWrapper getResults() {
       return results;
     }
 
@@ -194,13 +204,13 @@ public class PgStatement implements Statement, BaseStatement {
       if (results == null) {
         lastResult = results = newResult;
       } else {
-        lastResult.append(newResult);
+        castNonNull(lastResult).append(newResult);
       }
     }
 
     @Override
     public void handleResultRows(Query fromQuery, Field[] fields, List<Tuple> tuples,
-        ResultCursor cursor) {
+        /* @Nullable */ ResultCursor cursor) {
       try {
         ResultSet rs = PgStatement.this.createResultSet(fromQuery, fields, tuples, cursor);
         append(new ResultWrapper(rs));
@@ -233,12 +243,13 @@ public class PgStatement implements Statement, BaseStatement {
   protected ResultSet getSingleResultSet() throws SQLException {
     synchronized (this) {
       checkClosed();
+      ResultWrapper result = castNonNull(this.result);
       if (result.getNext() != null) {
         throw new PSQLException(GT.tr("Multiple ResultSets were returned by the query."),
             PSQLState.TOO_MANY_RESULTS);
       }
 
-      return result.getResultSet();
+      return castNonNull(result.getResultSet(), "result.getResultSet()");
     }
   }
 
@@ -273,7 +284,8 @@ public class PgStatement implements Statement, BaseStatement {
     return executeCachedSql(sql, flags, NO_RETURNING_COLUMNS);
   }
 
-  private boolean executeCachedSql(String sql, int flags, String[] columnNames) throws SQLException {
+  private boolean executeCachedSql(String sql, int flags,
+      String /* @Nullable */ [] columnNames) throws SQLException {
     PreferQueryMode preferQueryMode = connection.getPreferQueryMode();
     // Simple statements should not replace ?, ? with $1, $2
     boolean shouldUseParameterized = false;
@@ -320,6 +332,21 @@ public class PgStatement implements Statement, BaseStatement {
         PSQLState.WRONG_OBJECT_TYPE);
   }
 
+  private void closeUnclosedResults() throws SQLException {
+    synchronized (this) {
+      ResultWrapper resultWrapper = this.firstUnclosedResult;
+      ResultWrapper currentResult = this.result;
+      for (; resultWrapper != currentResult && resultWrapper != null;
+           resultWrapper = resultWrapper.getNext()) {
+        PgResultSet rs = (PgResultSet) resultWrapper.getResultSet();
+        if (rs != null) {
+          rs.closeInternally();
+        }
+      }
+      firstUnclosedResult = resultWrapper;
+    }
+  }
+
   protected void closeForNextExecution() throws SQLException {
 
     // Every statement execution clears any previous warnings.
@@ -327,20 +354,16 @@ public class PgStatement implements Statement, BaseStatement {
 
     // Close any existing resultsets associated with this statement.
     synchronized (this) {
-      while (firstUnclosedResult != null) {
-        PgResultSet rs = (PgResultSet)firstUnclosedResult.getResultSet();
-        if (rs != null) {
-          rs.closeInternally();
-        }
-        firstUnclosedResult = firstUnclosedResult.getNext();
-      }
+      closeUnclosedResults();
       result = null;
 
+      ResultWrapper generatedKeys = this.generatedKeys;
       if (generatedKeys != null) {
-        if (generatedKeys.getResultSet() != null) {
-          generatedKeys.getResultSet().close();
+        ResultSet resultSet = generatedKeys.getResultSet();
+        if (resultSet != null) {
+          resultSet.close();
         }
-        generatedKeys = null;
+        this.generatedKeys = null;
       }
     }
   }
@@ -351,7 +374,7 @@ public class PgStatement implements Statement, BaseStatement {
    * @param cachedQuery to check (null if current query)
    * @return true if query is unlikely to be reused
    */
-  protected boolean isOneShotQuery(CachedQuery cachedQuery) {
+  protected boolean isOneShotQuery(/* @Nullable */ CachedQuery cachedQuery) {
     if (cachedQuery == null) {
       return true;
     }
@@ -363,7 +386,8 @@ public class PgStatement implements Statement, BaseStatement {
     return false;
   }
 
-  protected final void execute(CachedQuery cachedQuery, ParameterList queryParameters, int flags)
+  protected final void execute(CachedQuery cachedQuery,
+      /* @Nullable */ ParameterList queryParameters, int flags)
       throws SQLException {
     try {
       executeInternal(cachedQuery, queryParameters, flags);
@@ -379,7 +403,8 @@ public class PgStatement implements Statement, BaseStatement {
     }
   }
 
-  private void executeInternal(CachedQuery cachedQuery, ParameterList queryParameters, int flags)
+  private void executeInternal(CachedQuery cachedQuery,
+      /* @Nullable */ ParameterList queryParameters, int flags)
       throws SQLException {
     closeForNextExecution();
 
@@ -435,7 +460,7 @@ public class PgStatement implements Statement, BaseStatement {
           flags2);
       ResultWrapper result2 = handler2.getResults();
       if (result2 != null) {
-        result2.getResultSet().close();
+        castNonNull(result2.getResultSet(), "result2.getResultSet()").close();
       }
     }
 
@@ -452,11 +477,13 @@ public class PgStatement implements Statement, BaseStatement {
     }
     synchronized (this) {
       checkClosed();
-      result = firstUnclosedResult = handler.getResults();
+
+      ResultWrapper currentResult = handler.getResults();
+      result = firstUnclosedResult = currentResult;
 
       if (wantsGeneratedKeysOnce || wantsGeneratedKeysAlways) {
-        generatedKeys = result;
-        result = result.getNext();
+        generatedKeys = currentResult;
+        result = castNonNull(currentResult, "handler.getResults()").getNext();
 
         if (wantsGeneratedKeysOnce) {
           wantsGeneratedKeysOnce = false;
@@ -486,24 +513,7 @@ public class PgStatement implements Statement, BaseStatement {
   }
 
   public boolean getMoreResults() throws SQLException {
-    synchronized (this) {
-      checkClosed();
-      if (result == null) {
-        return false;
-      }
-
-      result = result.getNext();
-
-      // Close preceding resultsets.
-      while (firstUnclosedResult != result) {
-        if (firstUnclosedResult.getResultSet() != null) {
-          firstUnclosedResult.getResultSet().close();
-        }
-        firstUnclosedResult = firstUnclosedResult.getNext();
-      }
-
-      return (result != null && result.getResultSet() != null);
-    }
+    return getMoreResults(CLOSE_ALL_RESULTS);
   }
 
   public int getMaxRows() throws SQLException {
@@ -586,7 +596,7 @@ public class PgStatement implements Statement, BaseStatement {
     }
   }
 
-  public SQLWarning getWarnings() throws SQLException {
+  public /* @Nullable */ SQLWarning getWarnings() throws SQLException {
     checkClosed();
     //copy reference to avoid NPE from concurrent modification of this.warnings
     final PSQLWarningWrapper warnWrap = this.warnings;
@@ -620,7 +630,7 @@ public class PgStatement implements Statement, BaseStatement {
     warnings = null;
   }
 
-  public ResultSet getResultSet() throws SQLException {
+  public /* @Nullable */ ResultSet getResultSet() throws SQLException {
     synchronized (this) {
       checkClosed();
 
@@ -717,9 +727,13 @@ public class PgStatement implements Statement, BaseStatement {
   public void addBatch(String sql) throws SQLException {
     checkClosed();
 
+    ArrayList<Query> batchStatements = this.batchStatements;
     if (batchStatements == null) {
-      batchStatements = new ArrayList<Query>();
-      batchParameters = new ArrayList<ParameterList>();
+      this.batchStatements = batchStatements = new ArrayList<Query>();
+    }
+    ArrayList</* @Nullable */ ParameterList> batchParameters = this.batchParameters;
+    if (batchParameters == null) {
+      this.batchParameters = batchParameters = new ArrayList</* @Nullable */ ParameterList>();
     }
 
     // Simple statements should not replace ?, ? with $1, $2
@@ -733,23 +747,28 @@ public class PgStatement implements Statement, BaseStatement {
   public void clearBatch() throws SQLException {
     if (batchStatements != null) {
       batchStatements.clear();
+    }
+    if (batchParameters != null) {
       batchParameters.clear();
     }
   }
 
   protected BatchResultHandler createBatchHandler(Query[] queries,
-      ParameterList[] parameterLists) {
+      /* @Nullable */ ParameterList[] parameterLists) {
     return new BatchResultHandler(this, queries, parameterLists,
         wantsGeneratedKeysAlways);
   }
 
+  /* @RequiresNonNull({"batchStatements", "batchParameters"}) */
   private BatchResultHandler internalExecuteBatch() throws SQLException {
     // Construct query/parameter arrays.
     transformQueriesAndParameters();
+    ArrayList<Query> batchStatements = castNonNull(this.batchStatements);
+    ArrayList</* @Nullable */ ParameterList> batchParameters = castNonNull(this.batchParameters);
     // 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]);
+    /* @Nullable */ ParameterList[] parameterLists = batchParameters.toArray(new ParameterList[0]);
     batchStatements.clear();
     batchParameters.clear();
 
@@ -838,7 +857,7 @@ public class PgStatement implements Statement, BaseStatement {
       }
       ResultWrapper result2 = handler2.getResults();
       if (result2 != null) {
-        result2.getResultSet().close();
+        castNonNull(result2.getResultSet(), "result2.getResultSet()").close();
       }
     }
 
@@ -867,7 +886,7 @@ public class PgStatement implements Statement, BaseStatement {
     checkClosed();
     closeForNextExecution();
 
-    if (batchStatements == null || batchStatements.isEmpty()) {
+    if (batchStatements == null || batchStatements.isEmpty() || batchParameters == null) {
       return new int[0];
     }
 
@@ -923,7 +942,7 @@ public class PgStatement implements Statement, BaseStatement {
     }
   }
 
-  public void setFetchSize(int rows) throws SQLException {
+  public void setFetchSize(/* @NonNegative */ int rows) throws SQLException {
     checkClosed();
     if (rows < 0) {
       throw new PSQLException(GT.tr("Fetch size must be a value greater to or equal to 0."),
@@ -1046,7 +1065,7 @@ public class PgStatement implements Statement, BaseStatement {
     checkClosed();
     closeForNextExecution();
 
-    if (batchStatements == null || batchStatements.isEmpty()) {
+    if (batchStatements == null || batchStatements.isEmpty() || batchParameters == null) {
       return new long[0];
     }
 
@@ -1080,7 +1099,7 @@ public class PgStatement implements Statement, BaseStatement {
   }
 
   @Override
-  public long executeLargeUpdate(String sql, String[] columnNames) throws SQLException {
+  public long executeLargeUpdate(String sql, String /* @Nullable */ [] columnNames) throws SQLException {
     if (columnNames != null && columnNames.length == 0) {
       return executeLargeUpdate(sql);
     }
@@ -1134,7 +1153,8 @@ public class PgStatement implements Statement, BaseStatement {
     synchronized (this) {
       ResultWrapper result = firstUnclosedResult;
       while (result != null) {
-        if (result.getResultSet() != null && !result.getResultSet().isClosed()) {
+        ResultSet resultSet = result.getResultSet();
+        if (resultSet != null && !resultSet.isClosed()) {
           return;
         }
         result = result.getNext();
@@ -1168,12 +1188,7 @@ public class PgStatement implements Statement, BaseStatement {
       // CLOSE_ALL_RESULTS
       if (current == Statement.CLOSE_ALL_RESULTS) {
         // Close preceding resultsets.
-        while (firstUnclosedResult != result) {
-          if (firstUnclosedResult.getResultSet() != null) {
-            firstUnclosedResult.getResultSet().close();
-          }
-          firstUnclosedResult = firstUnclosedResult.getNext();
-        }
+        closeUnclosedResults();
       }
 
       // Done.
@@ -1209,7 +1224,7 @@ public class PgStatement implements Statement, BaseStatement {
         PSQLState.NOT_IMPLEMENTED);
   }
 
-  public int executeUpdate(String sql, String[] columnNames) throws SQLException {
+  public int executeUpdate(String sql, String /* @Nullable */ [] columnNames) throws SQLException {
     if (columnNames != null && columnNames.length == 0) {
       return executeUpdate(sql);
     }
@@ -1228,7 +1243,7 @@ public class PgStatement implements Statement, BaseStatement {
     return execute(sql, (String[]) null);
   }
 
-  public boolean execute(String sql, int[] columnIndexes) throws SQLException {
+  public boolean execute(String sql, int /* @Nullable */ [] columnIndexes) throws SQLException {
     if (columnIndexes != null && columnIndexes.length == 0) {
       return execute(sql);
     }
@@ -1237,7 +1252,7 @@ public class PgStatement implements Statement, BaseStatement {
         PSQLState.NOT_IMPLEMENTED);
   }
 
-  public boolean execute(String sql, String[] columnNames) throws SQLException {
+  public boolean execute(String sql, String /* @Nullable */ [] columnNames) throws SQLException {
     if (columnNames != null && columnNames.length == 0) {
       return execute(sql);
     }
diff --git a/src/main/java/org/postgresql/jdbc/PrimitiveArraySupport.java b/src/main/java/org/postgresql/jdbc/PrimitiveArraySupport.java
deleted file mode 100644
index b92f01be01b30205df9bf6225023d0921a9c9bd3..0000000000000000000000000000000000000000
--- a/src/main/java/org/postgresql/jdbc/PrimitiveArraySupport.java
+++ /dev/null
@@ -1,488 +0,0 @@
-/*
- * Copyright (c) 2004, PostgreSQL Global Development Group
- * See the LICENSE file in the project root for more information.
- */
-
-package org.postgresql.jdbc;
-
-import org.postgresql.core.Oid;
-import org.postgresql.core.TypeInfo;
-import org.postgresql.util.ByteConverter;
-
-import java.sql.Connection;
-import java.sql.SQLFeatureNotSupportedException;
-import java.util.HashMap;
-import java.util.Map;
-
-abstract class PrimitiveArraySupport<A> {
-
-  public abstract int getDefaultArrayTypeOid(TypeInfo tiCache);
-
-  public abstract String toArrayString(char delim, A array);
-
-  public abstract void appendArray(StringBuilder sb, char delim, A array);
-
-  public boolean supportBinaryRepresentation() {
-    return true;
-  }
-
-  public abstract byte[] toBinaryRepresentation(Connection connection, A array) throws SQLFeatureNotSupportedException;
-
-  private static final PrimitiveArraySupport<long[]> LONG_ARRAY = new PrimitiveArraySupport<long[]>() {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getDefaultArrayTypeOid(TypeInfo tiCache) {
-      return Oid.INT8_ARRAY;
-    }
-
-    @Override
-    public String toArrayString(char delim, long[] array) {
-      final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8));
-      appendArray(sb, delim, array);
-      return sb.toString();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void appendArray(StringBuilder sb, char delim, long[] array) {
-      sb.append('{');
-      for (int i = 0; i < array.length; ++i) {
-        if (i > 0) {
-          sb.append(delim);
-        }
-        sb.append(array[i]);
-      }
-      sb.append('}');
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public byte[] toBinaryRepresentation(Connection connection, long[] array) {
-
-      int length = 20 + (12 * array.length);
-      final byte[] bytes = new byte[length];
-
-      // 1 dimension
-      ByteConverter.int4(bytes, 0, 1);
-      // no null
-      ByteConverter.int4(bytes, 4, 0);
-      // oid
-      ByteConverter.int4(bytes, 8, Oid.INT8);
-      // length
-      ByteConverter.int4(bytes, 12, array.length);
-
-      int idx = 20;
-      for (int i = 0; i < array.length; ++i) {
-        bytes[idx + 3] = 8;
-        ByteConverter.int8(bytes, idx + 4, array[i]);
-        idx += 12;
-      }
-
-      return bytes;
-    }
-  };
-
-  private static final PrimitiveArraySupport<int[]> INT_ARRAY = new PrimitiveArraySupport<int[]>() {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getDefaultArrayTypeOid(TypeInfo tiCache) {
-      return Oid.INT4_ARRAY;
-    }
-
-    @Override
-    public String toArrayString(char delim, int[] array) {
-      final StringBuilder sb = new StringBuilder(Math.max(32, array.length * 6));
-      appendArray(sb, delim, array);
-      return sb.toString();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void appendArray(StringBuilder sb, char delim, int[] array) {
-      sb.append('{');
-      for (int i = 0; i < array.length; ++i) {
-        if (i > 0) {
-          sb.append(delim);
-        }
-        sb.append(array[i]);
-      }
-      sb.append('}');
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public byte[] toBinaryRepresentation(Connection connection, int[] array) {
-
-      int length = 20 + (8 * array.length);
-      final byte[] bytes = new byte[length];
-
-      // 1 dimension
-      ByteConverter.int4(bytes, 0, 1);
-      // no null
-      ByteConverter.int4(bytes, 4, 0);
-      // oid
-      ByteConverter.int4(bytes, 8, Oid.INT4);
-      // length
-      ByteConverter.int4(bytes, 12, array.length);
-
-      int idx = 20;
-      for (int i = 0; i < array.length; ++i) {
-        bytes[idx + 3] = 4;
-        ByteConverter.int4(bytes, idx + 4, array[i]);
-        idx += 8;
-      }
-
-      return bytes;
-    }
-  };
-
-  private static final PrimitiveArraySupport<short[]> SHORT_ARRAY = new PrimitiveArraySupport<short[]>() {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getDefaultArrayTypeOid(TypeInfo tiCache) {
-      return Oid.INT2_ARRAY;
-    }
-
-    @Override
-    public String toArrayString(char delim, short[] array) {
-      final StringBuilder sb = new StringBuilder(Math.max(32, array.length * 4));
-      appendArray(sb, delim, array);
-      return sb.toString();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void appendArray(StringBuilder sb, char delim, short[] array) {
-      sb.append('{');
-      for (int i = 0; i < array.length; ++i) {
-        if (i > 0) {
-          sb.append(delim);
-        }
-        sb.append(array[i]);
-      }
-      sb.append('}');
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public byte[] toBinaryRepresentation(Connection connection, short[] array) {
-
-      int length = 20 + (6 * array.length);
-      final byte[] bytes = new byte[length];
-
-      // 1 dimension
-      ByteConverter.int4(bytes, 0, 1);
-      // no null
-      ByteConverter.int4(bytes, 4, 0);
-      // oid
-      ByteConverter.int4(bytes, 8, Oid.INT2);
-      // length
-      ByteConverter.int4(bytes, 12, array.length);
-
-      int idx = 20;
-      for (int i = 0; i < array.length; ++i) {
-        bytes[idx + 3] = 2;
-        ByteConverter.int2(bytes, idx + 4, array[i]);
-        idx += 6;
-      }
-
-      return bytes;
-    }
-
-  };
-
-  private static final PrimitiveArraySupport<double[]> DOUBLE_ARRAY = new PrimitiveArraySupport<double[]>() {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getDefaultArrayTypeOid(TypeInfo tiCache) {
-      return Oid.FLOAT8_ARRAY;
-    }
-
-    @Override
-    public String toArrayString(char delim, double[] array) {
-      final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8));
-      appendArray(sb, delim, array);
-      return sb.toString();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void appendArray(StringBuilder sb, char delim, double[] array) {
-      sb.append('{');
-      for (int i = 0; i < array.length; ++i) {
-        if (i > 0) {
-          sb.append(delim);
-        }
-        // use quotes to account for any issues with scientific notation
-        sb.append('"');
-        sb.append(array[i]);
-        sb.append('"');
-      }
-      sb.append('}');
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public byte[] toBinaryRepresentation(Connection connection, double[] array) {
-
-      int length = 20 + (12 * array.length);
-      final byte[] bytes = new byte[length];
-
-      // 1 dimension
-      ByteConverter.int4(bytes, 0, 1);
-      // no null
-      ByteConverter.int4(bytes, 4, 0);
-      // oid
-      ByteConverter.int4(bytes, 8, Oid.FLOAT8);
-      // length
-      ByteConverter.int4(bytes, 12, array.length);
-
-      int idx = 20;
-      for (int i = 0; i < array.length; ++i) {
-        bytes[idx + 3] = 8;
-        ByteConverter.float8(bytes, idx + 4, array[i]);
-        idx += 12;
-      }
-
-      return bytes;
-    }
-
-  };
-
-  private static final PrimitiveArraySupport<float[]> FLOAT_ARRAY = new PrimitiveArraySupport<float[]>() {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getDefaultArrayTypeOid(TypeInfo tiCache) {
-      return Oid.FLOAT4_ARRAY;
-    }
-
-    @Override
-    public String toArrayString(char delim, float[] array) {
-      final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8));
-      appendArray(sb, delim, array);
-      return sb.toString();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void appendArray(StringBuilder sb, char delim, float[] array) {
-      sb.append('{');
-      for (int i = 0; i < array.length; ++i) {
-        if (i > 0) {
-          sb.append(delim);
-        }
-        // use quotes to account for any issues with scientific notation
-        sb.append('"');
-        sb.append(array[i]);
-        sb.append('"');
-      }
-      sb.append('}');
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public byte[] toBinaryRepresentation(Connection connection, float[] array) {
-
-      int length = 20 + (8 * array.length);
-      final byte[] bytes = new byte[length];
-
-      // 1 dimension
-      ByteConverter.int4(bytes, 0, 1);
-      // no null
-      ByteConverter.int4(bytes, 4, 0);
-      // oid
-      ByteConverter.int4(bytes, 8, Oid.FLOAT4);
-      // length
-      ByteConverter.int4(bytes, 12, array.length);
-
-      int idx = 20;
-      for (int i = 0; i < array.length; ++i) {
-        bytes[idx + 3] = 4;
-        ByteConverter.float4(bytes, idx + 4, array[i]);
-        idx += 8;
-      }
-
-      return bytes;
-    }
-
-  };
-
-  private static final PrimitiveArraySupport<boolean[]> BOOLEAN_ARRAY = new PrimitiveArraySupport<boolean[]>() {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getDefaultArrayTypeOid(TypeInfo tiCache) {
-      return Oid.BOOL_ARRAY;
-    }
-
-    @Override
-    public String toArrayString(char delim, boolean[] array) {
-      final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8));
-      appendArray(sb, delim, array);
-      return sb.toString();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void appendArray(StringBuilder sb, char delim, boolean[] array) {
-      sb.append('{');
-      for (int i = 0; i < array.length; ++i) {
-        if (i > 0) {
-          sb.append(delim);
-        }
-        sb.append(array[i] ? '1' : '0');
-      }
-      sb.append('}');
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @throws SQLFeatureNotSupportedException
-     *           Because this feature is not supported.
-     */
-    @Override
-    public byte[] toBinaryRepresentation(Connection connection, boolean[] array) throws SQLFeatureNotSupportedException {
-      int length = 20 + (5 * array.length);
-      final byte[] bytes = new byte[length];
-
-      // 1 dimension
-      ByteConverter.int4(bytes, 0, 1);
-      // no null
-      ByteConverter.int4(bytes, 4, 0);
-      // oid
-      ByteConverter.int4(bytes, 8, Oid.BOOL);
-      // length
-      ByteConverter.int4(bytes, 12, array.length);
-
-      int idx = 20;
-      for (int i = 0; i < array.length; ++i) {
-        bytes[idx + 3] = 1;
-        ByteConverter.bool(bytes, idx + 4, array[i]);
-        idx += 5;
-      }
-
-      return bytes;
-    }
-
-  };
-
-  private static final PrimitiveArraySupport<String[]> STRING_ARRAY = new PrimitiveArraySupport<String[]>() {
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public int getDefaultArrayTypeOid(TypeInfo tiCache) {
-      return Oid.VARCHAR_ARRAY;
-    }
-
-    @Override
-    public String toArrayString(char delim, String[] array) {
-      final StringBuilder sb = new StringBuilder(Math.max(64, array.length * 8));
-      appendArray(sb, delim, array);
-      return sb.toString();
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void appendArray(StringBuilder sb, char delim, String[] array) {
-      sb.append('{');
-      for (int i = 0; i < array.length; ++i) {
-        if (i > 0) {
-          sb.append(delim);
-        }
-        if (array[i] == null) {
-          sb.append('N');
-          sb.append('U');
-          sb.append('L');
-          sb.append('L');
-        } else {
-          PgArray.escapeArrayElement(sb, array[i]);
-        }
-      }
-      sb.append('}');
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public boolean supportBinaryRepresentation() {
-      return false;
-    }
-
-    /**
-     * {@inheritDoc}
-     *
-     * @throws SQLFeatureNotSupportedException
-     *           Because this feature is not supported.
-     */
-    @Override
-    public byte[] toBinaryRepresentation(Connection connection, String[] array) throws SQLFeatureNotSupportedException {
-      throw new SQLFeatureNotSupportedException();
-    }
-
-  };
-
-  private static final Map<Class, PrimitiveArraySupport> ARRAY_CLASS_TO_SUPPORT = new HashMap<Class, PrimitiveArraySupport>((int) (7 / .75) + 1);
-
-  static {
-    ARRAY_CLASS_TO_SUPPORT.put(long[].class, LONG_ARRAY);
-    ARRAY_CLASS_TO_SUPPORT.put(int[].class, INT_ARRAY);
-    ARRAY_CLASS_TO_SUPPORT.put(short[].class, SHORT_ARRAY);
-    ARRAY_CLASS_TO_SUPPORT.put(double[].class, DOUBLE_ARRAY);
-    ARRAY_CLASS_TO_SUPPORT.put(float[].class, FLOAT_ARRAY);
-    ARRAY_CLASS_TO_SUPPORT.put(boolean[].class, BOOLEAN_ARRAY);
-    ARRAY_CLASS_TO_SUPPORT.put(String[].class, STRING_ARRAY);
-  }
-
-  public static boolean isSupportedPrimitiveArray(Object obj) {
-    return obj != null && ARRAY_CLASS_TO_SUPPORT.containsKey(obj.getClass());
-  }
-
-  public static <A> PrimitiveArraySupport<A> getArraySupport(A array) {
-    return ARRAY_CLASS_TO_SUPPORT.get(array.getClass());
-  }
-}
diff --git a/src/main/java/org/postgresql/jdbc/ResultWrapper.java b/src/main/java/org/postgresql/jdbc/ResultWrapper.java
index df79ae7873c33ed373a23fa1efa15499ec6ffb59..3749169150a5088fe588d17334f7a99b12f562ab 100644
--- a/src/main/java/org/postgresql/jdbc/ResultWrapper.java
+++ b/src/main/java/org/postgresql/jdbc/ResultWrapper.java
@@ -6,6 +6,9 @@
 
 package org.postgresql.jdbc;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.dataflow.qual.Pure;
+
 import java.sql.ResultSet;
 
 /**
@@ -15,7 +18,7 @@ import java.sql.ResultSet;
  * @author Oliver Jowett (oliver@opencloud.com)
  */
 public class ResultWrapper {
-  public ResultWrapper(ResultSet rs) {
+  public ResultWrapper(/* @Nullable */ ResultSet rs) {
     this.rs = rs;
     this.updateCount = -1;
     this.insertOID = -1;
@@ -27,7 +30,8 @@ public class ResultWrapper {
     this.insertOID = insertOID;
   }
 
-  public ResultSet getResultSet() {
+  /* @Pure */
+  public /* @Nullable */ ResultSet getResultSet() {
     return rs;
   }
 
@@ -39,7 +43,7 @@ public class ResultWrapper {
     return insertOID;
   }
 
-  public ResultWrapper getNext() {
+  public /* @Nullable */ ResultWrapper getNext() {
     return next;
   }
 
@@ -52,8 +56,8 @@ public class ResultWrapper {
     tail.next = newResult;
   }
 
-  private final ResultSet rs;
+  private final /* @Nullable */ ResultSet rs;
   private final long updateCount;
   private final long insertOID;
-  private ResultWrapper next;
+  private /* @Nullable */ ResultWrapper next;
 }
diff --git a/src/main/java/org/postgresql/jdbc/TimestampUtils.java b/src/main/java/org/postgresql/jdbc/TimestampUtils.java
index 126b326fc104cf91033c4f7271aaf73e06ded5af..babbfd3ff34ff8148dac526b77bd417305d0246b 100644
--- a/src/main/java/org/postgresql/jdbc/TimestampUtils.java
+++ b/src/main/java/org/postgresql/jdbc/TimestampUtils.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGStatement;
 import org.postgresql.core.JavaVersion;
 import org.postgresql.core.Oid;
@@ -14,6 +16,9 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.checker.nullness.qual.PolyNull;
+
 import java.lang.reflect.Field;
 import java.sql.Date;
 import java.sql.SQLException;
@@ -50,10 +55,10 @@ public class TimestampUtils {
   private static final java.time.OffsetDateTime MIN_OFFSET_DATETIME = MIN_LOCAL_DATETIME.atOffset(java.time.ZoneOffset.UTC);
   //#endif
 
-  private static final Field DEFAULT_TIME_ZONE_FIELD;
+  private static final /* @Nullable */ Field DEFAULT_TIME_ZONE_FIELD;
 
-  private TimeZone prevDefaultZoneFieldValue;
-  private TimeZone defaultTimeZoneCache;
+  private /* @Nullable */ TimeZone prevDefaultZoneFieldValue;
+  private /* @Nullable */ TimeZone defaultTimeZoneCache;
 
   static {
     // The expected maximum value is 60 (seconds), so 64 is used "just in case"
@@ -94,6 +99,7 @@ public class TimestampUtils {
         tzField = TimeZone.class.getDeclaredField("defaultTimeZone");
         tzField.setAccessible(true);
         TimeZone defaultTz = TimeZone.getDefault();
+        @SuppressWarnings("nulllability")
         Object tzFromField = tzField.get(null);
         if (defaultTz == null || !defaultTz.equals(tzFromField)) {
           tzField = null;
@@ -112,7 +118,7 @@ public class TimestampUtils {
   private final Calendar calendarWithUserTz = new GregorianCalendar();
   private final TimeZone utcTz = TimeZone.getTimeZone("UTC");
 
-  private Calendar calCache;
+  private /* @Nullable */ Calendar calCache;
   private int calCacheZone;
 
   /**
@@ -166,11 +172,11 @@ public class TimestampUtils {
     int second = 0;
     int nanos = 0;
 
-    Calendar tz = null;
+    /* @Nullable */ Calendar tz = null;
   }
 
   private static class ParsedBinaryTimestamp {
-    Infinity infinity = null;
+    /* @Nullable */ Infinity infinity = null;
     long millis = 0;
     int nanos = 0;
   }
@@ -368,7 +374,8 @@ public class TimestampUtils {
    * @return null if s is null or a timestamp of the parsed string s.
    * @throws SQLException if there is a problem parsing s.
    */
-  public synchronized Timestamp toTimestamp(Calendar cal, String s) throws SQLException {
+  public synchronized /* @PolyNull */ Timestamp toTimestamp(/* @Nullable */ Calendar cal,
+      /* @PolyNull */ String s) throws SQLException {
     if (s == null) {
       return null;
     }
@@ -408,7 +415,7 @@ public class TimestampUtils {
    * @return null if s is null or a LocalTime of the parsed string s.
    * @throws SQLException if there is a problem parsing s.
    */
-  public java.time.LocalTime toLocalTime(String s) throws SQLException {
+  public java.time./* @PolyNull */ LocalTime toLocalTime(/* @PolyNull */ String s) throws SQLException {
     if (s == null) {
       return null;
     }
@@ -434,7 +441,7 @@ public class TimestampUtils {
    * @return null if s is null or a LocalDateTime of the parsed string s.
    * @throws SQLException if there is a problem parsing s.
    */
-  public java.time.LocalDateTime toLocalDateTime(String s) throws SQLException {
+  public java.time./* @PolyNull */ LocalDateTime toLocalDateTime(/* @PolyNull */ String s) throws SQLException {
     if (s == null) {
       return null;
     }
@@ -469,7 +476,8 @@ public class TimestampUtils {
    * @return null if s is null or a LocalDateTime of the parsed string s.
    * @throws SQLException if there is a problem parsing s.
    */
-  public java.time.OffsetDateTime toOffsetDateTime(String s) throws SQLException {
+  public java.time./* @PolyNull */ OffsetDateTime toOffsetDateTime(
+      /* @PolyNull */ String s) throws SQLException {
     if (s == null) {
       return null;
     }
@@ -540,7 +548,8 @@ public class TimestampUtils {
 
   //#endif
 
-  public synchronized Time toTime(Calendar cal, String s) throws SQLException {
+  public synchronized /* @PolyNull */ Time toTime(
+      /* @Nullable */ Calendar cal, /* @PolyNull */ String s) throws SQLException {
     // 1) Parse backend string
     if (s == null) {
       return null;
@@ -583,7 +592,8 @@ public class TimestampUtils {
     return convertToTime(timeMillis, useCal.getTimeZone());
   }
 
-  public synchronized Date toDate(Calendar cal, String s) throws SQLException {
+  public synchronized /* @PolyNull */ Date toDate(/* @Nullable */ Calendar cal,
+      /* @PolyNull */ String s) throws SQLException {
     // 1) Parse backend string
     Timestamp timestamp = toTimestamp(cal, s);
 
@@ -596,7 +606,7 @@ public class TimestampUtils {
     return convertToDate(timestamp.getTime(), cal == null ? null : cal.getTimeZone());
   }
 
-  private Calendar setupCalendar(Calendar cal) {
+  private Calendar setupCalendar(/* @Nullable */ Calendar cal) {
     TimeZone timeZone = cal == null ? null : cal.getTimeZone();
     return getSharedCalendar(timeZone);
   }
@@ -607,7 +617,7 @@ public class TimestampUtils {
    * @param timeZone time zone to be set for the calendar
    * @return The shared calendar.
    */
-  public Calendar getSharedCalendar(TimeZone timeZone) {
+  public Calendar getSharedCalendar(/* @Nullable */ TimeZone timeZone) {
     if (timeZone == null) {
       timeZone = getDefaultTz();
     }
@@ -626,11 +636,11 @@ public class TimestampUtils {
     return nanos % 1000 > 499;
   }
 
-  public synchronized String toString(Calendar cal, Timestamp x) {
+  public synchronized String toString(/* @Nullable */ Calendar cal, Timestamp x) {
     return toString(cal, x, true);
   }
 
-  public synchronized String toString(Calendar cal, Timestamp x,
+  public synchronized String toString(/* @Nullable */ Calendar cal, Timestamp x,
       boolean withTimeZone) {
     if (x.getTime() == PGStatement.DATE_POSITIVE_INFINITY) {
       return "infinity";
@@ -667,11 +677,11 @@ public class TimestampUtils {
     return sbuf.toString();
   }
 
-  public synchronized String toString(Calendar cal, Date x) {
+  public synchronized String toString(/* @Nullable */ Calendar cal, Date x) {
     return toString(cal, x, true);
   }
 
-  public synchronized String toString(Calendar cal, Date x,
+  public synchronized String toString(/* @Nullable */ Calendar cal, Date x,
       boolean withTimeZone) {
     if (x.getTime() == PGStatement.DATE_POSITIVE_INFINITY) {
       return "infinity";
@@ -694,11 +704,11 @@ public class TimestampUtils {
     return sbuf.toString();
   }
 
-  public synchronized String toString(Calendar cal, Time x) {
+  public synchronized String toString(/* @Nullable */ Calendar cal, Time x) {
     return toString(cal, x, true);
   }
 
-  public synchronized String toString(Calendar cal, Time x,
+  public synchronized String toString(/* @Nullable */ Calendar cal, Time x,
       boolean withTimeZone) {
     cal = setupCalendar(cal);
     cal.setTime(x);
@@ -976,7 +986,7 @@ public class TimestampUtils {
    * @return The parsed date object.
    * @throws PSQLException If binary format could not be parsed.
    */
-  public Date toDateBin(TimeZone tz, byte[] bytes) throws PSQLException {
+  public Date toDateBin(/* @Nullable */ TimeZone tz, byte[] bytes) throws PSQLException {
     if (bytes.length != 4) {
       throw new PSQLException(GT.tr("Unsupported binary encoding of {0}.", "date"),
           PSQLState.BAD_DATETIME_FORMAT);
@@ -1005,9 +1015,10 @@ public class TimestampUtils {
     // Fast path to getting the default timezone.
     if (DEFAULT_TIME_ZONE_FIELD != null) {
       try {
+        @SuppressWarnings("nullability")
         TimeZone defaultTimeZone = (TimeZone) DEFAULT_TIME_ZONE_FIELD.get(null);
         if (defaultTimeZone == prevDefaultZoneFieldValue) {
-          return defaultTimeZoneCache;
+          return castNonNull(defaultTimeZoneCache);
         }
         prevDefaultZoneFieldValue = defaultTimeZone;
       } catch (Exception e) {
@@ -1033,7 +1044,7 @@ public class TimestampUtils {
    * @return The parsed time object.
    * @throws PSQLException If binary format could not be parsed.
    */
-  public Time toTimeBin(TimeZone tz, byte[] bytes) throws PSQLException {
+  public Time toTimeBin(/* @Nullable */ TimeZone tz, byte[] bytes) throws PSQLException {
     if ((bytes.length != 8 && bytes.length != 12)) {
       throw new PSQLException(GT.tr("Unsupported binary encoding of {0}.", "time"),
           PSQLState.BAD_DATETIME_FORMAT);
@@ -1109,7 +1120,7 @@ public class TimestampUtils {
    * @return The parsed timestamp object.
    * @throws PSQLException If binary format could not be parsed.
    */
-  public Timestamp toTimestampBin(TimeZone tz, byte[] bytes, boolean timestamptz)
+  public Timestamp toTimestampBin(/* @Nullable */ TimeZone tz, byte[] bytes, boolean timestamptz)
       throws PSQLException {
 
     ParsedBinaryTimestamp parsedTimestamp = this.toParsedTimestampBin(tz, bytes, timestamptz);
@@ -1182,7 +1193,8 @@ public class TimestampUtils {
     return ts;
   }
 
-  private ParsedBinaryTimestamp toParsedTimestampBin(TimeZone tz, byte[] bytes, boolean timestamptz)
+  private ParsedBinaryTimestamp toParsedTimestampBin(/* @Nullable */ TimeZone tz, byte[] bytes,
+      boolean timestamptz)
       throws PSQLException {
 
     ParsedBinaryTimestamp ts = toParsedTimestampBinPlain(bytes);
@@ -1258,7 +1270,7 @@ public class TimestampUtils {
    * @param tz desired time zone
    * @return timestamp that would be rendered in {@code tz} like {@code millis} in UTC
    */
-  private long guessTimestamp(long millis, TimeZone tz) {
+  private long guessTimestamp(long millis, /* @Nullable */ TimeZone tz) {
     if (tz == null) {
       // If client did not provide us with time zone, we use system default time zone
       tz = getDefaultTz();
@@ -1325,7 +1337,7 @@ public class TimestampUtils {
    * @param tz The time zone of the date.
    * @return The extracted date.
    */
-  public Date convertToDate(long millis, TimeZone tz) {
+  public Date convertToDate(long millis, /* @Nullable */ TimeZone tz) {
 
     // no adjustments for the inifity hack values
     if (millis <= PGStatement.DATE_NEGATIVE_INFINITY
@@ -1482,7 +1494,7 @@ public class TimestampUtils {
    * @param value value
    * @throws PSQLException If binary format could not be parsed.
    */
-  public void toBinDate(TimeZone tz, byte[] bytes, Date value) throws PSQLException {
+  public void toBinDate(/* @Nullable */ TimeZone tz, byte[] bytes, Date value) throws PSQLException {
     long millis = value.getTime();
 
     if (tz == null) {
diff --git a/src/main/java/org/postgresql/jdbc/TypeInfoCache.java b/src/main/java/org/postgresql/jdbc/TypeInfoCache.java
index a423c3a106ad5fa23c0fe175a7fb281c51d26d7d..4460410df35c8d36e73072586aa8d8fe35757c68 100644
--- a/src/main/java/org/postgresql/jdbc/TypeInfoCache.java
+++ b/src/main/java/org/postgresql/jdbc/TypeInfoCache.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jdbc;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseConnection;
 import org.postgresql.core.BaseStatement;
 import org.postgresql.core.Oid;
@@ -16,6 +18,8 @@ import org.postgresql.util.PGobject;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -52,16 +56,16 @@ public class TypeInfoCache implements TypeInfo {
   // array type oid -> base type array element delimiter
   private Map<Integer, Character> arrayOidToDelimiter;
 
-  private BaseConnection conn;
+  private final BaseConnection conn;
   private final int unknownLength;
-  private PreparedStatement getOidStatementSimple;
-  private PreparedStatement getOidStatementComplexNonArray;
-  private PreparedStatement getOidStatementComplexArray;
-  private PreparedStatement getNameStatement;
-  private PreparedStatement getArrayElementOidStatement;
-  private PreparedStatement getArrayDelimiterStatement;
-  private PreparedStatement getTypeInfoStatement;
-  private PreparedStatement getAllTypeInfoStatement;
+  private /* @Nullable */ PreparedStatement getOidStatementSimple;
+  private /* @Nullable */ PreparedStatement getOidStatementComplexNonArray;
+  private /* @Nullable */ PreparedStatement getOidStatementComplexArray;
+  private /* @Nullable */ PreparedStatement getNameStatement;
+  private /* @Nullable */ PreparedStatement getArrayElementOidStatement;
+  private /* @Nullable */ PreparedStatement getArrayDelimiterStatement;
+  private /* @Nullable */ PreparedStatement getTypeInfoStatement;
+  private /* @Nullable */ PreparedStatement getAllTypeInfoStatement;
 
   // basic pg types info:
   // 0 - type name
@@ -116,6 +120,7 @@ public class TypeInfoCache implements TypeInfo {
     typeAliases.put("decimal", "numeric");
   }
 
+  @SuppressWarnings("method.invocation.invalid")
   public TypeInfoCache(BaseConnection conn, int unknownLength) {
     this.conn = conn;
     this.unknownLength = unknownLength;
@@ -229,19 +234,26 @@ public class TypeInfoCache implements TypeInfo {
     return type;
   }
 
-  public void cacheSQLTypes() throws SQLException {
-    LOGGER.log(Level.FINEST, "caching all SQL typecodes");
+  private PreparedStatement prepareGetAllTypeInfoStatement() throws SQLException {
+    PreparedStatement getAllTypeInfoStatement = this.getAllTypeInfoStatement;
     if (getAllTypeInfoStatement == null) {
       getAllTypeInfoStatement = conn.prepareStatement(getSQLTypeQuery(false));
+      this.getAllTypeInfoStatement = getAllTypeInfoStatement;
     }
+    return getAllTypeInfoStatement;
+  }
+
+  public void cacheSQLTypes() throws SQLException {
+    LOGGER.log(Level.FINEST, "caching all SQL typecodes");
+    PreparedStatement getAllTypeInfoStatement = prepareGetAllTypeInfoStatement();
     // Go through BaseStatement to avoid transaction start.
     if (!((BaseStatement) getAllTypeInfoStatement)
         .executeWithFlags(QueryExecutor.QUERY_SUPPRESS_BEGIN)) {
       throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
     }
-    ResultSet rs = getAllTypeInfoStatement.getResultSet();
+    ResultSet rs = castNonNull(getAllTypeInfoStatement.getResultSet());
     while (rs.next()) {
-      String typeName = rs.getString("typname");
+      String typeName = castNonNull(rs.getString("typname"));
       Integer type = getSQLTypeFromQueryResult(rs);
       if (!pgNameToSQLType.containsKey(typeName)) {
         pgNameToSQLType.put(typeName, type);
@@ -251,7 +263,16 @@ public class TypeInfoCache implements TypeInfo {
   }
 
   public int getSQLType(int oid) throws SQLException {
-    return getSQLType(getPGType(oid));
+    return getSQLType(castNonNull(getPGType(oid)));
+  }
+
+  private PreparedStatement prepareGetTypeInfoStatement() throws SQLException {
+    PreparedStatement getTypeInfoStatement = this.getTypeInfoStatement;
+    if (getTypeInfoStatement == null) {
+      getTypeInfoStatement = conn.prepareStatement(getSQLTypeQuery(true));
+      this.getTypeInfoStatement = getTypeInfoStatement;
+    }
+    return getTypeInfoStatement;
   }
 
   public synchronized int getSQLType(String pgTypeName) throws SQLException {
@@ -265,9 +286,7 @@ public class TypeInfoCache implements TypeInfo {
 
     LOGGER.log(Level.FINEST, "querying SQL typecode for pg type '{0}'", pgTypeName);
 
-    if (getTypeInfoStatement == null) {
-      getTypeInfoStatement = conn.prepareStatement(getSQLTypeQuery(true));
-    }
+    PreparedStatement getTypeInfoStatement = prepareGetTypeInfoStatement();
 
     getTypeInfoStatement.setString(1, pgTypeName);
 
@@ -277,9 +296,9 @@ public class TypeInfoCache implements TypeInfo {
       throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
     }
 
-    ResultSet rs = getTypeInfoStatement.getResultSet();
+    ResultSet rs = castNonNull(getTypeInfoStatement.getResultSet());
 
-    Integer type = Types.OTHER;
+    int type = Types.OTHER;
     if (rs.next()) {
       type = getSQLTypeFromQueryResult(rs);
     }
@@ -409,10 +428,10 @@ public class TypeInfoCache implements TypeInfo {
     }
 
     oid = Oid.UNSPECIFIED;
-    ResultSet rs = oidStatement.getResultSet();
+    ResultSet rs = castNonNull(oidStatement.getResultSet());
     if (rs.next()) {
       oid = (int) rs.getLong(1);
-      String internalName = rs.getString(2);
+      String internalName = castNonNull(rs.getString(2));
       oidToPgName.put(oid, internalName);
       pgNameToOid.put(internalName, oid);
     }
@@ -422,8 +441,9 @@ public class TypeInfoCache implements TypeInfo {
     return oid;
   }
 
-  public synchronized String getPGType(int oid) throws SQLException {
+  public synchronized /* @Nullable */ String getPGType(int oid) throws SQLException {
     if (oid == Oid.UNSPECIFIED) {
+      // TODO: it would be great to forbid UNSPECIFIED argument, and make the return type non-nullable
       return null;
     }
 
@@ -432,14 +452,7 @@ public class TypeInfoCache implements TypeInfo {
       return pgTypeName;
     }
 
-    if (getNameStatement == null) {
-      String sql;
-      sql = "SELECT n.nspname = ANY(current_schemas(true)), n.nspname, t.typname "
-            + "FROM pg_catalog.pg_type t "
-            + "JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = ?";
-
-      getNameStatement = conn.prepareStatement(sql);
-    }
+    PreparedStatement getNameStatement = prepareGetNameStatement();
 
     getNameStatement.setInt(1, oid);
 
@@ -448,11 +461,11 @@ public class TypeInfoCache implements TypeInfo {
       throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
     }
 
-    ResultSet rs = getNameStatement.getResultSet();
+    ResultSet rs = castNonNull(getNameStatement.getResultSet());
     if (rs.next()) {
       boolean onPath = rs.getBoolean(1);
-      String schema = rs.getString(2);
-      String name = rs.getString(3);
+      String schema = castNonNull(rs.getString(2), "schema");
+      String name = castNonNull(rs.getString(3), "name");
       if (onPath) {
         pgTypeName = name;
         pgNameToOid.put(schema + "." + name, oid);
@@ -474,6 +487,19 @@ public class TypeInfoCache implements TypeInfo {
     return pgTypeName;
   }
 
+  private PreparedStatement prepareGetNameStatement() throws SQLException {
+    PreparedStatement getNameStatement = this.getNameStatement;
+    if (getNameStatement == null) {
+      String sql;
+      sql = "SELECT n.nspname = ANY(current_schemas(true)), n.nspname, t.typname "
+            + "FROM pg_catalog.pg_type t "
+            + "JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = ?";
+
+      this.getNameStatement = getNameStatement = conn.prepareStatement(sql);
+    }
+    return getNameStatement;
+  }
+
   public int getPGArrayType(String elementTypeName) throws SQLException {
     elementTypeName = getTypeForAlias(elementTypeName);
     return getPGType(elementTypeName + "[]");
@@ -506,12 +532,7 @@ public class TypeInfoCache implements TypeInfo {
       return delim;
     }
 
-    if (getArrayDelimiterStatement == null) {
-      String sql;
-      sql = "SELECT e.typdelim FROM pg_catalog.pg_type t, pg_catalog.pg_type e "
-            + "WHERE t.oid = ? and t.typelem = e.oid";
-      getArrayDelimiterStatement = conn.prepareStatement(sql);
-    }
+    PreparedStatement getArrayDelimiterStatement = prepareGetArrayDelimiterStatement();
 
     getArrayDelimiterStatement.setInt(1, oid);
 
@@ -521,12 +542,12 @@ public class TypeInfoCache implements TypeInfo {
       throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
     }
 
-    ResultSet rs = getArrayDelimiterStatement.getResultSet();
+    ResultSet rs = castNonNull(getArrayDelimiterStatement.getResultSet());
     if (!rs.next()) {
       throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
     }
 
-    String s = rs.getString(1);
+    String s = castNonNull(rs.getString(1));
     delim = s.charAt(0);
 
     arrayOidToDelimiter.put(oid, delim);
@@ -536,6 +557,17 @@ public class TypeInfoCache implements TypeInfo {
     return delim;
   }
 
+  private PreparedStatement prepareGetArrayDelimiterStatement() throws SQLException {
+    PreparedStatement getArrayDelimiterStatement = this.getArrayDelimiterStatement;
+    if (getArrayDelimiterStatement == null) {
+      String sql;
+      sql = "SELECT e.typdelim FROM pg_catalog.pg_type t, pg_catalog.pg_type e "
+            + "WHERE t.oid = ? and t.typelem = e.oid";
+      this.getArrayDelimiterStatement = getArrayDelimiterStatement = conn.prepareStatement(sql);
+    }
+    return getArrayDelimiterStatement;
+  }
+
   public synchronized int getPGArrayElement(int oid) throws SQLException {
     if (oid == Oid.UNSPECIFIED) {
       return Oid.UNSPECIFIED;
@@ -547,13 +579,7 @@ public class TypeInfoCache implements TypeInfo {
       return pgType;
     }
 
-    if (getArrayElementOidStatement == null) {
-      String sql;
-      sql = "SELECT e.oid, n.nspname = ANY(current_schemas(true)), n.nspname, e.typname "
-            + "FROM pg_catalog.pg_type t JOIN pg_catalog.pg_type e ON t.typelem = e.oid "
-            + "JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = ?";
-      getArrayElementOidStatement = conn.prepareStatement(sql);
-    }
+    PreparedStatement getArrayElementOidStatement = prepareGetArrayElementOidStatement();
 
     getArrayElementOidStatement.setInt(1, oid);
 
@@ -563,7 +589,7 @@ public class TypeInfoCache implements TypeInfo {
       throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
     }
 
-    ResultSet rs = getArrayElementOidStatement.getResultSet();
+    ResultSet rs = castNonNull(getArrayElementOidStatement.getResultSet());
     if (!rs.next()) {
       throw new PSQLException(GT.tr("No results were returned by the query."), PSQLState.NO_DATA);
     }
@@ -571,7 +597,7 @@ public class TypeInfoCache implements TypeInfo {
     pgType = (int) rs.getLong(1);
     boolean onPath = rs.getBoolean(2);
     String schema = rs.getString(3);
-    String name = rs.getString(4);
+    String name = castNonNull(rs.getString(4));
     pgArrayToPgType.put(oid, pgType);
     pgNameToOid.put(schema + "." + name, pgType);
     String fullName = "\"" + schema + "\".\"" + name + "\"";
@@ -588,12 +614,30 @@ public class TypeInfoCache implements TypeInfo {
     return pgType;
   }
 
-  public synchronized Class<? extends PGobject> getPGobject(String type) {
+  private PreparedStatement prepareGetArrayElementOidStatement() throws SQLException {
+    PreparedStatement getArrayElementOidStatement = this.getArrayElementOidStatement;
+    if (getArrayElementOidStatement == null) {
+      String sql;
+      sql = "SELECT e.oid, n.nspname = ANY(current_schemas(true)), n.nspname, e.typname "
+            + "FROM pg_catalog.pg_type t JOIN pg_catalog.pg_type e ON t.typelem = e.oid "
+            + "JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = ?";
+      this.getArrayElementOidStatement = getArrayElementOidStatement = conn.prepareStatement(sql);
+    }
+    return getArrayElementOidStatement;
+  }
+
+  public synchronized /* @Nullable */ Class<? extends PGobject> getPGobject(String type) {
     return pgNameToPgObject.get(type);
   }
 
   public synchronized String getJavaClass(int oid) throws SQLException {
     String pgTypeName = getPGType(oid);
+    if (pgTypeName == null) {
+      // Technically speaking, we should not be here
+      // null result probably means oid == UNSPECIFIED which has no clear way
+      // to map to Java
+      return "java.lang.String";
+    }
 
     String result = pgNameToJavaClass.get(pgTypeName);
     if (result != null) {
@@ -605,7 +649,7 @@ public class TypeInfoCache implements TypeInfo {
       pgNameToJavaClass.put(pgTypeName, result);
     }
 
-    return result;
+    return result == null ? "java.lang.String" : result;
   }
 
   public String getTypeForAlias(String alias) {
diff --git a/src/main/java/org/postgresql/jdbc2/ArrayAssistantRegistry.java b/src/main/java/org/postgresql/jdbc2/ArrayAssistantRegistry.java
index 56f56a6ff89f912d1d7cefe6698be2ac4d20900b..4ef6000280e2dae29146243232e42c3ee59aa1b6 100644
--- a/src/main/java/org/postgresql/jdbc2/ArrayAssistantRegistry.java
+++ b/src/main/java/org/postgresql/jdbc2/ArrayAssistantRegistry.java
@@ -5,8 +5,10 @@
 
 package org.postgresql.jdbc2;
 
-import java.util.HashMap;
-import java.util.Map;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
 
 /**
  * Array assistants register here.
@@ -14,15 +16,14 @@ import java.util.Map;
  * @author Minglei Tu
  */
 public class ArrayAssistantRegistry {
-  private static Map<Integer, ArrayAssistant> arrayAssistantMap =
-      new HashMap<Integer, ArrayAssistant>();
+  private static final ConcurrentMap<Integer, ArrayAssistant> ARRAY_ASSISTANT_MAP =
+      new ConcurrentHashMap<Integer, ArrayAssistant>();
 
-  public static ArrayAssistant getAssistant(int oid) {
-    return arrayAssistantMap.get(oid);
+  public static /* @Nullable */ ArrayAssistant getAssistant(int oid) {
+    return ARRAY_ASSISTANT_MAP.get(oid);
   }
 
-  ////
   public static void register(int oid, ArrayAssistant assistant) {
-    arrayAssistantMap.put(oid, assistant);
+    ARRAY_ASSISTANT_MAP.put(oid, assistant);
   }
 }
diff --git a/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java b/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
index 2d973872c253dede1f37c557444f96721ecc045d..b86872b512ee264e249442fd756da7b9cb4d6d1d 100644
--- a/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
+++ b/src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.jre7.sasl;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.PGStream;
 import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
@@ -17,6 +19,7 @@ import com.ongres.scram.common.exception.ScramInvalidServerSignatureException;
 import com.ongres.scram.common.exception.ScramParseException;
 import com.ongres.scram.common.exception.ScramServerErrorException;
 import com.ongres.scram.common.stringprep.StringPreparations;
+// import org.checkerframework.checker.nullness.qual.Nullable;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -31,10 +34,9 @@ public class ScramAuthenticator {
   private final String user;
   private final String password;
   private final PGStream pgStream;
-  private ScramClient scramClient;
-  private ScramSession scramSession;
-  private ScramSession.ServerFirstProcessor serverFirstProcessor;
-  private ScramSession.ClientFinalProcessor clientFinalProcessor;
+  private /* @Nullable */ ScramClient scramClient;
+  private /* @Nullable */ ScramSession scramSession;
+  private /* @Nullable */ ScramSession.ClientFinalProcessor clientFinalProcessor;
 
   private interface BodySender {
     void sendBody(PGStream pgStream) throws IOException;
@@ -68,6 +70,7 @@ public class ScramAuthenticator {
       );
     }
 
+    ScramClient scramClient;
     try {
       scramClient = ScramClient
           .channelBinding(ScramClient.ChannelBinding.NO)
@@ -84,15 +87,18 @@ public class ScramAuthenticator {
       LOGGER.log(Level.FINEST, " Using SCRAM mechanism {0}", scramClient.getScramMechanism().getName());
     }
 
+    this.scramClient = scramClient;
     scramSession =
         scramClient.scramSession("*");   // Real username is ignored by server, uses startup one
   }
 
   public void sendScramClientFirstMessage() throws IOException {
-    String clientFirstMessage = scramSession.clientFirstMessage();
+    ScramSession scramSession = this.scramSession;
+    String clientFirstMessage = castNonNull(scramSession).clientFirstMessage();
     LOGGER.log(Level.FINEST, " FE=> SASLInitialResponse( {0} )", clientFirstMessage);
 
-    String scramMechanismName = scramClient.getScramMechanism().getName();
+    ScramClient scramClient = this.scramClient;
+    String scramMechanismName = castNonNull(scramClient).getScramMechanism().getName();
     final byte[] scramMechanismNameBytes = scramMechanismName.getBytes(StandardCharsets.UTF_8);
     final byte[] clientFirstMessageBytes = clientFirstMessage.getBytes(StandardCharsets.UTF_8);
     sendAuthenticationMessage(
@@ -113,6 +119,15 @@ public class ScramAuthenticator {
     String serverFirstMessage = pgStream.receiveString(length);
     LOGGER.log(Level.FINEST, " <=BE AuthenticationSASLContinue( {0} )", serverFirstMessage);
 
+    ScramSession scramSession = this.scramSession;
+    if (scramSession == null) {
+      throw new PSQLException(
+          GT.tr("SCRAM session does not exist"),
+          PSQLState.UNKNOWN_STATE
+      );
+    }
+
+    ScramSession.ServerFirstProcessor serverFirstProcessor;
     try {
       serverFirstProcessor = scramSession.receiveServerFirstMessage(serverFirstMessage);
     } catch (ScramException e) {
@@ -150,6 +165,13 @@ public class ScramAuthenticator {
     String serverFinalMessage = pgStream.receiveString(length);
     LOGGER.log(Level.FINEST, " <=BE AuthenticationSASLFinal( {0} )", serverFinalMessage);
 
+    ScramSession.ClientFinalProcessor clientFinalProcessor = this.clientFinalProcessor;
+    if (clientFinalProcessor == null) {
+      throw new PSQLException(
+          GT.tr("SCRAM client final processor does not exist"),
+          PSQLState.UNKNOWN_STATE
+      );
+    }
     try {
       clientFinalProcessor.receiveServerFinalMessage(serverFinalMessage);
     } catch (ScramParseException e) {
diff --git a/src/main/java/org/postgresql/largeobject/BlobInputStream.java b/src/main/java/org/postgresql/largeobject/BlobInputStream.java
index 5266cbc8720146388c9652a49e7d0bc48258bace..314da8ca401bf9c2fbbeb602399037e191385498 100644
--- a/src/main/java/org/postgresql/largeobject/BlobInputStream.java
+++ b/src/main/java/org/postgresql/largeobject/BlobInputStream.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.largeobject;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.sql.SQLException;
@@ -16,7 +18,7 @@ public class BlobInputStream extends InputStream {
   /**
    * The parent LargeObject.
    */
-  private LargeObject lo;
+  private /* @Nullable */ LargeObject lo;
 
   /**
    * The absolute position.
@@ -26,7 +28,7 @@ public class BlobInputStream extends InputStream {
   /**
    * Buffer used to improve performance.
    */
-  private byte[] buffer;
+  private byte /* @Nullable */ [] buffer;
 
   /**
    * Position within buffer.
@@ -82,7 +84,7 @@ public class BlobInputStream extends InputStream {
    * The minimum required to implement input stream.
    */
   public int read() throws java.io.IOException {
-    checkClosed();
+    LargeObject lo = getLo();
     try {
       if (limit > 0 && apos >= limit) {
         return -1;
@@ -93,7 +95,7 @@ public class BlobInputStream extends InputStream {
       }
 
       // Handle EOF
-      if (bpos >= buffer.length) {
+      if (buffer == null || bpos >= buffer.length) {
         return -1;
       }
 
@@ -162,7 +164,7 @@ public class BlobInputStream extends InputStream {
    * @see java.io.IOException
    */
   public synchronized void reset() throws IOException {
-    checkClosed();
+    LargeObject lo = getLo();
     try {
       if (mpos <= Integer.MAX_VALUE) {
         lo.seek((int)mpos);
@@ -189,9 +191,10 @@ public class BlobInputStream extends InputStream {
     return true;
   }
 
-  private void checkClosed() throws IOException {
+  private LargeObject getLo() throws IOException {
     if (lo == null) {
       throw new IOException("BlobOutputStream is closed");
     }
+    return lo;
   }
 }
diff --git a/src/main/java/org/postgresql/largeobject/BlobOutputStream.java b/src/main/java/org/postgresql/largeobject/BlobOutputStream.java
index dbfc11939cc09b6d53f59fb39be53eba9c1d7e24..ad81b43453fba40f4e1dfbd4003ecc3e1fb512dc 100644
--- a/src/main/java/org/postgresql/largeobject/BlobOutputStream.java
+++ b/src/main/java/org/postgresql/largeobject/BlobOutputStream.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.largeobject;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.io.OutputStream;
 import java.sql.SQLException;
@@ -16,7 +18,7 @@ public class BlobOutputStream extends OutputStream {
   /**
    * The parent LargeObject.
    */
-  private LargeObject lo;
+  private /* @Nullable */ LargeObject lo;
 
   /**
    * Buffer.
@@ -56,7 +58,7 @@ public class BlobOutputStream extends OutputStream {
   }
 
   public void write(int b) throws java.io.IOException {
-    checkClosed();
+    LargeObject lo = checkClosed();
     try {
       if (bpos >= bsize) {
         lo.write(buf);
@@ -69,7 +71,7 @@ public class BlobOutputStream extends OutputStream {
   }
 
   public void write(byte[] buf, int off, int len) throws java.io.IOException {
-    checkClosed();
+    LargeObject lo = checkClosed();
     try {
       // If we have any internally buffered data, send it first
       if (bpos > 0) {
@@ -95,7 +97,7 @@ public class BlobOutputStream extends OutputStream {
    * @throws IOException if an I/O error occurs.
    */
   public void flush() throws IOException {
-    checkClosed();
+    LargeObject lo = checkClosed();
     try {
       if (bpos > 0) {
         lo.write(buf, 0, bpos);
@@ -107,20 +109,22 @@ public class BlobOutputStream extends OutputStream {
   }
 
   public void close() throws IOException {
+    LargeObject lo = this.lo;
     if (lo != null) {
       try {
         flush();
         lo.close();
-        lo = null;
+        this.lo = null;
       } catch (SQLException se) {
         throw new IOException(se.toString());
       }
     }
   }
 
-  private void checkClosed() throws IOException {
+  private LargeObject checkClosed() throws IOException {
     if (lo == null) {
       throw new IOException("BlobOutputStream is closed");
     }
+    return lo;
   }
 }
diff --git a/src/main/java/org/postgresql/largeobject/LargeObject.java b/src/main/java/org/postgresql/largeobject/LargeObject.java
index 31d2ad4c348a6cd0af0203259a2021d7d63add72..9bcbe062e671af7226a2514fa24f3efe527b787a 100644
--- a/src/main/java/org/postgresql/largeobject/LargeObject.java
+++ b/src/main/java/org/postgresql/largeobject/LargeObject.java
@@ -5,12 +5,16 @@
 
 package org.postgresql.largeobject;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseConnection;
 import org.postgresql.fastpath.Fastpath;
 import org.postgresql.fastpath.FastpathArg;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
@@ -63,12 +67,12 @@ public class LargeObject
   private final int mode; // read/write mode of this object
   private final int fd; // the descriptor of the open large object
 
-  private BlobOutputStream os; // The current output stream
+  private /* @Nullable */ BlobOutputStream os; // The current output stream
 
   private boolean closed = false; // true when we are closed
 
-  private BaseConnection conn; // Only initialized when open a LOB with CommitOnClose
-  private boolean commitOnClose; // Only initialized when open a LOB with CommitOnClose
+  private /* @Nullable */ BaseConnection conn; // Only initialized when open a LOB with CommitOnClose
+  private final boolean commitOnClose; // Only initialized when open a LOB with CommitOnClose
 
   /**
    * <p>This opens a large object.</p>
@@ -84,7 +88,8 @@ public class LargeObject
    * @throws SQLException if a database-access error occurs.
    * @see org.postgresql.largeobject.LargeObjectManager
    */
-  protected LargeObject(Fastpath fp, long oid, int mode, BaseConnection conn, boolean commitOnClose)
+  protected LargeObject(Fastpath fp, long oid, int mode,
+      /* @Nullable */ BaseConnection conn, boolean commitOnClose)
       throws SQLException {
     this.fp = fp;
     this.oid = oid;
@@ -172,8 +177,9 @@ public class LargeObject
       args[0] = new FastpathArg(fd);
       fp.fastpath("lo_close", args); // true here as we dont care!!
       closed = true;
-      if (this.commitOnClose) {
-        this.conn.commit();
+      BaseConnection conn = this.conn;
+      if (this.commitOnClose && conn != null) {
+        conn.commit();
       }
     }
   }
@@ -191,7 +197,7 @@ public class LargeObject
     FastpathArg[] args = new FastpathArg[2];
     args[0] = new FastpathArg(fd);
     args[1] = new FastpathArg(len);
-    return fp.getData("loread", args);
+    return castNonNull(fp.getData("loread", args));
   }
 
   /**
@@ -205,6 +211,9 @@ public class LargeObject
    */
   public int read(byte[] buf, int off, int len) throws SQLException {
     byte[] b = read(len);
+    if (b == null) {
+      return 0;
+    }
     if (b.length < len) {
       len = b.length;
     }
diff --git a/src/main/java/org/postgresql/largeobject/LargeObjectManager.java b/src/main/java/org/postgresql/largeobject/LargeObjectManager.java
index bb7b156ce3f718423c8f1bf7607e04c37a60705d..12780968ac0cf600198d46b4bc0b3b58f19dc313 100644
--- a/src/main/java/org/postgresql/largeobject/LargeObjectManager.java
+++ b/src/main/java/org/postgresql/largeobject/LargeObjectManager.java
@@ -76,12 +76,6 @@ public class LargeObjectManager {
    */
   public static final int READWRITE = READ | WRITE;
 
-  /**
-   * This prevents us being created by mere mortals.
-   */
-  private LargeObjectManager() {
-  }
-
   /**
    * <p>Constructs the LargeObject API.</p>
    *
diff --git a/src/main/java/org/postgresql/package-info.java b/src/main/java/org/postgresql/package-info.java
new file mode 100644
index 0000000000000000000000000000000000000000..758855f5cea0b8fe1fca57a3cba2141e90169c70
--- /dev/null
+++ b/src/main/java/org/postgresql/package-info.java
@@ -0,0 +1,13 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+/* @DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.FIELD) */
+/* @DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.PARAMETER) */
+/* @DefaultQualifier(value = NonNull.class, locations = TypeUseLocation.RETURN) */
+package org.postgresql;
+
+// import org.checkerframework.checker.nullness.qual.NonNull;
+// import org.checkerframework.framework.qual.DefaultQualifier;
+// import org.checkerframework.framework.qual.TypeUseLocation;
diff --git a/src/main/java/org/postgresql/replication/LogSequenceNumber.java b/src/main/java/org/postgresql/replication/LogSequenceNumber.java
index 1886a7da29cc577a59a5c393f0929372f6f26e89..f1ed18ed2732f302f08a2eb02aa108f10671883c 100644
--- a/src/main/java/org/postgresql/replication/LogSequenceNumber.java
+++ b/src/main/java/org/postgresql/replication/LogSequenceNumber.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.replication;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.nio.ByteBuffer;
 
 /**
@@ -82,7 +84,7 @@ public final class LogSequenceNumber implements Comparable<LogSequenceNumber> {
   }
 
   @Override
-  public boolean equals(Object o) {
+  public boolean equals(/* @Nullable */ Object o) {
     if (this == o) {
       return true;
     }
diff --git a/src/main/java/org/postgresql/replication/PGReplicationStream.java b/src/main/java/org/postgresql/replication/PGReplicationStream.java
index 45c6c977ffb32c8a9fc49f346be82f7d311bb57f..774133d18d1c888168ddb3311cc6a63d1fe26f3b 100644
--- a/src/main/java/org/postgresql/replication/PGReplicationStream.java
+++ b/src/main/java/org/postgresql/replication/PGReplicationStream.java
@@ -8,6 +8,8 @@ package org.postgresql.replication;
 import org.postgresql.replication.fluent.CommonOptions;
 import org.postgresql.replication.fluent.logical.LogicalReplicationOptions;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.nio.ByteBuffer;
 import java.sql.SQLException;
 
@@ -37,7 +39,7 @@ public interface PGReplicationStream
    *     received byte array with use offset, so, use {@link ByteBuffer#array()} carefully
    * @throws SQLException when some internal exception occurs during read from stream
    */
-  ByteBuffer read() throws SQLException;
+  /* @Nullable */ ByteBuffer read() throws SQLException;
 
   /**
    * <p>Read next WAL record from backend. This method does not block and in contrast to {@link
@@ -55,7 +57,7 @@ public interface PGReplicationStream
    *     ByteBuffer#array()} carefully.
    * @throws SQLException when some internal exception occurs during read from stream
    */
-  ByteBuffer readPending() throws SQLException;
+  /* @Nullable */ ByteBuffer readPending() throws SQLException;
 
   /**
    * <p>Parameter updates by execute {@link PGReplicationStream#read()} method.</p>
diff --git a/src/main/java/org/postgresql/replication/ReplicationSlotInfo.java b/src/main/java/org/postgresql/replication/ReplicationSlotInfo.java
index 5a8bde420fda0cdb8c4bb4f43c346a70b1cb3e51..feb5f6f4b71b14aa379fc8a026f254e47b032513 100644
--- a/src/main/java/org/postgresql/replication/ReplicationSlotInfo.java
+++ b/src/main/java/org/postgresql/replication/ReplicationSlotInfo.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.replication;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Information returned on replication slot creation.
  *
@@ -15,17 +17,20 @@ package org.postgresql.replication;
  * <li><b>snapshot_name</b> String {@code =>} exported snapshot's name (may be <code>null</code>)
  * <li><b>output_plugin</b> String {@code =>} output plugin (may be <code>null</code>)
  * </ol>
+ *
+ * @see <a href="https://www.postgresql.org/docs/12/protocol-replication.html#PROTOCOL-REPLICATION-CREATE-SLOT">CREATE_REPLICATION_SLOT documentation</a>
  */
 public final class ReplicationSlotInfo {
 
   private final String slotName;
   private final ReplicationType replicationType;
   private final LogSequenceNumber consistentPoint;
-  private final String snapshotName;
-  private final String outputPlugin;
+  private final /* @Nullable */ String snapshotName;
+  private final /* @Nullable */ String outputPlugin;
 
   public ReplicationSlotInfo(String slotName, ReplicationType replicationType,
-      LogSequenceNumber consistentPoint, String snapshotName, String outputPlugin) {
+      LogSequenceNumber consistentPoint, /* @Nullable */ String snapshotName,
+      /* @Nullable */ String outputPlugin) {
     this.slotName = slotName;
     this.replicationType = replicationType;
     this.consistentPoint = consistentPoint;
@@ -69,7 +74,7 @@ public final class ReplicationSlotInfo {
    *
    * @return exported snapshot_name (may be <code>null</code>)
    */
-  public String getSnapshotName() {
+  public /* @Nullable */ String getSnapshotName() {
     return snapshotName;
   }
 
@@ -78,7 +83,7 @@ public final class ReplicationSlotInfo {
    *
    * @return output_plugin (may be <code>null</code>)
    */
-  public String getOutputPlugin() {
+  public /* @Nullable */ String getOutputPlugin() {
     return outputPlugin;
   }
 
diff --git a/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java b/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
index c55f65b8dd9da947a8a2f6b561f614494504d2b2..4d930e63b98ace9b457dabd8db18e1bc4b3ec6db 100644
--- a/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
@@ -9,12 +9,14 @@ import org.postgresql.core.BaseConnection;
 import org.postgresql.core.ServerVersion;
 import org.postgresql.util.GT;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLFeatureNotSupportedException;
 
 public abstract class AbstractCreateSlotBuilder<T extends ChainedCommonCreateSlotBuilder<T>>
     implements ChainedCommonCreateSlotBuilder<T> {
 
-  protected String slotName;
+  protected /* @Nullable */ String slotName;
   protected boolean temporaryOption = false;
   protected BaseConnection connection;
 
diff --git a/src/main/java/org/postgresql/replication/fluent/AbstractStreamBuilder.java b/src/main/java/org/postgresql/replication/fluent/AbstractStreamBuilder.java
index b93b00fd53a2cabad8cd387c1cc0b9222ae9c5eb..36c2bf5b230d63d30eb619cf8327f59809f69edb 100644
--- a/src/main/java/org/postgresql/replication/fluent/AbstractStreamBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/AbstractStreamBuilder.java
@@ -7,6 +7,8 @@ package org.postgresql.replication.fluent;
 
 import org.postgresql.replication.LogSequenceNumber;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.concurrent.TimeUnit;
 
 public abstract class AbstractStreamBuilder<T extends ChainedCommonStreamBuilder<T>>
@@ -14,7 +16,7 @@ public abstract class AbstractStreamBuilder<T extends ChainedCommonStreamBuilder
   private static final int DEFAULT_STATUS_INTERVAL = (int) TimeUnit.SECONDS.toMillis(10L);
   protected int statusIntervalMs = DEFAULT_STATUS_INTERVAL;
   protected LogSequenceNumber startPosition = LogSequenceNumber.INVALID_LSN;
-  protected String slotName;
+  protected /* @Nullable */ String slotName;
 
   protected abstract T self();
 
diff --git a/src/main/java/org/postgresql/replication/fluent/CommonOptions.java b/src/main/java/org/postgresql/replication/fluent/CommonOptions.java
index 6eacbee85d2f1887b8e6464cd60d0ee438be1dc4..ebd3ef38ead10eb209c452827cc6b5d2ba9bfd58 100644
--- a/src/main/java/org/postgresql/replication/fluent/CommonOptions.java
+++ b/src/main/java/org/postgresql/replication/fluent/CommonOptions.java
@@ -7,6 +7,8 @@ package org.postgresql.replication.fluent;
 
 import org.postgresql.replication.LogSequenceNumber;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * Common parameters for logical and physical replication.
  */
@@ -18,7 +20,7 @@ public interface CommonOptions {
    *
    * @return nullable replication slot name that already exists on server and free.
    */
-  String getSlotName();
+  /* @Nullable */ String getSlotName();
 
   /**
    * @return the position to start replication. This cannot be null.
diff --git a/src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java b/src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java
index 27004c8281e694e9424b0313863b3918a51bc288..9cc8614142e5f8dd7d1d54937a2c3ad90665fa7f 100644
--- a/src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java
@@ -5,11 +5,18 @@
 
 package org.postgresql.replication.fluent.logical;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseConnection;
 import org.postgresql.replication.LogSequenceNumber;
 import org.postgresql.replication.ReplicationSlotInfo;
 import org.postgresql.replication.ReplicationType;
 import org.postgresql.replication.fluent.AbstractCreateSlotBuilder;
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -19,7 +26,7 @@ public class LogicalCreateSlotBuilder
     extends AbstractCreateSlotBuilder<ChainedLogicalCreateSlotBuilder>
     implements ChainedLogicalCreateSlotBuilder {
 
-  private String outputPlugin;
+  private /* @Nullable */ String outputPlugin;
 
   public LogicalCreateSlotBuilder(BaseConnection connection) {
     super(connection);
@@ -38,6 +45,7 @@ public class LogicalCreateSlotBuilder
 
   @Override
   public ReplicationSlotInfo make() throws SQLException {
+    String outputPlugin = this.outputPlugin;
     if (outputPlugin == null || outputPlugin.isEmpty()) {
       throw new IllegalArgumentException(
           "OutputPlugin required parameter for logical replication slot");
@@ -51,20 +59,25 @@ public class LogicalCreateSlotBuilder
     ResultSet result = null;
     ReplicationSlotInfo slotInfo = null;
     try {
-      statement.execute(String.format(
+      String sql = String.format(
           "CREATE_REPLICATION_SLOT %s %s LOGICAL %s",
           slotName,
           temporaryOption ? "TEMPORARY" : "",
           outputPlugin
-      ));
+      );
+      statement.execute(sql);
       result = statement.getResultSet();
       if (result != null && result.next()) {
         slotInfo = new ReplicationSlotInfo(
-            result.getString("slot_name"),
+            castNonNull(result.getString("slot_name")),
             ReplicationType.LOGICAL,
-            LogSequenceNumber.valueOf(result.getString("consistent_point")),
+            LogSequenceNumber.valueOf(castNonNull(result.getString("consistent_point"))),
             result.getString("snapshot_name"),
             result.getString("output_plugin"));
+      } else {
+        throw new PSQLException(
+            GT.tr("{0} returned no results"),
+            PSQLState.OBJECT_NOT_IN_STATE);
       }
     } finally {
       if (result != null) {
diff --git a/src/main/java/org/postgresql/replication/fluent/logical/LogicalReplicationOptions.java b/src/main/java/org/postgresql/replication/fluent/logical/LogicalReplicationOptions.java
index 2355f610d07ab93cd4eee30bd5bf1712351303bd..7019e18520e4f2cc57a9a6b849839ea56100b5c5 100644
--- a/src/main/java/org/postgresql/replication/fluent/logical/LogicalReplicationOptions.java
+++ b/src/main/java/org/postgresql/replication/fluent/logical/LogicalReplicationOptions.java
@@ -7,6 +7,8 @@ package org.postgresql.replication.fluent.logical;
 
 import org.postgresql.replication.fluent.CommonOptions;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.Properties;
 
 public interface LogicalReplicationOptions extends CommonOptions {
@@ -15,7 +17,7 @@ public interface LogicalReplicationOptions extends CommonOptions {
    *
    * @return not null logical replication slot name that already exists on server and free.
    */
-  String getSlotName();
+  /* @Nullable */ String getSlotName();
 
   /**
    * Parameters for output plugin. Parameters will be set to output plugin that register for
diff --git a/src/main/java/org/postgresql/replication/fluent/logical/LogicalStreamBuilder.java b/src/main/java/org/postgresql/replication/fluent/logical/LogicalStreamBuilder.java
index dd6b0c312fdc7b98607ba46fb159c180d688be9a..e73d086f4868e1a8aa48be5cd6fa436aa320a0cb 100644
--- a/src/main/java/org/postgresql/replication/fluent/logical/LogicalStreamBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/logical/LogicalStreamBuilder.java
@@ -5,10 +5,14 @@
 
 package org.postgresql.replication.fluent.logical;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.replication.LogSequenceNumber;
 import org.postgresql.replication.PGReplicationStream;
 import org.postgresql.replication.fluent.AbstractStreamBuilder;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 import java.util.Properties;
 
@@ -38,7 +42,7 @@ public class LogicalStreamBuilder extends AbstractStreamBuilder<ChainedLogicalSt
   }
 
   @Override
-  public String getSlotName() {
+  public /* @Nullable */ String getSlotName() {
     return slotName;
   }
 
@@ -69,7 +73,7 @@ public class LogicalStreamBuilder extends AbstractStreamBuilder<ChainedLogicalSt
   @Override
   public ChainedLogicalStreamBuilder withSlotOptions(Properties options) {
     for (String propertyName : options.stringPropertyNames()) {
-      slotOptions.setProperty(propertyName, options.getProperty(propertyName));
+      slotOptions.setProperty(propertyName, castNonNull(options.getProperty(propertyName)));
     }
     return this;
   }
diff --git a/src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java b/src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java
index 45df2a1627b8e9b82598cffd2f32fdd01791dacb..f91837ec05b73802b8537aada798ca9a18ecb229 100644
--- a/src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java
@@ -5,11 +5,16 @@
 
 package org.postgresql.replication.fluent.physical;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.core.BaseConnection;
 import org.postgresql.replication.LogSequenceNumber;
 import org.postgresql.replication.ReplicationSlotInfo;
 import org.postgresql.replication.ReplicationType;
 import org.postgresql.replication.fluent.AbstractCreateSlotBuilder;
+import org.postgresql.util.GT;
+import org.postgresql.util.PSQLException;
+import org.postgresql.util.PSQLState;
 
 import java.sql.ResultSet;
 import java.sql.SQLException;
@@ -38,19 +43,24 @@ public class PhysicalCreateSlotBuilder
     ResultSet result = null;
     ReplicationSlotInfo slotInfo = null;
     try {
-      statement.execute(String.format(
+      String sql = String.format(
           "CREATE_REPLICATION_SLOT %s %s PHYSICAL",
           slotName,
           temporaryOption ? "TEMPORARY" : ""
-      ));
+      );
+      statement.execute(sql);
       result = statement.getResultSet();
       if (result != null && result.next()) {
         slotInfo = new ReplicationSlotInfo(
-            result.getString("slot_name"),
+            castNonNull(result.getString("slot_name")),
             ReplicationType.PHYSICAL,
-            LogSequenceNumber.valueOf(result.getString("consistent_point")),
+            LogSequenceNumber.valueOf(castNonNull(result.getString("consistent_point"))),
             result.getString("snapshot_name"),
             result.getString("output_plugin"));
+      } else {
+        throw new PSQLException(
+            GT.tr("{0} returned no results"),
+            PSQLState.OBJECT_NOT_IN_STATE);
       }
     } finally {
       if (result != null) {
diff --git a/src/main/java/org/postgresql/replication/fluent/physical/PhysicalStreamBuilder.java b/src/main/java/org/postgresql/replication/fluent/physical/PhysicalStreamBuilder.java
index eb177d084c19ba4a8f9b544e93187886663c6648..4382eb00becdcc74d5985019fa1917c154c8a49b 100644
--- a/src/main/java/org/postgresql/replication/fluent/physical/PhysicalStreamBuilder.java
+++ b/src/main/java/org/postgresql/replication/fluent/physical/PhysicalStreamBuilder.java
@@ -9,6 +9,8 @@ import org.postgresql.replication.LogSequenceNumber;
 import org.postgresql.replication.PGReplicationStream;
 import org.postgresql.replication.fluent.AbstractStreamBuilder;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 
 public class PhysicalStreamBuilder extends AbstractStreamBuilder<ChainedPhysicalStreamBuilder>
@@ -35,7 +37,7 @@ public class PhysicalStreamBuilder extends AbstractStreamBuilder<ChainedPhysical
   }
 
   @Override
-  public String getSlotName() {
+  public /* @Nullable */ String getSlotName() {
     return slotName;
   }
 
diff --git a/src/main/java/org/postgresql/ssl/DbKeyStoreSocketFactory.java b/src/main/java/org/postgresql/ssl/DbKeyStoreSocketFactory.java
index e7d9052b0397283143ade84d144e16320ea296c9..cb8131df70c649fadac96d72f1e37f622817bb49 100644
--- a/src/main/java/org/postgresql/ssl/DbKeyStoreSocketFactory.java
+++ b/src/main/java/org/postgresql/ssl/DbKeyStoreSocketFactory.java
@@ -20,11 +20,14 @@ public abstract class DbKeyStoreSocketFactory extends org.postgresql.ssl.Wrapped
    * certificate to send to the server, as well as checking the server's certificate against a set
    * of trusted CAs.
    */
+  @SuppressWarnings("nullness:method.invocation.invalid")
   public DbKeyStoreSocketFactory() throws DbKeyStoreSocketException {
     KeyStore keys;
     char[] password;
     try {
       keys = KeyStore.getInstance("JKS");
+      // Call of the sub-class method during object initialization is generally a bad idea
+      // Currently we suppress it with method.invocation.invalid
       password = getKeyStorePassword();
       keys.load(getKeyStoreStream(), password);
     } catch (java.security.GeneralSecurityException gse) {
diff --git a/src/main/java/org/postgresql/ssl/LazyKeyManager.java b/src/main/java/org/postgresql/ssl/LazyKeyManager.java
index 4bb874c45a8765a77ccdc5b650a9365192c2df7d..5ae37c4cc427c3d696fb3726486b6cb4920362a6 100644
--- a/src/main/java/org/postgresql/ssl/LazyKeyManager.java
+++ b/src/main/java/org/postgresql/ssl/LazyKeyManager.java
@@ -9,6 +9,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.IOException;
@@ -46,13 +48,13 @@ import javax.security.auth.x500.X500Principal;
  * A Key manager that only loads the keys, if necessary.
  */
 public class LazyKeyManager implements X509KeyManager {
-  private X509Certificate[] cert = null;
-  private PrivateKey key = null;
-  private String certfile;
-  private String keyfile;
-  private CallbackHandler cbh;
-  private boolean defaultfile;
-  private PSQLException error = null;
+  private X509Certificate /* @Nullable */ [] cert = null;
+  private /* @Nullable */ PrivateKey key = null;
+  private final /* @Nullable */ String certfile;
+  private final /* @Nullable */ String keyfile;
+  private final CallbackHandler cbh;
+  private final boolean defaultfile;
+  private /* @Nullable */ PSQLException error = null;
 
   /**
    * Constructor. certfile and keyfile can be null, in that case no certificate is presented to the
@@ -63,7 +65,7 @@ public class LazyKeyManager implements X509KeyManager {
    * @param cbh callback handler
    * @param defaultfile default file
    */
-  public LazyKeyManager(String certfile, String keyfile, CallbackHandler cbh, boolean defaultfile) {
+  public LazyKeyManager(/* @Nullable */ String certfile, /* @Nullable */ String keyfile, CallbackHandler cbh, boolean defaultfile) {
     this.certfile = certfile;
     this.keyfile = keyfile;
     this.cbh = cbh;
@@ -83,7 +85,8 @@ public class LazyKeyManager implements X509KeyManager {
   }
 
   @Override
-  public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {
+  public /* @Nullable */ String chooseClientAlias(String[] keyType,
+      Principal /* @Nullable */ [] issuers, /* @Nullable */ Socket socket) {
     if (certfile == null) {
       return null;
     } else {
@@ -113,12 +116,13 @@ public class LazyKeyManager implements X509KeyManager {
   }
 
   @Override
-  public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {
+  public /* @Nullable */ String chooseServerAlias(String keyType,
+      Principal /* @Nullable */ [] issuers, /* @Nullable */ Socket socket) {
     return null; // We are not a server
   }
 
   @Override
-  public X509Certificate[] getCertificateChain(String alias) {
+  public X509Certificate /* @Nullable */ [] getCertificateChain(String alias) {
     if (cert == null && certfile != null) {
       // If certfile is null, we do not load the certificate
       // The certificate must be loaded
@@ -134,8 +138,10 @@ public class LazyKeyManager implements X509KeyManager {
         return null;
       }
       Collection<? extends Certificate> certs;
+      FileInputStream certfileStream = null;
       try {
-        certs = cf.generateCertificates(new FileInputStream(certfile));
+        certfileStream = new FileInputStream(certfile);
+        certs = cf.generateCertificates(certfileStream);
       } catch (FileNotFoundException ioex) {
         if (!defaultfile) { // It is not an error if there is no file at the default location
           error = new PSQLException(
@@ -147,6 +153,18 @@ public class LazyKeyManager implements X509KeyManager {
         error = new PSQLException(GT.tr("Loading the SSL certificate {0} into a KeyManager failed.",
             certfile), PSQLState.CONNECTION_FAILURE, gsex);
         return null;
+      } finally {
+        if (certfileStream != null) {
+          try {
+            certfileStream.close();
+          } catch (IOException ioex) {
+            if (!defaultfile) { // It is not an error if there is no file at the default location
+              error = new PSQLException(
+                  GT.tr("Could not close SSL certificate file {0}.", certfile),
+                  PSQLState.CONNECTION_FAILURE, ioex);
+            }
+          }
+        }
       }
       cert = certs.toArray(new X509Certificate[0]);
     }
@@ -154,7 +172,8 @@ public class LazyKeyManager implements X509KeyManager {
   }
 
   @Override
-  public String[] getClientAliases(String keyType, Principal[] issuers) {
+  public String /* @Nullable */ [] getClientAliases(String keyType,
+      Principal /* @Nullable */ [] issuers) {
     String alias = chooseClientAlias(new String[]{keyType}, issuers, (Socket) null);
     return (alias == null ? new String[]{} : new String[]{alias});
   }
@@ -171,15 +190,14 @@ public class LazyKeyManager implements X509KeyManager {
   }
 
   @Override
-  public PrivateKey getPrivateKey(String alias) {
+  public /* @Nullable */ PrivateKey getPrivateKey(String alias) {
     try {
       if (key == null && keyfile != null) {
         // If keyfile is null, we do not load the key
         // The private key must be loaded
-        if (cert == null) { // We need the certificate for the algorithm
-          if (getCertificateChain("user") == null) {
-            return null; // getCertificateChain failed...
-          }
+        X509Certificate[] cert = getCertificateChain("user");
+        if (cert == null || cert.length == 0) { // We need the certificate for the algorithm
+          return null;
         }
 
         byte[] keydata;
@@ -259,7 +277,7 @@ public class LazyKeyManager implements X509KeyManager {
   }
 
   @Override
-  public String[] getServerAliases(String keyType, Principal[] issuers) {
+  public String /* @Nullable */ [] getServerAliases(String keyType, Principal /* @Nullable */ [] issuers) {
     return new String[]{};
   }
 }
diff --git a/src/main/java/org/postgresql/ssl/LibPQFactory.java b/src/main/java/org/postgresql/ssl/LibPQFactory.java
index 9256ffd760a8666c07cda0e8cc0b2b5e5d0e72f0..1339ccead8f1e7c37d34cc1aaf19f2cd45df64c1 100644
--- a/src/main/java/org/postgresql/ssl/LibPQFactory.java
+++ b/src/main/java/org/postgresql/ssl/LibPQFactory.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.ssl;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGProperty;
 import org.postgresql.jdbc.SslMode;
 import org.postgresql.ssl.NonValidatingFactory.NonValidatingTM;
@@ -13,6 +15,9 @@ import org.postgresql.util.ObjectFactory;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.initialization.qual.UnderInitialization;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Console;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
@@ -40,10 +45,14 @@ import javax.security.auth.callback.UnsupportedCallbackException;
  */
 public class LibPQFactory extends WrappedFactory {
 
-  KeyManager km;
+  /* @Nullable */ KeyManager km;
   boolean defaultfile;
 
-  private CallbackHandler getCallbackHandler(Properties info) throws PSQLException {
+  private CallbackHandler getCallbackHandler(
+      //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+      /* @UnderInitialization(WrappedFactory.class) */ LibPQFactory this,
+      //#endif
+      Properties info) throws PSQLException {
     // Determine the callback handler
     CallbackHandler cbh;
     String sslpasswordcallback = PGProperty.SSL_PASSWORD_CALLBACK.get(info);
@@ -62,7 +71,11 @@ public class LibPQFactory extends WrappedFactory {
     return cbh;
   }
 
-  private void initPk8(String sslkeyfile, String defaultdir, Properties info) throws  PSQLException {
+  private void initPk8(
+      //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+      /* @UnderInitialization(WrappedFactory.class) */ LibPQFactory this,
+      //#endif
+      String sslkeyfile, String defaultdir, Properties info) throws  PSQLException {
 
     // Load the client's certificate and key
     String sslcertfile = PGProperty.SSL_CERT.get(info);
@@ -76,7 +89,11 @@ public class LibPQFactory extends WrappedFactory {
       ("".equals(sslkeyfile) ? null : sslkeyfile), getCallbackHandler(info), defaultfile);
   }
 
-  private void initP12(String sslkeyfile, Properties info) throws PSQLException {
+  private void initP12(
+      //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.2"
+      /* @UnderInitialization(WrappedFactory.class) */ LibPQFactory this,
+      //#endif
+      String sslkeyfile, Properties info) throws PSQLException {
     km = new PKCS12KeyManager(sslkeyfile, getCallbackHandler(info));
   }
 
@@ -104,12 +121,11 @@ public class LibPQFactory extends WrappedFactory {
         defaultfile = true;
         sslkeyfile = defaultdir + "postgresql.pk8";
       }
-      if (sslkeyfile.endsWith("pk8")) {
-        initPk8(sslkeyfile, defaultdir, info);
-      }
 
-      if (sslkeyfile.endsWith("p12")) {
+      if (sslkeyfile.endsWith(".p12") || sslkeyfile.endsWith(".pfx")) {
         initP12(sslkeyfile, info);
+      } else {
+        initPk8(sslkeyfile, defaultdir, info);
       }
 
       TrustManager[] tm;
@@ -171,7 +187,8 @@ public class LibPQFactory extends WrappedFactory {
 
       // finally we can initialize the context
       try {
-        ctx.init(new KeyManager[]{km}, tm, null);
+        KeyManager km = this.km;
+        ctx.init(km == null ? null : new KeyManager[]{km}, tm, null);
       } catch (KeyManagementException ex) {
         throw new PSQLException(GT.tr("Could not initialize SSL context."),
             PSQLState.CONNECTION_FAILURE, ex);
@@ -206,9 +223,9 @@ public class LibPQFactory extends WrappedFactory {
    */
   public static class ConsoleCallbackHandler implements CallbackHandler {
 
-    private char[] password = null;
+    private char /* @Nullable */ [] password = null;
 
-    ConsoleCallbackHandler(String password) {
+    ConsoleCallbackHandler(/* @Nullable */ String password) {
       if (password != null) {
         this.password = password.toCharArray();
       }
@@ -224,6 +241,7 @@ public class LibPQFactory extends WrappedFactory {
     @Override
     public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
       Console cons = System.console();
+      char[] password = this.password;
       if (cons == null && password == null) {
         throw new UnsupportedCallbackException(callbacks[0], "Console is not available");
       }
@@ -238,7 +256,10 @@ public class LibPQFactory extends WrappedFactory {
         }
         // It is used instead of cons.readPassword(prompt), because the prompt may contain '%'
         // characters
-        pwdCallback.setPassword(cons.readPassword("%s", pwdCallback.getPrompt()));
+        pwdCallback.setPassword(
+            castNonNull(cons, "System.console()")
+                .readPassword("%s", pwdCallback.getPrompt())
+        );
       }
     }
   }
diff --git a/src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java b/src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java
index c81dcbeb2654ec3bd6df9a6145093e6265f017cd..20de49f4e68787364f49b270e8dc72a9ee29e2e3 100644
--- a/src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java
+++ b/src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java
@@ -7,6 +7,8 @@ package org.postgresql.ssl;
 
 import org.postgresql.util.GT;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.net.IDN;
 import java.security.cert.CertificateParsingException;
 import java.security.cert.X509Certificate;
@@ -146,7 +148,7 @@ public class PGjdbcHostnameVerifier implements HostnameVerifier {
         continue;
       }
       String san = (String) sanItem.get(1);
-      if (sanType == TYPE_IP_ADDRESS && san.startsWith("*")) {
+      if (sanType == TYPE_IP_ADDRESS && san != null && san.startsWith("*")) {
         // Wildcards should not be present in the IP Address field
         continue;
       }
@@ -220,7 +222,7 @@ public class PGjdbcHostnameVerifier implements HostnameVerifier {
     return result;
   }
 
-  public boolean verifyHostName(String hostname, String pattern) {
+  public boolean verifyHostName(/* @Nullable */ String hostname, /* @Nullable */ String pattern) {
     if (hostname == null || pattern == null) {
       return false;
     }
diff --git a/src/main/java/org/postgresql/ssl/PKCS12KeyManager.java b/src/main/java/org/postgresql/ssl/PKCS12KeyManager.java
index 00f2e0faa2b2d411782535934886e999a164ae5f..132ecfd3cd7269414ba97285d67b5ce6504ee761 100644
--- a/src/main/java/org/postgresql/ssl/PKCS12KeyManager.java
+++ b/src/main/java/org/postgresql/ssl/PKCS12KeyManager.java
@@ -9,6 +9,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.File;
 import java.io.FileInputStream;
 import java.net.Socket;
@@ -29,7 +31,7 @@ import javax.security.auth.x500.X500Principal;
 public class PKCS12KeyManager implements X509KeyManager {
 
   private final CallbackHandler cbh;
-  private PSQLException error = null;
+  private /* @Nullable */ PSQLException error = null;
   private final String keyfile;
   private final KeyStore keyStore;
   boolean keystoreLoaded = false;
@@ -59,13 +61,14 @@ public class PKCS12KeyManager implements X509KeyManager {
   }
 
   @Override
-  public String[] getClientAliases(String keyType, Principal[] principals) {
+  public String /* @Nullable */ [] getClientAliases(String keyType, Principal /* @Nullable */ [] principals) {
     String alias = chooseClientAlias(new String[]{keyType}, principals, (Socket) null);
-    return (alias == null ? new String[]{} : new String[]{alias});
+    return alias == null ? null : new String[]{alias};
   }
 
   @Override
-  public String chooseClientAlias(String[] strings, Principal[] principals, Socket socket) {
+  public /* @Nullable */ String chooseClientAlias(String[] strings, Principal /* @Nullable */ [] principals,
+      /* @Nullable */ Socket socket) {
     if (principals == null || principals.length == 0) {
       // Postgres 8.4 and earlier do not send the list of accepted certificate authorities
       // to the client. See BUG #5468. We only hope, that our certificate will be accepted.
@@ -91,28 +94,32 @@ public class PKCS12KeyManager implements X509KeyManager {
   }
 
   @Override
-  public String[] getServerAliases(String s, Principal[] principals) {
+  public String /* @Nullable */ [] getServerAliases(String s, Principal /* @Nullable */ [] principals) {
     return new String[]{};
   }
 
   @Override
-  public String chooseServerAlias(String s, Principal[] principals, Socket socket) {
+  public /* @Nullable */ String chooseServerAlias(String s, Principal /* @Nullable */ [] principals,
+      /* @Nullable */ Socket socket) {
     // we are not a server
     return null;
   }
 
   @Override
-  public X509Certificate[] getCertificateChain(String alias) {
+  public X509Certificate /* @Nullable */ [] getCertificateChain(String alias) {
     try {
       loadKeyStore();
-      Certificate []certs = keyStore.getCertificateChain(alias);
-      X509Certificate [] x509Certificates = new X509Certificate[certs.length];
+      Certificate[] certs = keyStore.getCertificateChain(alias);
+      if (certs == null) {
+        return null;
+      }
+      X509Certificate[] x509Certificates = new X509Certificate[certs.length];
       int i = 0;
       for (Certificate cert : certs) {
         x509Certificates[i++] = (X509Certificate)cert;
       }
       return x509Certificates;
-    } catch (Exception kse ) {
+    } catch (Exception kse) {
       error = new PSQLException(GT.tr(
         "Could not find a java cryptographic algorithm: X.509 CertificateFactory not available."),
         PSQLState.CONNECTION_FAILURE, kse);
@@ -121,7 +128,7 @@ public class PKCS12KeyManager implements X509KeyManager {
   }
 
   @Override
-  public PrivateKey getPrivateKey(String s) {
+  public /* @Nullable */ PrivateKey getPrivateKey(String s) {
     try {
       loadKeyStore();
       PasswordCallback pwdcb = new PasswordCallback(GT.tr("Enter SSL password: "), false);
@@ -130,6 +137,9 @@ public class PKCS12KeyManager implements X509KeyManager {
       KeyStore.ProtectionParameter protParam = new KeyStore.PasswordProtection(pwdcb.getPassword());
       KeyStore.PrivateKeyEntry pkEntry =
           (KeyStore.PrivateKeyEntry) keyStore.getEntry("user", protParam);
+      if (pkEntry == null) {
+        return null;
+      }
       PrivateKey myPrivateKey = pkEntry.getPrivateKey();
       return myPrivateKey;
     } catch (Exception ioex ) {
diff --git a/src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java b/src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java
index f643d410a3116bd7b7fd7af179c7a20cd72b3212..49a57a5c08f8f4b778a2fddc63bd683b5d083dbc 100644
--- a/src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java
+++ b/src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java
@@ -97,8 +97,26 @@ public class SingleCertValidatingFactory extends WrappedFactory {
         in = new BufferedInputStream(new FileInputStream(path));
       } else if (sslFactoryArg.startsWith(CLASSPATH_PREFIX)) {
         String path = sslFactoryArg.substring(CLASSPATH_PREFIX.length());
-        in = new BufferedInputStream(
-            Thread.currentThread().getContextClassLoader().getResourceAsStream(path));
+        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+        InputStream inputStream;
+        if (classLoader != null) {
+          inputStream = classLoader.getResourceAsStream(path);
+          if (inputStream == null) {
+            throw new IllegalArgumentException(
+                GT.tr("Unable to find resource {0} via Thread contextClassLoader {1}",
+                    path, classLoader)
+            );
+          }
+        } else {
+          inputStream = getClass().getResourceAsStream(path);
+          if (inputStream == null) {
+            throw new IllegalArgumentException(
+                GT.tr("Unable to find resource {0} via class {1} ClassLoader {2}",
+                    path, getClass(), getClass().getClassLoader())
+            );
+          }
+        }
+        in = new BufferedInputStream(inputStream);
       } else if (sslFactoryArg.startsWith(ENV_PREFIX)) {
         String name = sslFactoryArg.substring(ENV_PREFIX.length());
         String cert = System.getenv(name);
diff --git a/src/main/java/org/postgresql/ssl/WrappedFactory.java b/src/main/java/org/postgresql/ssl/WrappedFactory.java
index 2e3a0440da4b9f49b9ad642b5013f37167508554..0a983d6eda1febdae5eba5ad7bbb35d2e3d66d11 100644
--- a/src/main/java/org/postgresql/ssl/WrappedFactory.java
+++ b/src/main/java/org/postgresql/ssl/WrappedFactory.java
@@ -17,6 +17,9 @@ import javax.net.ssl.SSLSocketFactory;
  */
 public abstract class WrappedFactory extends SSLSocketFactory {
 
+  // The field is indeed not initialized in this class, however it is a part of public API,
+  // so it is hard to fix.
+  @SuppressWarnings("initialization.fields.uninitialized")
   protected SSLSocketFactory factory;
 
   public Socket createSocket(InetAddress host, int port) throws IOException {
diff --git a/src/main/java/org/postgresql/translation/cs.po b/src/main/java/org/postgresql/translation/cs.po
index 2517d76160ce96ab23ff94d91fe0c29fddba78ce..e5af30d1c2db4ba518bc13704bf39d1089d69663 100644
--- a/src/main/java/org/postgresql/translation/cs.po
+++ b/src/main/java/org/postgresql/translation/cs.po
@@ -17,7 +17,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/Driver.java:221
 msgid "Error loading default settings from driverconfig.properties"
-msgstr "Chyba na��t�n� standardn�ho nastaven� z driverconfig.properties"
+msgstr "Chyba na��t�n� standardn�ho nastaven� z driverconfig.properties"
 
 #: src/main/java/org/postgresql/Driver.java:233
 msgid "Properties for the driver contains a non-string value for the key "
@@ -36,22 +36,22 @@ msgid ""
 "Something unusual has occurred to cause the driver to fail. Please report "
 "this exception."
 msgstr ""
-"N�co neobvykl�ho p�inutilo ovlada� selhat. Pros�m nahlaste tuto vyj�mku."
+"N�co neobvykl�ho p�inutilo ovlada� selhat. Pros�m nahlaste tuto vyj�mku."
 
 #: src/main/java/org/postgresql/Driver.java:422
 #, fuzzy
 msgid "Connection attempt timed out."
-msgstr "Pokus o p�ipojen� selhal."
+msgstr "Pokus o p�ipojen� selhal."
 
 #: src/main/java/org/postgresql/Driver.java:435
 #, fuzzy
 msgid "Interrupted while attempting to connect."
-msgstr "Nastala chyba p�i nastaven� SSL spojen�."
+msgstr "Nastala chyba p�i nastaven� SSL spojen�."
 
 #: src/main/java/org/postgresql/Driver.java:691
 #, java-format
 msgid "Method {0} is not yet implemented."
-msgstr "Metoda {0} nen� implementov�na."
+msgstr "Metoda {0} nen� implementov�na."
 
 #: src/main/java/org/postgresql/PGProperty.java:818
 #: src/main/java/org/postgresql/PGProperty.java:838
@@ -83,7 +83,7 @@ msgstr ""
 #: src/main/java/org/postgresql/copy/PGCopyOutputStream.java:95
 #, fuzzy
 msgid "This copy stream is closed."
-msgstr "Tento ResultSet je uzav�en�."
+msgstr "Tento ResultSet je uzav�en�."
 
 #: src/main/java/org/postgresql/copy/PGCopyInputStream.java:109
 msgid "Read from copy failed."
@@ -102,7 +102,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/ConnectionFactory.java:57
 #, java-format
 msgid "A connection could not be made using the requested protocol {0}."
-msgstr "Spojen� nelze vytvo�it s pou�it�m ��dan�ho protokolu {0}."
+msgstr "Spojen� nelze vytvo�it s pou�it�m ��dan�ho protokolu {0}."
 
 #: src/main/java/org/postgresql/core/Oid.java:128
 #, java-format
@@ -155,21 +155,21 @@ msgstr ""
 #: src/main/java/org/postgresql/core/Parser.java:1069
 #, java-format
 msgid "Malformed function or procedure escape syntax at offset {0}."
-msgstr "Po�kozen� funkce nebo opu�t�n� procedury na pozici {0}."
+msgstr "Po�kozen� funkce nebo opu�t�n� procedury na pozici {0}."
 
 #: src/main/java/org/postgresql/core/SetupQueryRunner.java:64
 msgid "An unexpected result was returned by a query."
-msgstr "Obdr�en neo�ek�van� v�sledek dotazu."
+msgstr "Obdr�en neo�ek�van� v�sledek dotazu."
 
 #: src/main/java/org/postgresql/core/SocketFactoryFactory.java:43
 #, fuzzy, java-format
 msgid "The SocketFactory class provided {0} could not be instantiated."
-msgstr "T��da SSLSocketFactory poskytla {0} co� nem��e b�t instancionizov�no."
+msgstr "T��da SSLSocketFactory poskytla {0} co� nem��e b�t instancionizov�no."
 
 #: src/main/java/org/postgresql/core/SocketFactoryFactory.java:68
 #, java-format
 msgid "The SSLSocketFactory class provided {0} could not be instantiated."
-msgstr "T��da SSLSocketFactory poskytla {0} co� nem��e b�t instancionizov�no."
+msgstr "T��da SSLSocketFactory poskytla {0} co� nem��e b�t instancionizov�no."
 
 #: src/main/java/org/postgresql/core/Utils.java:93
 #: src/main/java/org/postgresql/core/Utils.java:110
@@ -193,12 +193,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSetMetaData.java:393
 #, java-format
 msgid "The column index is out of range: {0}, number of columns: {1}."
-msgstr "Index sloupece je mimo rozsah: {0}, po�et sloupc�: {1}."
+msgstr "Index sloupece je mimo rozsah: {0}, po�et sloupc�: {1}."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:162
 #, fuzzy, java-format
 msgid "Invalid targetServerType value: {0}"
-msgstr "Vadn� d�lka proudu {0}."
+msgstr "Vadn� d�lka proudu {0}."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:286
 #, fuzzy, java-format
@@ -206,13 +206,13 @@ msgid ""
 "Connection to {0} refused. Check that the hostname and port are correct and "
 "that the postmaster is accepting TCP/IP connections."
 msgstr ""
-"Spojen� odm�tnuto. Zkontrolujte zda je jm�no hosta a port spr�vn� a zda "
-"postmaster p�ij�m� TCP/IP spojen�."
+"Spojen� odm�tnuto. Zkontrolujte zda je jm�no hosta a port spr�vn� a zda "
+"postmaster p�ij�m� TCP/IP spojen�."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:297
 #: src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java:135
 msgid "The connection attempt failed."
-msgstr "Pokus o p�ipojen� selhal."
+msgstr "Pokus o p�ipojen� selhal."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:312
 #, java-format
@@ -226,14 +226,14 @@ msgstr "Server nepodporuje SSL."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:450
 msgid "An error occurred while setting up the SSL connection."
-msgstr "Nastala chyba p�i nastaven� SSL spojen�."
+msgstr "Nastala chyba p�i nastaven� SSL spojen�."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:546
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:573
 msgid ""
 "The server requested password-based authentication, but no password was "
 "provided."
-msgstr "Server vy�aduje ov��en� heslem, ale ��dn� nebylo posl�no."
+msgstr "Server vy�aduje ov��en� heslem, ale ��dn� nebylo posl�no."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:680
 msgid ""
@@ -248,9 +248,9 @@ msgid ""
 "the pg_hba.conf file to include the client''s IP address or subnet, and that "
 "it is using an authentication scheme supported by the driver."
 msgstr ""
-"Ov��en� typu {0} nen� podporov�no. Zkontrolujte zda konfigura�n� soubor "
-"pg_hba.conf obsahuje klientskou IP adresu �i pods�� a zda je pou�it� "
-"ov��enovac� sch�ma podporov�no ovlada�em."
+"Ov��en� typu {0} nen� podporov�no. Zkontrolujte zda konfigura�n� soubor "
+"pg_hba.conf obsahuje klientskou IP adresu �i pods� a zda je pou�it� "
+"ov��enovac� sch�ma podporov�no ovlada�em."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:713
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2628
@@ -259,7 +259,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2738
 #: src/main/java/org/postgresql/gss/GssAction.java:125
 msgid "Protocol error.  Session setup failed."
-msgstr "Chyba protokolu. Nastaven� relace selhalo."
+msgstr "Chyba protokolu. Nastaven� relace selhalo."
 
 #: src/main/java/org/postgresql/core/v3/CopyInImpl.java:54
 msgid "CopyIn copy direction can't receive data"
@@ -280,7 +280,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:201
 #, fuzzy
 msgid "Interrupted while waiting to obtain lock on database connection"
-msgstr "Nastala chyba p�i nastaven� SSL spojen�."
+msgstr "Nastala chyba p�i nastaven� SSL spojen�."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:333
 msgid "Unable to bind parameter values for statement."
@@ -294,7 +294,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2465
 #: src/main/java/org/postgresql/util/StreamWrapper.java:141
 msgid "An I/O error occurred while sending to the backend."
-msgstr "Vystupn�/v�stupn� chyba p�i odes�l�n� k backend."
+msgstr "Vystupn�/v�stupn� chyba p�i odes�l�n� k backend."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:391
 msgid "Error releasing savepoint"
@@ -304,29 +304,29 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:614
 #, java-format
 msgid "Expected command status BEGIN, got {0}."
-msgstr "O�ek�v�n p��kaz BEGIN, obdr�en {0}."
+msgstr "O�ek�v�n p��kaz BEGIN, obdr�en {0}."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:619
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1744
 #, java-format
 msgid "Unexpected command status: {0}."
-msgstr "Neo�ek�van� stav p��kazu: {0}."
+msgstr "Neo�ek�van� stav p��kazu: {0}."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:729
 #, fuzzy
 msgid "An error occurred while trying to get the socket timeout."
-msgstr "Vystupn�/v�stupn� chyba p�i odes�l�n� k backend."
+msgstr "Vystupn�/v�stupn� chyba p�i odes�l�n� k backend."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:764
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:840
 #, java-format
 msgid "Unknown Response Type {0}."
-msgstr "Nezn�m� typ odpov�di {0}."
+msgstr "Nezn�m� typ odpov�di {0}."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:787
 #, fuzzy
 msgid "An error occurred while trying to reset the socket timeout."
-msgstr "Vystupn�/v�stupn� chyba p�i odes�l�n� k backend."
+msgstr "Vystupn�/v�stupn� chyba p�i odes�l�n� k backend."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:885
 msgid "Database connection failed when starting copy"
@@ -380,7 +380,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1126
 #, fuzzy
 msgid "PGStream is closed"
-msgstr "Tento ResultSet je uzav�en�."
+msgstr "Tento ResultSet je uzav�en�."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1187
 #, java-format
@@ -409,7 +409,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1263
 #, fuzzy, java-format
 msgid "Unexpected copydata from server for {0}"
-msgstr "Neo�ek�van� stav p��kazu: {0}."
+msgstr "Neo�ek�van� stav p��kazu: {0}."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1323
 #, java-format
@@ -455,12 +455,12 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:270
 #, java-format
 msgid "No value specified for parameter {0}."
-msgstr "Nespecifikov�na hodnota parametru {0}."
+msgstr "Nespecifikov�na hodnota parametru {0}."
 
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:462
 #, fuzzy, java-format
 msgid "Added parameters index out of range: {0}, number of columns: {1}."
-msgstr "Index parametru mimo rozsah: {0}, po�et parametr� {1}."
+msgstr "Index parametru mimo rozsah: {0}, po�et parametr� {1}."
 
 #: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:147
 #, java-format
@@ -470,11 +470,11 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:272
 #, fuzzy
 msgid "This replication stream has been closed."
-msgstr "Spojeni bylo uzav�eno."
+msgstr "Spojeni bylo uzav�eno."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:118
 msgid "This PooledConnection has already been closed."
-msgstr "Tento PooledConnection byl uzav�en."
+msgstr "Tento PooledConnection byl uzav�en."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:314
 msgid ""
@@ -484,11 +484,11 @@ msgstr ""
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:315
 msgid "Connection has been closed."
-msgstr "Spojeni bylo uzav�eno."
+msgstr "Spojeni bylo uzav�eno."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:420
 msgid "Statement has been closed."
-msgstr "Statement byl uzav�en."
+msgstr "Statement byl uzav�en."
 
 #: src/main/java/org/postgresql/ds/PGPoolingDataSource.java:269
 msgid "Failed to setup DataSource."
@@ -496,13 +496,13 @@ msgstr ""
 
 #: src/main/java/org/postgresql/ds/PGPoolingDataSource.java:371
 msgid "DataSource has been closed."
-msgstr "DataSource byl uzav�en."
+msgstr "DataSource byl uzav�en."
 
 #: src/main/java/org/postgresql/ds/common/BaseDataSource.java:1270
 #: src/main/java/org/postgresql/ds/common/BaseDataSource.java:1280
 #, fuzzy, java-format
 msgid "Unsupported property name: {0}"
-msgstr "Nepodporovan� hodnota typu: {0}"
+msgstr "Nepodporovan� hodnota typu: {0}"
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:84
 #, java-format
@@ -524,7 +524,7 @@ msgstr ""
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:186
 #, fuzzy, java-format
 msgid "Fastpath call {0} - No result was returned and we expected a long."
-msgstr "Obdr�en v�sledek, ikdy� ��dn� nebyl o�ek�v�n."
+msgstr "Obdr�en v�sledek, ikdy� ��dn� nebyl o�ek�v�n."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:194
 #, java-format
@@ -577,7 +577,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:227
 msgid "LOB positioning offsets start at 1."
-msgstr "Za��tek pozicov�n� LOB za��na na 1."
+msgstr "Za��tek pozicov�n� LOB za��na na 1."
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:243
 msgid "free() was called on this LOB previously"
@@ -585,7 +585,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:95
 msgid "Too many update results were returned."
-msgstr "Bylo vr�ceno p��li� mnoho v�sledk� aktualizac�."
+msgstr "Bylo vr�ceno p��li� mnoho v�sledk� aktualizac�."
 
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:152
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:157
@@ -604,7 +604,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:168
 #, java-format
 msgid "{0} function takes four and only four argument."
-msgstr "Funkce {0} bere p�esn� �ty�i argumenty."
+msgstr "Funkce {0} bere p�esn� �ty�i argumenty."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:268
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:335
@@ -614,7 +614,7 @@ msgstr "Funkce {0} bere p
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:665
 #, java-format
 msgid "{0} function takes two and only two arguments."
-msgstr "Funkce {0} bere pr�v� dva argumenty."
+msgstr "Funkce {0} bere pr�v� dva argumenty."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:286
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:439
@@ -636,7 +636,7 @@ msgstr "Funkce {0} bere jeden argument."
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:308
 #, java-format
 msgid "{0} function takes two or three arguments."
-msgstr "Funkce {0} bere dva nebo t�i argumenty."
+msgstr "Funkce {0} bere dva nebo t�i argumenty."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:409
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:424
@@ -645,7 +645,7 @@ msgstr "Funkce {0} bere dva nebo t
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:645
 #, java-format
 msgid "{0} function doesn''t take any argument."
-msgstr "Funkce {0} nebere ��dn� argument."
+msgstr "Funkce {0} nebere ��dn� argument."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:584
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:636
@@ -653,7 +653,7 @@ msgstr "Funkce {0} nebere 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:571
 #, fuzzy, java-format
 msgid "{0} function takes three and only three arguments."
-msgstr "Funkce {0} bere pr�v� dva argumenty."
+msgstr "Funkce {0} bere pr�v� dva argumenty."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:597
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:618
@@ -667,21 +667,21 @@ msgstr "Funkce {0} bere pr
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:597
 #, fuzzy, java-format
 msgid "Interval {0} not yet implemented"
-msgstr "Metoda {0} nen� implementov�na."
+msgstr "Metoda {0} nen� implementov�na."
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:38
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:53
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:71
 msgid "Cannot reference a savepoint after it has been released."
-msgstr "Nemohu z�skat odkaz na savepoint, kdy� byl uvoln�n."
+msgstr "Nemohu z�skat odkaz na savepoint, kdy� byl uvoln�n."
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:43
 msgid "Cannot retrieve the id of a named savepoint."
-msgstr "Nemohu z�skat id nepojmenovan�ho savepointu."
+msgstr "Nemohu z�skat id nepojmenovan�ho savepointu."
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:58
 msgid "Cannot retrieve the name of an unnamed savepoint."
-msgstr "Nemohu z�skat n�zev nepojmenovan�ho savepointu."
+msgstr "Nemohu z�skat n�zev nepojmenovan�ho savepointu."
 
 #: src/main/java/org/postgresql/jdbc/PgArray.java:156
 #: src/main/java/org/postgresql/jdbc/PgArray.java:847
@@ -693,7 +693,7 @@ msgstr "Index pole mimo rozsah: {0}"
 #: src/main/java/org/postgresql/jdbc/PgArray.java:864
 #, java-format
 msgid "The array index is out of range: {0}, number of elements: {1}."
-msgstr "Index pole mimo rozsah: {0}, po�et prvk�: {1}."
+msgstr "Index pole mimo rozsah: {0}, po�et prvk�: {1}."
 
 #: src/main/java/org/postgresql/jdbc/PgArray.java:209
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1908
@@ -705,20 +705,20 @@ msgid ""
 "was created in.  The most common example of this is storing 8bit data in a "
 "SQL_ASCII database."
 msgstr ""
-"Nalezena vada ve znakov�ch datech. Toto m��e b�t zp�sobeno ulo�en�mi daty "
-"obsahuj�c�mi znaky, kter� jsou z�vadn� pro znakovou sadu nastavenou p�i "
-"zakl�d�n� datab�ze. Nejzn�mej�� p��klad je ukl�d�n� 8bitov�ch dat vSQL_ASCII "
-"datab�zi."
+"Nalezena vada ve znakov�ch datech. Toto m��e b�t zp�sobeno ulo�en�mi daty "
+"obsahuj�c�mi znaky, kter� jsou z�vadn� pro znakovou sadu nastavenou p�i "
+"zakl�d�n� datab�ze. Nejzn�mej�� p��klad je ukl�d�n� 8bitov�ch dat vSQL_ASCII "
+"datab�zi."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:85
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:95
 msgid "A CallableStatement was executed with nothing returned."
-msgstr "CallableStatement byl spu�t�n, le� nic nebylo vr�ceno."
+msgstr "CallableStatement byl spu�t�n, le� nic nebylo vr�ceno."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:106
 #, fuzzy
 msgid "A CallableStatement was executed with an invalid number of parameters"
-msgstr "CallableStatement byl spu�t�n, le� nic nebylo vr�ceno."
+msgstr "CallableStatement byl spu�t�n, le� nic nebylo vr�ceno."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:144
 #, java-format
@@ -763,12 +763,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:686
 #, fuzzy, java-format
 msgid "Unsupported type conversion to {1}."
-msgstr "Nepodporovan� hodnota typu: {0}"
+msgstr "Nepodporovan� hodnota typu: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:262
 #, fuzzy, java-format
 msgid "Unsupported value for stringtype parameter: {0}"
-msgstr "Nepodporovan� hodnota typu: {0}"
+msgstr "Nepodporovan� hodnota typu: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:463
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:104
@@ -782,13 +782,13 @@ msgstr "Nepodporovan
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:563
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:568
 msgid "No results were returned by the query."
-msgstr "Neobdr�en ��dn� v�sledek dotazu."
+msgstr "Neobdr�en ��dn� v�sledek dotazu."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:481
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:499
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:258
 msgid "A result was returned when none was expected."
-msgstr "Obdr�en v�sledek, ikdy� ��dn� nebyl o�ek�v�n."
+msgstr "Obdr�en v�sledek, ikdy� ��dn� nebyl o�ek�v�n."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:604
 msgid "Custom type maps are not supported."
@@ -797,12 +797,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:646
 #, java-format
 msgid "Failed to create object for: {0}."
-msgstr "Selhalo vytvo�en� objektu: {0}."
+msgstr "Selhalo vytvo�en� objektu: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:700
 #, java-format
 msgid "Unable to load the class {0} responsible for the datatype {1}"
-msgstr "Nemohu na��st t��du {0} odpov�dnou za typ {1}"
+msgstr "Nemohu na��st t��du {0} odpov�dnou za typ {1}"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:761
 msgid ""
@@ -818,7 +818,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1529
 #, fuzzy
 msgid "This connection has been closed."
-msgstr "Spojeni bylo uzav�eno."
+msgstr "Spojeni bylo uzav�eno."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:867
 msgid "Cannot rollback when autoCommit is enabled."
@@ -837,17 +837,17 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:975
 #, fuzzy
 msgid "Finalizing a Connection that was never closed:"
-msgstr "Spojeni bylo uzav�eno."
+msgstr "Spojeni bylo uzav�eno."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1042
 msgid "Unable to translate data into the desired encoding."
-msgstr "Nemohu p�elo�it data do po�adovan�ho k�dov�n�."
+msgstr "Nemohu p�elo�it data do po�adovan�ho k�dov�n�."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:929
 msgid "Fetch size must be a value greater to or equal to 0."
-msgstr "Nabran� velikost mus� b�t nez�porn�."
+msgstr "Nabran� velikost mus� b�t nez�porn�."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1425
@@ -858,12 +858,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1407
 #, fuzzy, java-format
 msgid "Invalid elements {0}"
-msgstr "Vadn� d�lka proudu {0}."
+msgstr "Vadn� d�lka proudu {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1443
 #, fuzzy, java-format
 msgid "Invalid timeout ({0}<0)."
-msgstr "Vadn� d�lka proudu {0}."
+msgstr "Vadn� d�lka proudu {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1473
 msgid "Validating connection."
@@ -872,12 +872,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1506
 #, fuzzy, java-format
 msgid "Failed to set ClientInfo property: {0}"
-msgstr "Selhalo vytvo�en� objektu: {0}."
+msgstr "Selhalo vytvo�en� objektu: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1516
 #, fuzzy
 msgid "ClientInfo property not supported."
-msgstr "Vr�cen� automaticky generovan�ch kl��� nen� podporov�no."
+msgstr "Vr�cen� automaticky generovan�ch kl��� nen� podporov�no."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1542
 msgid "One or more ClientInfo failed."
@@ -886,7 +886,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1642
 #, fuzzy
 msgid "Network timeout must be a value greater than or equal to 0."
-msgstr "�asov� limit dotazu mus� b�t nez�porn� ��slo."
+msgstr "�asov� limit dotazu mus� b�t nez�porn� ��slo."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1654
 msgid "Unable to set network timeout."
@@ -904,27 +904,27 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1700
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1721
 msgid "Cannot establish a savepoint in auto-commit mode."
-msgstr "Nemohu vytvo�it savepoint v auto-commit modu."
+msgstr "Nemohu vytvo�it savepoint v auto-commit modu."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1790
 msgid "Returning autogenerated keys is not supported."
-msgstr "Vr�cen� automaticky generovan�ch kl��� nen� podporov�no."
+msgstr "Vr�cen� automaticky generovan�ch kl��� nen� podporov�no."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:65
 #, fuzzy
 msgid ""
 "Unable to determine a value for MaxIndexKeys due to missing system catalog "
 "data."
-msgstr "Nemohu naj�t oid a oidvector typu v syst�mov�m katalogu."
+msgstr "Nemohu naj�t oid a oidvector typu v syst�mov�m katalogu."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:88
 msgid "Unable to find name datatype in the system catalogs."
-msgstr "Nemohu naj�t n�zev typu v syst�mov�m katalogu."
+msgstr "Nemohu naj�t n�zev typu v syst�mov�m katalogu."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:327
 #, fuzzy
 msgid "Unable to find keywords in the system catalogs."
-msgstr "Nemohu naj�t n�zev typu v syst�mov�m katalogu."
+msgstr "Nemohu naj�t n�zev typu v syst�mov�m katalogu."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1110
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:2819
@@ -973,7 +973,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgParameterMetaData.java:88
 #, java-format
 msgid "The parameter index is out of range: {0}, number of parameters: {1}."
-msgstr "Index parametru mimo rozsah: {0}, po�et parametr� {1}."
+msgstr "Index parametru mimo rozsah: {0}, po�et parametr� {1}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:90
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:113
@@ -985,7 +985,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:254
 msgid "Unknown Types value."
-msgstr "Nezn�m� hodnota typu."
+msgstr "Nezn�m� hodnota typu."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:385
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:442
@@ -993,24 +993,24 @@ msgstr "Nezn
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1497
 #, java-format
 msgid "Invalid stream length {0}."
-msgstr "Vadn� d�lka proudu {0}."
+msgstr "Vadn� d�lka proudu {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:414
 #, java-format
 msgid "The JVM claims not to support the {0} encoding."
-msgstr "JVM tvrd�, �e nepodporuje kodov�n� {0}."
+msgstr "JVM tvrd�, �e nepodporuje kodov�n� {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:417
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1124
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1156
 msgid "Provided InputStream failed."
-msgstr "Selhal poskytnut� InputStream."
+msgstr "Selhal poskytnut� InputStream."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:463
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1095
 #, java-format
 msgid "Unknown type {0}."
-msgstr "Nezn�m� typ {0}."
+msgstr "Nezn�m� typ {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:480
 msgid "No hstore extension installed."
@@ -1022,17 +1022,17 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:667
 #, java-format
 msgid "Cannot cast an instance of {0} to type {1}"
-msgstr "Nemohu p�etypovat instanci {0} na typ {1}"
+msgstr "Nemohu p�etypovat instanci {0} na typ {1}"
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:685
 #, java-format
 msgid "Unsupported Types value: {0}"
-msgstr "Nepodporovan� hodnota typu: {0}"
+msgstr "Nepodporovan� hodnota typu: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:897
 #, fuzzy, java-format
 msgid "Cannot convert an instance of {0} to type {1}"
-msgstr "Nemohu p�etypovat instanci {0} na typ {1}"
+msgstr "Nemohu p�etypovat instanci {0} na typ {1}"
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:973
 #, java-format
@@ -1044,12 +1044,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1132
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1233
 msgid "Unexpected error writing large object to database."
-msgstr "Neo�ek�van� chyba p�i zapisov�n� velk�ho objektu do datab�ze."
+msgstr "Neo�ek�van� chyba p�i zapisov�n� velk�ho objektu do datab�ze."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1177
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1206
 msgid "Provided Reader failed."
-msgstr "Selhal poskytnut� Reader."
+msgstr "Selhal poskytnut� Reader."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1456
 #: src/main/java/org/postgresql/util/StreamWrapper.java:56
@@ -1072,27 +1072,27 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3065
 #, fuzzy, java-format
 msgid "Cannot convert the column of type {0} to requested type {1}."
-msgstr "Nemohu p�etypovat instanci {0} na typ {1}"
+msgstr "Nemohu p�etypovat instanci {0} na typ {1}"
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:851
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:872
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1799
 msgid "Can''t use relative move methods while on the insert row."
-msgstr "Nem��ete pou��vat relativn� p�esuny p�i vkl�d�n� ��dku."
+msgstr "Nem��ete pou��vat relativn� p�esuny p�i vkl�d�n� ��dku."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:895
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:921
 #, java-format
 msgid "Invalid fetch direction constant: {0}."
-msgstr "�patn� sm�r �ten�: {0}."
+msgstr "�patn� sm�r �ten�: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:905
 msgid "Cannot call cancelRowUpdates() when on the insert row."
-msgstr "Nem��ete volat cancelRowUpdates() p�i vkl�d�n� ��dku."
+msgstr "Nem��ete volat cancelRowUpdates() p�i vkl�d�n� ��dku."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:920
 msgid "Cannot call deleteRow() when on the insert row."
-msgstr "Nem��ete volat deleteRow() p�i vkl�d�n� ��dku."
+msgstr "Nem��ete volat deleteRow() p�i vkl�d�n� ��dku."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:927
 msgid ""
@@ -1105,19 +1105,19 @@ msgid ""
 "Currently positioned after the end of the ResultSet.  You cannot call "
 "deleteRow() here."
 msgstr ""
-"Pr�v� jste za pozic� konce ResultSetu. Zde nem��ete volat deleteRow().s"
+"Pr�v� jste za pozic� konce ResultSetu. Zde nem��ete volat deleteRow().s"
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:937
 msgid "There are no rows in this ResultSet."
-msgstr "��dn� ��dek v ResultSet."
+msgstr "��dn� ��dek v ResultSet."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:974
 msgid "Not on the insert row."
-msgstr "Ne na vkl�dan�m ��dku."
+msgstr "Ne na vkl�dan�m ��dku."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:976
 msgid "You must specify at least one column value to insert a row."
-msgstr "Mus�te vyplnit alespo� jeden sloupec pro vlo�en� ��dku."
+msgstr "Mus�te vyplnit alespo� jeden sloupec pro vlo�en� ��dku."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1121
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1718
@@ -1125,15 +1125,15 @@ msgstr "Mus
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2439
 #, java-format
 msgid "The JVM claims not to support the encoding: {0}"
-msgstr "JVM tvrd�, �e nepodporuje kodov�n�: {0}"
+msgstr "JVM tvrd�, �e nepodporuje kodov�n�: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1248
 msgid "Can''t refresh the insert row."
-msgstr "Nemohu obnovit vkl�dan� ��dek."
+msgstr "Nemohu obnovit vkl�dan� ��dek."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1313
 msgid "Cannot call updateRow() when on the insert row."
-msgstr "Nemohu volat updateRow() na vlk�dan�m ��dku."
+msgstr "Nemohu volat updateRow() na vlk�dan�m ��dku."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1320
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3082
@@ -1144,12 +1144,12 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1496
 msgid "ResultSets with concurrency CONCUR_READ_ONLY cannot be updated."
-msgstr "ResultSets se soub�nost� CONCUR_READ_ONLY nem��e b�t aktualizov�no"
+msgstr "ResultSets se soub�nost� CONCUR_READ_ONLY nem��e b�t aktualizov�no"
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1564
 #, java-format
 msgid "No primary key found for table {0}."
-msgstr "Nenalezen prim�rn� kl�� pro tabulku {0}."
+msgstr "Nenalezen prim�rn� kl�� pro tabulku {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1995
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2000
@@ -1166,12 +1166,12 @@ msgstr "Nenalezen prim
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3070
 #, java-format
 msgid "Bad value for type {0} : {1}"
-msgstr "�patn� hodnota pro typ {0} : {1}"
+msgstr "�patn� hodnota pro typ {0} : {1}"
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2592
 #, java-format
 msgid "The column name {0} was not found in this ResultSet."
-msgstr "Sloupec pojmenovan� {0} nebyl nalezen v ResultSet."
+msgstr "Sloupec pojmenovan� {0} nebyl nalezen v ResultSet."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2728
 msgid ""
@@ -1179,13 +1179,13 @@ msgid ""
 "select only one table, and must select all primary keys from that table. See "
 "the JDBC 2.1 API Specification, section 5.6 for more details."
 msgstr ""
-"ResultSet nen� aktualizavateln�. Dotaz mus� vyb�rat pouze z jedn� tabulky a "
-"mus� obsahovat v�echny prim�rn� kl��e tabulky. Koukni do JDBC 2.1 API "
-"Specifikace, sekce 5.6 pro v�ce podrobnost�."
+"ResultSet nen� aktualizavateln�. Dotaz mus� vyb�rat pouze z jedn� tabulky a "
+"mus� obsahovat v�echny prim�rn� kl��e tabulky. Koukni do JDBC 2.1 API "
+"Specifikace, sekce 5.6 pro v�ce podrobnost�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2740
 msgid "This ResultSet is closed."
-msgstr "Tento ResultSet je uzav�en�."
+msgstr "Tento ResultSet je uzav�en�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2771
 msgid "ResultSet not positioned properly, perhaps you need to call next."
@@ -1238,7 +1238,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:202
 #, fuzzy
 msgid "Unable to create SAXResult for SQLXML."
-msgstr "Selhalo vytvo�en� objektu: {0}."
+msgstr "Selhalo vytvo�en� objektu: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:217
 msgid "Unable to create StAXResult for SQLXML"
@@ -1252,7 +1252,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:235
 #, fuzzy
 msgid "This SQLXML object has already been freed."
-msgstr "Tento PooledConnection byl uzav�en."
+msgstr "Tento PooledConnection byl uzav�en."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:244
 msgid ""
@@ -1277,7 +1277,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:237
 msgid "Multiple ResultSets were returned by the query."
-msgstr "V�cen�sobn� ResultSet byl vr�cen dotazem."
+msgstr "V�cen�sobn� ResultSet byl vr�cen dotazem."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:319
 msgid "Can''t use executeWithFlags(int) on a Statement."
@@ -1285,37 +1285,37 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:518
 msgid "Maximum number of rows must be a value grater than or equal to 0."
-msgstr "Maxim�ln� po�et ��dek mus� b�t nez�porn� ��slo."
+msgstr "Maxim�ln� po�et ��dek mus� b�t nez�porn� ��slo."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:564
 msgid "Query timeout must be a value greater than or equals to 0."
-msgstr "�asov� limit dotazu mus� b�t nez�porn� ��slo."
+msgstr "�asov� limit dotazu mus� b�t nez�porn� ��slo."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:606
 msgid "The maximum field size must be a value greater than or equal to 0."
-msgstr "Maxim�ln� velikost pole mus� b�t nez�porn� ��slo."
+msgstr "Maxim�ln� velikost pole mus� b�t nez�porn� ��slo."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:709
 msgid "This statement has been closed."
-msgstr "P��kaz byl uzav�en."
+msgstr "P��kaz byl uzav�en."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1078
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1208
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1236
 #, fuzzy
 msgid "Returning autogenerated keys by column index is not supported."
-msgstr "Vr�cen� automaticky generovan�ch kl��� nen� podporov�no."
+msgstr "Vr�cen� automaticky generovan�ch kl��� nen� podporov�no."
 
 #: src/main/java/org/postgresql/jdbc/SslMode.java:78
 #, fuzzy, java-format
 msgid "Invalid sslmode value: {0}"
-msgstr "Vadn� d�lka proudu {0}."
+msgstr "Vadn� d�lka proudu {0}."
 
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:356
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:424
 #, fuzzy, java-format
 msgid "Bad value for type timestamp/date/time: {1}"
-msgstr "�patn� hodnota pro typ {0} : {1}"
+msgstr "�patn� hodnota pro typ {0} : {1}"
 
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:981
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1038
@@ -1323,7 +1323,7 @@ msgstr "
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1131
 #, fuzzy, java-format
 msgid "Unsupported binary encoding of {0}."
-msgstr "Nepodporovan� hodnota typu: {0}"
+msgstr "Nepodporovan� hodnota typu: {0}"
 
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:244
 msgid "typname"
@@ -1340,12 +1340,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:120
 #, fuzzy, java-format
 msgid "Invalid server-first-message: {0}"
-msgstr "Vadn� d�lka proudu {0}."
+msgstr "Vadn� d�lka proudu {0}."
 
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:157
 #, fuzzy, java-format
 msgid "Invalid server-final-message: {0}"
-msgstr "Vadn� d�lka proudu {0}."
+msgstr "Vadn� d�lka proudu {0}."
 
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:163
 #, java-format
@@ -1359,12 +1359,12 @@ msgstr ""
 #: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:249
 #: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:290
 msgid "Large Objects may not be used in auto-commit mode."
-msgstr "Velk� objecky nemohou b�t pou�ity v auto-commit modu."
+msgstr "Velk� objecky nemohou b�t pou�ity v auto-commit modu."
 
 #: src/main/java/org/postgresql/osgi/PGDataSourceFactory.java:82
 #, fuzzy, java-format
 msgid "Unsupported properties: {0}"
-msgstr "Nepodporovan� hodnota typu: {0}"
+msgstr "Nepodporovan� hodnota typu: {0}"
 
 #: src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java:38
 msgid "Server does not support temporary replication slots"
@@ -1444,7 +1444,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/LibPQFactory.java:55
 #, fuzzy, java-format
 msgid "The password callback class provided {0} could not be instantiated."
-msgstr "T��da SSLSocketFactory poskytla {0} co� nem��e b�t instancionizov�no."
+msgstr "T��da SSLSocketFactory poskytla {0} co� nem��e b�t instancionizov�no."
 
 #: src/main/java/org/postgresql/ssl/LibPQFactory.java:140
 #, java-format
@@ -1473,7 +1473,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/MakeSSL.java:70
 #, fuzzy, java-format
 msgid "The HostnameVerifier class provided {0} could not be instantiated."
-msgstr "T��da SSLSocketFactory poskytla {0} co� nem��e b�t instancionizov�no."
+msgstr "T��da SSLSocketFactory poskytla {0} co� nem��e b�t instancionizov�no."
 
 #: src/main/java/org/postgresql/ssl/MakeSSL.java:81
 #, java-format
@@ -1488,7 +1488,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:90
 #, fuzzy, java-format
 msgid "No certificates found for hostname {0}"
-msgstr "Nenalezen prim�rn� kl�� pro tabulku {0}."
+msgstr "Nenalezen prim�rn� kl�� pro tabulku {0}."
 
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:109
 #, java-format
@@ -1536,7 +1536,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:44
 #, fuzzy
 msgid "Unable to find pkcs12 keystore."
-msgstr "Nemohu naj�t n�zev typu v syst�mov�m katalogu."
+msgstr "Nemohu naj�t n�zev typu v syst�mov�m katalogu."
 
 #: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:91
 msgid "The sslfactoryarg property may not be empty."
@@ -1563,7 +1563,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:134
 #, fuzzy
 msgid "An error occurred reading the certificate"
-msgstr "Nastala chyba p�i nastaven� SSL spojen�."
+msgstr "Nastala chyba p�i nastaven� SSL spojen�."
 
 #: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:167
 msgid "No X509TrustManager found"
@@ -1572,7 +1572,7 @@ msgstr ""
 #: src/main/java/org/postgresql/util/PGInterval.java:210
 #, fuzzy
 msgid "Conversion of interval failed"
-msgstr "P�evod pen�z selhal."
+msgstr "P�evod pen�z selhal."
 
 #: src/main/java/org/postgresql/util/PGPropertyMaxResultBufferParser.java:204
 #, java-format
@@ -1583,7 +1583,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/util/PGmoney.java:61
 msgid "Conversion of money failed."
-msgstr "P�evod pen�z selhal."
+msgstr "P�evod pen�z selhal."
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:43
 #, java-format
@@ -1626,7 +1626,7 @@ msgstr "Pozice: {0}"
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:217
 #, java-format
 msgid "Location: File: {0}, Routine: {1}, Line: {2}"
-msgstr "Poloha: Soubor: {0}, Rutina: {1}, ��dek: {2}"
+msgstr "Poloha: Soubor: {0}, Rutina: {1}, ��dek: {2}"
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:222
 #, java-format
@@ -1644,7 +1644,7 @@ msgstr ""
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:381
 #, fuzzy, java-format
 msgid "Invalid flags {0}"
-msgstr "Vadn� d�lka proudu {0}."
+msgstr "Vadn� d�lka proudu {0}."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:190
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:276
@@ -1767,4 +1767,4 @@ msgid "Heuristic commit/rollback not supported. forget xid={0}"
 msgstr ""
 
 #~ msgid "The driver currently does not support COPY operations."
-#~ msgstr "Ovlada� nyn� nepodporuje p��kaz COPY."
+#~ msgstr "Ovlada� nyn� nepodporuje p��kaz COPY."
diff --git a/src/main/java/org/postgresql/translation/de.po b/src/main/java/org/postgresql/translation/de.po
index 2d4b2350da9d47be7f7618ab06477c51bb28fe9a..6492344aff78355e066ad8f879139ede4276e3b0 100644
--- a/src/main/java/org/postgresql/translation/de.po
+++ b/src/main/java/org/postgresql/translation/de.po
@@ -34,8 +34,8 @@ msgid ""
 "server host and port that you wish to connect to."
 msgstr ""
 "Ihre Sicherheitsrichtlinie hat den Versuch des Verbindungsaufbaus "
-"verhindert. Sie m�ssen wahrscheinlich der Verbindung zum Datenbankrechner "
-"java.net.SocketPermission gew�hren, um den Rechner auf dem gew�hlten Port zu "
+"verhindert. Sie m�ssen wahrscheinlich der Verbindung zum Datenbankrechner "
+"java.net.SocketPermission gew�hren, um den Rechner auf dem gew�hlten Port zu "
 "erreichen."
 
 #: src/main/java/org/postgresql/Driver.java:282
@@ -44,12 +44,12 @@ msgid ""
 "Something unusual has occurred to cause the driver to fail. Please report "
 "this exception."
 msgstr ""
-"Etwas Ungew�hnliches ist passiert, das den Treiber fehlschlagen lie�. Bitte "
+"Etwas Ungew�hnliches ist passiert, das den Treiber fehlschlagen lie�. Bitte "
 "teilen Sie diesen Fehler mit."
 
 #: src/main/java/org/postgresql/Driver.java:422
 msgid "Connection attempt timed out."
-msgstr "Keine Verbindung innerhalb des Zeitintervalls m�glich."
+msgstr "Keine Verbindung innerhalb des Zeitintervalls m�glich."
 
 #: src/main/java/org/postgresql/Driver.java:435
 msgid "Interrupted while attempting to connect."
@@ -84,7 +84,7 @@ msgstr ""
 #: src/main/java/org/postgresql/copy/PGCopyInputStream.java:51
 #, fuzzy, java-format
 msgid "Copying from database failed: {0}"
-msgstr "Konnte �{0}� nicht in Typ �box� umwandeln"
+msgstr "Konnte �{0}� nicht in Typ �box� umwandeln"
 
 #: src/main/java/org/postgresql/copy/PGCopyInputStream.java:67
 #: src/main/java/org/postgresql/copy/PGCopyOutputStream.java:95
@@ -105,7 +105,7 @@ msgstr ""
 #, fuzzy, java-format
 msgid "Unable to parse the count in command completion tag: {0}."
 msgstr ""
-"Der Updatecount aus der Kommandovervollst�ndigungsmarkierung(?) {0} konnte "
+"Der Updatecount aus der Kommandovervollst�ndigungsmarkierung(?) {0} konnte "
 "nicht interpretiert werden."
 
 #: src/main/java/org/postgresql/core/ConnectionFactory.java:57
@@ -124,32 +124,32 @@ msgstr ""
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:129
 #, java-format
 msgid "Illegal UTF-8 sequence: initial byte is {0}: {1}"
-msgstr "Ung�ltige UTF-8-Sequenz: das erste Byte ist {0}: {1}"
+msgstr "Ung�ltige UTF-8-Sequenz: das erste Byte ist {0}: {1}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:135
 #, java-format
 msgid "Illegal UTF-8 sequence: final value is out of range: {0}"
 msgstr ""
-"Ung�ltige UTF-8-Sequenz: Der letzte Wert ist au�erhalb des zul�ssigen "
+"Ung�ltige UTF-8-Sequenz: Der letzte Wert ist au�erhalb des zul�ssigen "
 "Bereichs: {0}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:148
 #, java-format
 msgid "Illegal UTF-8 sequence: final value is a surrogate value: {0}"
-msgstr "Ung�ltige UTF-8-Sequenz: der letzte Wert ist ein Ersatzwert: {0}"
+msgstr "Ung�ltige UTF-8-Sequenz: der letzte Wert ist ein Ersatzwert: {0}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:164
 #, java-format
 msgid ""
 "Illegal UTF-8 sequence: byte {0} of {1} byte sequence is not 10xxxxxx: {2}"
 msgstr ""
-"Ung�ltige UTF-8-Sequenz: Byte {0} der {1} Bytesequenz ist nicht 10xxxxxx: {2}"
+"Ung�ltige UTF-8-Sequenz: Byte {0} der {1} Bytesequenz ist nicht 10xxxxxx: {2}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:200
 #, java-format
 msgid "Illegal UTF-8 sequence: {0} bytes used to encode a {1} byte value: {2}"
 msgstr ""
-"Ung�ltige UTF-8-Sequenz: {0} Bytes wurden verwendet um einen {1} Bytewert zu "
+"Ung�ltige UTF-8-Sequenz: {0} Bytes wurden verwendet um einen {1} Bytewert zu "
 "kodieren: {2}"
 
 #: src/main/java/org/postgresql/core/PGStream.java:565
@@ -174,7 +174,7 @@ msgstr ""
 #, java-format
 msgid "Malformed function or procedure escape syntax at offset {0}."
 msgstr ""
-"Unzul�ssige Syntax f�r ein Funktions- oder Prozedur-Escape an Offset {0}."
+"Unzul�ssige Syntax f�r ein Funktions- oder Prozedur-Escape an Offset {0}."
 
 #: src/main/java/org/postgresql/core/SetupQueryRunner.java:64
 msgid "An unexpected result was returned by a query."
@@ -197,7 +197,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/Utils.java:93
 #: src/main/java/org/postgresql/core/Utils.java:110
 msgid "Zero bytes may not occur in string parameters."
-msgstr "Stringparameter d�rfen keine Nullbytes enthalten."
+msgstr "Stringparameter d�rfen keine Nullbytes enthalten."
 
 #: src/main/java/org/postgresql/core/Utils.java:120
 #: src/main/java/org/postgresql/core/Utils.java:170
@@ -206,7 +206,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/core/Utils.java:159
 msgid "Zero bytes may not occur in identifiers."
-msgstr "Nullbytes d�rfen in Bezeichnern nicht vorkommen."
+msgstr "Nullbytes d�rfen in Bezeichnern nicht vorkommen."
 
 #: src/main/java/org/postgresql/core/v3/CompositeParameterList.java:34
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:54
@@ -217,13 +217,13 @@ msgstr "Nullbytes d
 #, java-format
 msgid "The column index is out of range: {0}, number of columns: {1}."
 msgstr ""
-"Der Spaltenindex {0} ist au�erhalb des g�ltigen Bereichs. Anzahl Spalten: "
+"Der Spaltenindex {0} ist au�erhalb des g�ltigen Bereichs. Anzahl Spalten: "
 "{1}."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:162
 #, fuzzy, java-format
 msgid "Invalid targetServerType value: {0}"
-msgstr "Ung�ltige L�nge des Datenstroms: {0}."
+msgstr "Ung�ltige L�nge des Datenstroms: {0}."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:286
 #, fuzzy, java-format
@@ -231,7 +231,7 @@ msgid ""
 "Connection to {0} refused. Check that the hostname and port are correct and "
 "that the postmaster is accepting TCP/IP connections."
 msgstr ""
-"Verbindung verweigert. �berpr�fen Sie die Korrektheit von Hostnamen und der "
+"Verbindung verweigert. �berpr�fen Sie die Korrektheit von Hostnamen und der "
 "Portnummer und dass der Datenbankserver TCP/IP-Verbindungen annimmt."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:297
@@ -247,7 +247,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:423
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:436
 msgid "The server does not support SSL."
-msgstr "Der Server unterst�tzt SSL nicht."
+msgstr "Der Server unterst�tzt SSL nicht."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:450
 msgid "An error occurred while setting up the SSL connection."
@@ -275,10 +275,10 @@ msgid ""
 "the pg_hba.conf file to include the client''s IP address or subnet, and that "
 "it is using an authentication scheme supported by the driver."
 msgstr ""
-"Der Authentifizierungstyp {0} wird nicht unterst�tzt. Stellen Sie sicher, "
+"Der Authentifizierungstyp {0} wird nicht unterst�tzt. Stellen Sie sicher, "
 "dass die Datei ''pg_hba.conf'' die IP-Adresse oder das Subnetz des Clients "
-"enth�lt und dass der Client ein Authentifizierungsschema nutzt, das vom "
-"Treiber unterst�tzt wird."
+"enth�lt und dass der Client ein Authentifizierungsschema nutzt, das vom "
+"Treiber unterst�tzt wird."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:713
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2628
@@ -451,12 +451,12 @@ msgid ""
 "Bind message length {0} too long.  This can be caused by very large or "
 "incorrect length specifications on InputStream parameters."
 msgstr ""
-"Die Nachrichtenl�nge {0} ist zu gro�. Das kann von sehr gro�en oder "
-"inkorrekten L�ngenangaben eines InputStream-Parameters herr�hren."
+"Die Nachrichtenl�nge {0} ist zu gro�. Das kann von sehr gro�en oder "
+"inkorrekten L�ngenangaben eines InputStream-Parameters herr�hren."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2240
 msgid "Ran out of memory retrieving query results."
-msgstr "Nicht gen�gend Speicher beim Abholen der Abfrageergebnisse."
+msgstr "Nicht gen�gend Speicher beim Abholen der Abfrageergebnisse."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2402
 msgid "COPY commands are only supported using the CopyManager API."
@@ -468,8 +468,8 @@ msgid ""
 "The server''s client_encoding parameter was changed to {0}. The JDBC driver "
 "requires client_encoding to be UTF8 for correct operation."
 msgstr ""
-"Der Parameter ''client_encoding'' wurde auf dem Server auf {0} ver�ndert. "
-"Der JDBC-Treiber setzt f�r korrektes Funktionieren die Einstellung UNICODE "
+"Der Parameter ''client_encoding'' wurde auf dem Server auf {0} ver�ndert. "
+"Der JDBC-Treiber setzt f�r korrektes Funktionieren die Einstellung UNICODE "
 "voraus."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2705
@@ -478,8 +478,8 @@ msgid ""
 "The server''s DateStyle parameter was changed to {0}. The JDBC driver "
 "requires DateStyle to begin with ISO for correct operation."
 msgstr ""
-"Der Parameter ''Date Style'' wurde auf dem Server auf {0} ver�ndert. Der "
-"JDBC-Treiber setzt f�r korrekte Funktion voraus, dass ''Date Style'' mit "
+"Der Parameter ''Date Style'' wurde auf dem Server auf {0} ver�ndert. Der "
+"JDBC-Treiber setzt f�r korrekte Funktion voraus, dass ''Date Style'' mit "
 "''ISO'' beginnt."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2718
@@ -494,13 +494,13 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:270
 #, java-format
 msgid "No value specified for parameter {0}."
-msgstr "F�r den Parameter {0} wurde kein Wert angegeben."
+msgstr "F�r den Parameter {0} wurde kein Wert angegeben."
 
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:462
 #, fuzzy, java-format
 msgid "Added parameters index out of range: {0}, number of columns: {1}."
 msgstr ""
-"Der Parameterindex {0} ist au�erhalb des g�ltigen Bereichs. Es gibt {1} "
+"Der Parameterindex {0} ist au�erhalb des g�ltigen Bereichs. Es gibt {1} "
 "Parameter."
 
 #: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:147
@@ -523,7 +523,7 @@ msgid ""
 "for the same PooledConnection or the PooledConnection has been closed."
 msgstr ""
 "Die Verbindung wurde automatisch geschlossen, da entweder eine neue "
-"Verbindung f�r die gleiche PooledConnection ge�ffnet wurde, oder die "
+"Verbindung f�r die gleiche PooledConnection ge�ffnet wurde, oder die "
 "PooledConnection geschlossen worden ist.."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:315
@@ -552,14 +552,14 @@ msgstr "Unbekannter Typ: {0}."
 #, fuzzy, java-format
 msgid "Fastpath call {0} - No result was returned and we expected a numeric."
 msgstr ""
-"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
+"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
 "erwartet."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:161
 #, java-format
 msgid "Fastpath call {0} - No result was returned and we expected an integer."
 msgstr ""
-"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
+"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
 "erwartet."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:169
@@ -568,14 +568,14 @@ msgid ""
 "Fastpath call {0} - No result was returned or wrong size while expecting an "
 "integer."
 msgstr ""
-"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
+"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
 "erwartet."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:186
 #, fuzzy, java-format
 msgid "Fastpath call {0} - No result was returned and we expected a long."
 msgstr ""
-"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
+"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
 "erwartet."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:194
@@ -584,7 +584,7 @@ msgid ""
 "Fastpath call {0} - No result was returned or wrong size while expecting a "
 "long."
 msgstr ""
-"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
+"Der Fastpath-Aufruf {0} gab kein Ergebnis zur�ck, jedoch wurde ein Integer "
 "erwartet."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:297
@@ -620,7 +620,7 @@ msgstr ""
 msgid ""
 "Truncation of large objects is only implemented in 8.3 and later servers."
 msgstr ""
-"Das Abschneiden gro�er Objekte ist nur in Versionen nach 8.3 implementiert."
+"Das Abschneiden gro�er Objekte ist nur in Versionen nach 8.3 implementiert."
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:82
 msgid "Cannot truncate LOB to a negative length."
@@ -630,19 +630,19 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:231
 #, java-format
 msgid "PostgreSQL LOBs can only index to: {0}"
-msgstr "LOBs in PostgreSQL k�nnen nur auf {0} verweisen."
+msgstr "LOBs in PostgreSQL k�nnen nur auf {0} verweisen."
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:227
 msgid "LOB positioning offsets start at 1."
-msgstr "Positionsoffsets f�r LOBs beginnen bei 1."
+msgstr "Positionsoffsets f�r LOBs beginnen bei 1."
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:243
 msgid "free() was called on this LOB previously"
-msgstr "free() wurde bereits f�r dieses LOB aufgerufen."
+msgstr "free() wurde bereits f�r dieses LOB aufgerufen."
 
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:95
 msgid "Too many update results were returned."
-msgstr "Zu viele Updateergebnisse wurden zur�ckgegeben."
+msgstr "Zu viele Updateergebnisse wurden zur�ckgegeben."
 
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:152
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:157
@@ -747,14 +747,14 @@ msgstr "Der Name eines namenlosen Rettungpunktes kann nicht ermittelt werden."
 #: src/main/java/org/postgresql/jdbc/PgArray.java:847
 #, java-format
 msgid "The array index is out of range: {0}"
-msgstr "Der Arrayindex ist au�erhalb des g�ltigen Bereichs: {0}."
+msgstr "Der Arrayindex ist au�erhalb des g�ltigen Bereichs: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgArray.java:177
 #: src/main/java/org/postgresql/jdbc/PgArray.java:864
 #, java-format
 msgid "The array index is out of range: {0}, number of elements: {1}."
 msgstr ""
-"Der Arrayindex {0} ist au�erhalb des g�ltigen Bereichs. Vorhandene Elemente: "
+"Der Arrayindex {0} ist au�erhalb des g�ltigen Bereichs. Vorhandene Elemente: "
 "{1}."
 
 #: src/main/java/org/postgresql/jdbc/PgArray.java:209
@@ -767,20 +767,20 @@ msgid ""
 "was created in.  The most common example of this is storing 8bit data in a "
 "SQL_ASCII database."
 msgstr ""
-"Ung�ltige Zeichendaten.  Das ist h�chstwahrscheinlich von in der Datenbank "
+"Ung�ltige Zeichendaten.  Das ist h�chstwahrscheinlich von in der Datenbank "
 "gespeicherten Zeichen hervorgerufen, die in einer anderen Kodierung "
-"vorliegen, als die, in der die Datenbank erstellt wurde.  Das h�ufigste "
-"Beispiel daf�r ist es, 8Bit-Daten in SQL_ASCII-Datenbanken abzulegen."
+"vorliegen, als die, in der die Datenbank erstellt wurde.  Das h�ufigste "
+"Beispiel daf�r ist es, 8Bit-Daten in SQL_ASCII-Datenbanken abzulegen."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:85
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:95
 msgid "A CallableStatement was executed with nothing returned."
-msgstr "Ein CallableStatement wurde ausgef�hrt ohne etwas zur�ckzugeben."
+msgstr "Ein CallableStatement wurde ausgef�hrt ohne etwas zur�ckzugeben."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:106
 msgid "A CallableStatement was executed with an invalid number of parameters"
 msgstr ""
-"Ein CallableStatement wurde mit einer falschen Anzahl Parameter ausgef�hrt."
+"Ein CallableStatement wurde mit einer falschen Anzahl Parameter ausgef�hrt."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:144
 #, java-format
@@ -788,8 +788,8 @@ msgid ""
 "A CallableStatement function was executed and the out parameter {0} was of "
 "type {1} however type {2} was registered."
 msgstr ""
-"Eine CallableStatement-Funktion wurde ausgef�hrt und der R�ckgabewert {0} "
-"war vom Typ {1}. Jedoch wurde der Typ {2} daf�r registriert."
+"Eine CallableStatement-Funktion wurde ausgef�hrt und der R�ckgabewert {0} "
+"war vom Typ {1}. Jedoch wurde der Typ {2} daf�r registriert."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:206
 msgid ""
@@ -831,8 +831,8 @@ msgstr "Es wurden keine Funktionsausgaben registriert."
 msgid ""
 "Results cannot be retrieved from a CallableStatement before it is executed."
 msgstr ""
-"Ergebnisse k�nnen nicht von einem CallableStatement abgerufen werden, bevor "
-"es ausgef�hrt wurde."
+"Ergebnisse k�nnen nicht von einem CallableStatement abgerufen werden, bevor "
+"es ausgef�hrt wurde."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:686
 #, fuzzy, java-format
@@ -842,7 +842,7 @@ msgstr "Unbekannter Typ: {0}."
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:262
 #, java-format
 msgid "Unsupported value for stringtype parameter: {0}"
-msgstr "Nichtunterst�tzter Wert f�r den Stringparameter: {0}"
+msgstr "Nichtunterst�tzter Wert f�r den Stringparameter: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:463
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:104
@@ -867,26 +867,26 @@ msgstr "Die Anweisung lieferte ein Ergebnis obwohl keines erwartet wurde."
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:604
 #, fuzzy
 msgid "Custom type maps are not supported."
-msgstr "Selbstdefinierte Typabbildungen werden nicht unterst�tzt."
+msgstr "Selbstdefinierte Typabbildungen werden nicht unterst�tzt."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:646
 #, java-format
 msgid "Failed to create object for: {0}."
-msgstr "Erstellung des Objektes schlug fehl f�r: {0}."
+msgstr "Erstellung des Objektes schlug fehl f�r: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:700
 #, java-format
 msgid "Unable to load the class {0} responsible for the datatype {1}"
 msgstr ""
-"Die f�r den Datentyp {1} verantwortliche Klasse {0} konnte nicht geladen "
+"Die f�r den Datentyp {1} verantwortliche Klasse {0} konnte nicht geladen "
 "werden."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:761
 msgid ""
 "Cannot change transaction read-only property in the middle of a transaction."
 msgstr ""
-"Die Nur-Lesen-Eigenschaft einer Transaktion kann nicht w�hrend der "
-"Transaktion ver�ndert werden."
+"Die Nur-Lesen-Eigenschaft einer Transaktion kann nicht w�hrend der "
+"Transaktion ver�ndert werden."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:846
 msgid "Cannot commit when autoCommit is enabled."
@@ -907,13 +907,13 @@ msgstr ""
 msgid ""
 "Cannot change transaction isolation level in the middle of a transaction."
 msgstr ""
-"Die Transaktions-Trennungsstufe kann nicht w�hrend einer Transaktion "
-"ver�ndert werden."
+"Die Transaktions-Trennungsstufe kann nicht w�hrend einer Transaktion "
+"ver�ndert werden."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:926
 #, java-format
 msgid "Transaction isolation level {0} not supported."
-msgstr "Die Transaktions-Trennungsstufe {0} ist nicht unterst�tzt."
+msgstr "Die Transaktions-Trennungsstufe {0} ist nicht unterst�tzt."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:975
 msgid "Finalizing a Connection that was never closed:"
@@ -921,30 +921,30 @@ msgstr "Eine Connection wurde finalisiert, die nie geschlossen wurde:"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1042
 msgid "Unable to translate data into the desired encoding."
-msgstr "Die Daten konnten nicht in die gew�nschte Kodierung gewandelt werden."
+msgstr "Die Daten konnten nicht in die gew�nschte Kodierung gewandelt werden."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:929
 msgid "Fetch size must be a value greater to or equal to 0."
-msgstr "Die Fetch-Gr��e muss ein Wert gr��er oder gleich Null sein."
+msgstr "Die Fetch-Gr��e muss ein Wert gr��er oder gleich Null sein."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1425
 #, fuzzy, java-format
 msgid "Unable to find server array type for provided name {0}."
 msgstr ""
-"F�r den angegebenen Namen {0} konnte kein Serverarraytyp gefunden werden."
+"F�r den angegebenen Namen {0} konnte kein Serverarraytyp gefunden werden."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1407
 #, fuzzy, java-format
 msgid "Invalid elements {0}"
-msgstr "Ung�ltige L�nge des Datenstroms: {0}."
+msgstr "Ung�ltige L�nge des Datenstroms: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1443
 #, fuzzy, java-format
 msgid "Invalid timeout ({0}<0)."
-msgstr "Ung�ltige L�nge des Datenstroms: {0}."
+msgstr "Ung�ltige L�nge des Datenstroms: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1473
 msgid "Validating connection."
@@ -953,11 +953,11 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1506
 #, fuzzy, java-format
 msgid "Failed to set ClientInfo property: {0}"
-msgstr "Erstellung des Objektes schlug fehl f�r: {0}."
+msgstr "Erstellung des Objektes schlug fehl f�r: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1516
 msgid "ClientInfo property not supported."
-msgstr "Die ClientInfo-Eigenschaft ist nicht unterst�tzt."
+msgstr "Die ClientInfo-Eigenschaft ist nicht unterst�tzt."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1542
 msgid "One or more ClientInfo failed."
@@ -966,7 +966,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1642
 #, fuzzy
 msgid "Network timeout must be a value greater than or equal to 0."
-msgstr "Das Abfragetimeout muss ein Wert gr��er oder gleich Null sein."
+msgstr "Das Abfragetimeout muss ein Wert gr��er oder gleich Null sein."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1654
 msgid "Unable to set network timeout."
@@ -979,7 +979,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1682
 #, java-format
 msgid "Unknown ResultSet holdability setting: {0}."
-msgstr "Unbekannte Einstellung f�r die Haltbarkeit des ResultSets: {0}."
+msgstr "Unbekannte Einstellung f�r die Haltbarkeit des ResultSets: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1700
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1721
@@ -988,14 +988,14 @@ msgstr "Ein Rettungspunkt kann im Modus ''auto-commit'' nicht erstellt werden."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1790
 msgid "Returning autogenerated keys is not supported."
-msgstr "Die R�ckgabe automatisch generierter Schl�ssel wird nicht unterst�tzt,"
+msgstr "Die R�ckgabe automatisch generierter Schl�ssel wird nicht unterst�tzt,"
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:65
 msgid ""
 "Unable to determine a value for MaxIndexKeys due to missing system catalog "
 "data."
 msgstr ""
-"Es konnte kein Wert f�r MaxIndexKeys gefunden werden, da die "
+"Es konnte kein Wert f�r MaxIndexKeys gefunden werden, da die "
 "Systemkatalogdaten fehlen."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:88
@@ -1057,7 +1057,7 @@ msgstr ""
 #, java-format
 msgid "The parameter index is out of range: {0}, number of parameters: {1}."
 msgstr ""
-"Der Parameterindex {0} ist au�erhalb des g�ltigen Bereichs. Es gibt {1} "
+"Der Parameterindex {0} ist au�erhalb des g�ltigen Bereichs. Es gibt {1} "
 "Parameter."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:90
@@ -1068,7 +1068,7 @@ msgstr ""
 msgid ""
 "Can''t use query methods that take a query string on a PreparedStatement."
 msgstr ""
-"Abfragemethoden, die einen Abfragestring annehmen, k�nnen nicht auf ein "
+"Abfragemethoden, die einen Abfragestring annehmen, k�nnen nicht auf ein "
 "PreparedStatement angewandt werden."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:254
@@ -1081,12 +1081,12 @@ msgstr "Unbekannter Typ."
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1497
 #, java-format
 msgid "Invalid stream length {0}."
-msgstr "Ung�ltige L�nge des Datenstroms: {0}."
+msgstr "Ung�ltige L�nge des Datenstroms: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:414
 #, java-format
 msgid "The JVM claims not to support the {0} encoding."
-msgstr "Die JVM behauptet, die Zeichenkodierung {0} nicht zu unterst�tzen."
+msgstr "Die JVM behauptet, die Zeichenkodierung {0} nicht zu unterst�tzen."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:417
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1124
@@ -1110,7 +1110,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:667
 #, java-format
 msgid "Cannot cast an instance of {0} to type {1}"
-msgstr "Die Typwandlung f�r eine Instanz von {0} nach {1} ist nicht m�glich."
+msgstr "Die Typwandlung f�r eine Instanz von {0} nach {1} ist nicht m�glich."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:685
 #, java-format
@@ -1120,7 +1120,7 @@ msgstr "Unbekannter Typ: {0}."
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:897
 #, java-format
 msgid "Cannot convert an instance of {0} to type {1}"
-msgstr "Die Typwandlung f�r eine Instanz von {0} nach {1} ist nicht m�glich."
+msgstr "Die Typwandlung f�r eine Instanz von {0} nach {1} ist nicht m�glich."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:973
 #, java-format
@@ -1128,7 +1128,7 @@ msgid ""
 "Can''t infer the SQL type to use for an instance of {0}. Use setObject() "
 "with an explicit Types value to specify the type to use."
 msgstr ""
-"Der in SQL f�r eine Instanz von {0} zu verwendende Datentyp kann nicht "
+"Der in SQL f�r eine Instanz von {0} zu verwendende Datentyp kann nicht "
 "abgeleitet werden. Benutzen Sie ''setObject()'' mit einem expliziten Typ, um "
 "ihn festzulegen."
 
@@ -1167,29 +1167,29 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3065
 #, fuzzy, java-format
 msgid "Cannot convert the column of type {0} to requested type {1}."
-msgstr "Die Typwandlung f�r eine Instanz von {0} nach {1} ist nicht m�glich."
+msgstr "Die Typwandlung f�r eine Instanz von {0} nach {1} ist nicht m�glich."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:851
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:872
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1799
 msgid "Can''t use relative move methods while on the insert row."
 msgstr ""
-"Relative Bewegungen k�nnen in der Einf�gezeile nicht durchgef�hrt werden."
+"Relative Bewegungen k�nnen in der Einf�gezeile nicht durchgef�hrt werden."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:895
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:921
 #, java-format
 msgid "Invalid fetch direction constant: {0}."
-msgstr "Unzul�ssige Richtungskonstante bei fetch: {0}."
+msgstr "Unzul�ssige Richtungskonstante bei fetch: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:905
 msgid "Cannot call cancelRowUpdates() when on the insert row."
 msgstr ""
-"''cancelRowUpdates()'' kann in der Einf�gezeile nicht aufgerufen werden."
+"''cancelRowUpdates()'' kann in der Einf�gezeile nicht aufgerufen werden."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:920
 msgid "Cannot call deleteRow() when on the insert row."
-msgstr "''deleteRow()'' kann in der Einf�gezeile nicht aufgerufen werden."
+msgstr "''deleteRow()'' kann in der Einf�gezeile nicht aufgerufen werden."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:927
 msgid ""
@@ -1213,12 +1213,12 @@ msgstr "Es gibt keine Zeilen in diesem ResultSet."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:974
 msgid "Not on the insert row."
-msgstr "Nicht in der Einf�gezeile."
+msgstr "Nicht in der Einf�gezeile."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:976
 msgid "You must specify at least one column value to insert a row."
 msgstr ""
-"Sie m�ssen mindestens einen Spaltenwert angeben, um eine Zeile einzuf�gen."
+"Sie m�ssen mindestens einen Spaltenwert angeben, um eine Zeile einzuf�gen."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1121
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1718
@@ -1226,15 +1226,15 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2439
 #, java-format
 msgid "The JVM claims not to support the encoding: {0}"
-msgstr "Die JVM behauptet, die Zeichenkodierung {0} nicht zu unterst�tzen."
+msgstr "Die JVM behauptet, die Zeichenkodierung {0} nicht zu unterst�tzen."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1248
 msgid "Can''t refresh the insert row."
-msgstr "Die Einf�gezeile kann nicht aufgefrischt werden."
+msgstr "Die Einf�gezeile kann nicht aufgefrischt werden."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1313
 msgid "Cannot call updateRow() when on the insert row."
-msgstr "''updateRow()'' kann in der Einf�gezeile nicht aufgerufen werden."
+msgstr "''updateRow()'' kann in der Einf�gezeile nicht aufgerufen werden."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1320
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3082
@@ -1248,13 +1248,13 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1496
 msgid "ResultSets with concurrency CONCUR_READ_ONLY cannot be updated."
 msgstr ""
-"ResultSets, deren Zugriffsart CONCUR_READ_ONLY ist, k�nnen nicht "
+"ResultSets, deren Zugriffsart CONCUR_READ_ONLY ist, k�nnen nicht "
 "aktualisiert werden."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1564
 #, java-format
 msgid "No primary key found for table {0}."
-msgstr "F�r die Tabelle {0} konnte kein Prim�rschl�ssel gefunden werden."
+msgstr "F�r die Tabelle {0} konnte kein Prim�rschl�ssel gefunden werden."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1995
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2000
@@ -1271,7 +1271,7 @@ msgstr "F
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3070
 #, java-format
 msgid "Bad value for type {0} : {1}"
-msgstr "Unzul�ssiger Wert f�r den Typ {0} : {1}."
+msgstr "Unzul�ssiger Wert f�r den Typ {0} : {1}."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2592
 #, java-format
@@ -1285,8 +1285,8 @@ msgid ""
 "the JDBC 2.1 API Specification, section 5.6 for more details."
 msgstr ""
 "Das ResultSet kann nicht aktualisiert werden.  Die Abfrage, die es erzeugte, "
-"darf nur eine Tabelle und muss darin alle Prim�rschl�ssel ausw�hlen. Siehe "
-"JDBC 2.1 API-Spezifikation, Abschnitt 5.6 f�r mehr Details."
+"darf nur eine Tabelle und muss darin alle Prim�rschl�ssel ausw�hlen. Siehe "
+"JDBC 2.1 API-Spezifikation, Abschnitt 5.6 f�r mehr Details."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2740
 msgid "This ResultSet is closed."
@@ -1301,7 +1301,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3102
 #, fuzzy
 msgid "Invalid UUID data."
-msgstr "Ung�ltiges Flag."
+msgstr "Ung�ltiges Flag."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3191
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3198
@@ -1328,12 +1328,12 @@ msgstr "Ung
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3430
 #, fuzzy, java-format
 msgid "conversion to {0} from {1} not supported"
-msgstr "Die Transaktions-Trennungsstufe {0} ist nicht unterst�tzt."
+msgstr "Die Transaktions-Trennungsstufe {0} ist nicht unterst�tzt."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3370
 #, fuzzy
 msgid "Invalid Inet data."
-msgstr "Ung�ltiges Flag."
+msgstr "Ung�ltiges Flag."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:151
 msgid "Unable to decode xml data."
@@ -1347,7 +1347,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:202
 #, fuzzy
 msgid "Unable to create SAXResult for SQLXML."
-msgstr "Erstellung des Objektes schlug fehl f�r: {0}."
+msgstr "Erstellung des Objektes schlug fehl f�r: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:217
 msgid "Unable to create StAXResult for SQLXML"
@@ -1356,7 +1356,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:222
 #, fuzzy, java-format
 msgid "Unknown XML Result class: {0}"
-msgstr "Unbekannte Einstellung f�r die Haltbarkeit des ResultSets: {0}."
+msgstr "Unbekannte Einstellung f�r die Haltbarkeit des ResultSets: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:235
 #, fuzzy
@@ -1394,15 +1394,15 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:518
 msgid "Maximum number of rows must be a value grater than or equal to 0."
-msgstr "Die maximale Zeilenzahl muss ein Wert gr��er oder gleich Null sein."
+msgstr "Die maximale Zeilenzahl muss ein Wert gr��er oder gleich Null sein."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:564
 msgid "Query timeout must be a value greater than or equals to 0."
-msgstr "Das Abfragetimeout muss ein Wert gr��er oder gleich Null sein."
+msgstr "Das Abfragetimeout muss ein Wert gr��er oder gleich Null sein."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:606
 msgid "The maximum field size must be a value greater than or equal to 0."
-msgstr "Die maximale Feldgr��e muss ein Wert gr��er oder gleich Null sein."
+msgstr "Die maximale Feldgr��e muss ein Wert gr��er oder gleich Null sein."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:709
 msgid "This statement has been closed."
@@ -1413,18 +1413,18 @@ msgstr "Die Anweisung wurde geschlossen."
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1236
 #, fuzzy
 msgid "Returning autogenerated keys by column index is not supported."
-msgstr "Die R�ckgabe automatisch generierter Schl�ssel wird nicht unterst�tzt,"
+msgstr "Die R�ckgabe automatisch generierter Schl�ssel wird nicht unterst�tzt,"
 
 #: src/main/java/org/postgresql/jdbc/SslMode.java:78
 #, fuzzy, java-format
 msgid "Invalid sslmode value: {0}"
-msgstr "Ung�ltige L�nge des Datenstroms: {0}."
+msgstr "Ung�ltige L�nge des Datenstroms: {0}."
 
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:356
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:424
 #, fuzzy, java-format
 msgid "Bad value for type timestamp/date/time: {1}"
-msgstr "Unzul�ssiger Wert f�r den Typ {0} : {1}."
+msgstr "Unzul�ssiger Wert f�r den Typ {0} : {1}."
 
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:981
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1038
@@ -1449,12 +1449,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:120
 #, fuzzy, java-format
 msgid "Invalid server-first-message: {0}"
-msgstr "Ung�ltige L�nge des Datenstroms: {0}."
+msgstr "Ung�ltige L�nge des Datenstroms: {0}."
 
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:157
 #, fuzzy, java-format
 msgid "Invalid server-final-message: {0}"
-msgstr "Ung�ltige L�nge des Datenstroms: {0}."
+msgstr "Ung�ltige L�nge des Datenstroms: {0}."
 
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:163
 #, java-format
@@ -1469,7 +1469,7 @@ msgstr ""
 #: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:290
 msgid "Large Objects may not be used in auto-commit mode."
 msgstr ""
-"LargeObjects (LOB) d�rfen im Modus ''auto-commit'' nicht verwendet werden."
+"LargeObjects (LOB) d�rfen im Modus ''auto-commit'' nicht verwendet werden."
 
 #: src/main/java/org/postgresql/osgi/PGDataSourceFactory.java:82
 #, fuzzy, java-format
@@ -1598,12 +1598,12 @@ msgstr ""
 #, fuzzy, java-format
 msgid "Unable to parse X509Certificate for hostname {0}"
 msgstr ""
-"F�r den angegebenen Namen {0} konnte kein Serverarraytyp gefunden werden."
+"F�r den angegebenen Namen {0} konnte kein Serverarraytyp gefunden werden."
 
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:90
 #, fuzzy, java-format
 msgid "No certificates found for hostname {0}"
-msgstr "F�r die Tabelle {0} konnte kein Prim�rschl�ssel gefunden werden."
+msgstr "F�r die Tabelle {0} konnte kein Prim�rschl�ssel gefunden werden."
 
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:109
 #, java-format
@@ -1698,7 +1698,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/util/PGmoney.java:61
 msgid "Conversion of money failed."
-msgstr "Die Umwandlung eines W�hrungsbetrags schlug fehl."
+msgstr "Die Umwandlung eines W�hrungsbetrags schlug fehl."
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:43
 #, java-format
@@ -1759,7 +1759,7 @@ msgstr ""
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:381
 #, fuzzy, java-format
 msgid "Invalid flags {0}"
-msgstr "Ung�ltige Flags"
+msgstr "Ung�ltige Flags"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:190
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:276
@@ -1769,7 +1769,7 @@ msgstr "Die xid darf nicht null sein."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:194
 msgid "Connection is busy with another transaction"
-msgstr "Die Verbindung ist derzeit mit einer anderen Transaktion besch�ftigt."
+msgstr "Die Verbindung ist derzeit mit einer anderen Transaktion besch�ftigt."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:203
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:286
@@ -1795,7 +1795,7 @@ msgid ""
 "tried to call end without corresponding start call. state={0}, start "
 "xid={1}, currentXid={2}, preparedXid={3}"
 msgstr ""
-"Es wurde versucht, ohne dazugeh�rigen ''start''-Aufruf ''end'' aufzurufen."
+"Es wurde versucht, ohne dazugeh�rigen ''start''-Aufruf ''end'' aufzurufen."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:326
 #, java-format
@@ -1814,7 +1814,7 @@ msgid ""
 "Not implemented: Prepare must be issued using the same connection that "
 "started the transaction. currentXid={0}, prepare xid={1}"
 msgstr ""
-"Nicht implementiert: ''Prepare'' muss �ber die selbe Verbindung abgesetzt "
+"Nicht implementiert: ''Prepare'' muss �ber die selbe Verbindung abgesetzt "
 "werden, die die Transaktion startete."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:340
@@ -1849,7 +1849,7 @@ msgid ""
 "Not implemented: one-phase commit must be issued using the same connection "
 "that was used to start it"
 msgstr ""
-"Nicht implementiert: Die einphasige Best�tigung muss �ber die selbe "
+"Nicht implementiert: Die einphasige Best�tigung muss �ber die selbe "
 "Verbindung abgewickelt werden, die verwendet wurde, um sie zu beginnen."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:533
@@ -1865,7 +1865,7 @@ msgstr "''Commit'' wurde vor ''end'' aufgerufen."
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:548
 #, fuzzy, java-format
 msgid "Error during one-phase commit. commit xid={0}"
-msgstr "Bei der einphasigen Best�tigung trat ein Fehler auf."
+msgstr "Bei der einphasigen Best�tigung trat ein Fehler auf."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:576
 #, fuzzy, java-format
@@ -1873,7 +1873,7 @@ msgid ""
 "Not implemented: 2nd phase commit must be issued using an idle connection. "
 "commit xid={0}, currentXid={1}, state={2}, transactionState={3}"
 msgstr ""
-"Nicht implementiert: Die zweite Best�tigungsphase muss �ber eine im Leerlauf "
+"Nicht implementiert: Die zweite Best�tigungsphase muss �ber eine im Leerlauf "
 "befindliche Verbindung abgewickelt werden."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:609
@@ -1886,7 +1886,7 @@ msgstr "Fehler beim Rollback einer vorbereiteten Transaktion."
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:626
 #, fuzzy, java-format
 msgid "Heuristic commit/rollback not supported. forget xid={0}"
-msgstr "Heuristisches Commit/Rollback wird nicht unterst�tzt."
+msgstr "Heuristisches Commit/Rollback wird nicht unterst�tzt."
 
 #~ msgid "The driver currently does not support COPY operations."
-#~ msgstr "Der Treiber unterst�tzt derzeit keine COPY-Operationen."
+#~ msgstr "Der Treiber unterst�tzt derzeit keine COPY-Operationen."
diff --git a/src/main/java/org/postgresql/translation/fr.po b/src/main/java/org/postgresql/translation/fr.po
index 3ed6a35c01d084e47204c4dd4a861dbd393382c0..e015746a4d1034f2cd340880d42308fa99bedf37 100644
--- a/src/main/java/org/postgresql/translation/fr.po
+++ b/src/main/java/org/postgresql/translation/fr.po
@@ -21,7 +21,7 @@ msgstr ""
 #: src/main/java/org/postgresql/Driver.java:221
 msgid "Error loading default settings from driverconfig.properties"
 msgstr ""
-"Erreur de chargement des valeurs par d�faut depuis driverconfig.properties"
+"Erreur de chargement des valeurs par d�faut depuis driverconfig.properties"
 
 #: src/main/java/org/postgresql/Driver.java:233
 msgid "Properties for the driver contains a non-string value for the key "
@@ -40,21 +40,21 @@ msgid ""
 "Something unusual has occurred to cause the driver to fail. Please report "
 "this exception."
 msgstr ""
-"Quelque chose d''inhabituel a provoqu� l''�chec du pilote. Veuillez faire un "
+"Quelque chose d''inhabituel a provoqu� l''�chec du pilote. Veuillez faire un "
 "rapport sur cette erreur."
 
 #: src/main/java/org/postgresql/Driver.java:422
 msgid "Connection attempt timed out."
-msgstr "La tentative de connexion a �chou� dans le d�lai imparti."
+msgstr "La tentative de connexion a �chou� dans le d�lai imparti."
 
 #: src/main/java/org/postgresql/Driver.java:435
 msgid "Interrupted while attempting to connect."
-msgstr "Interrompu pendant l''�tablissement de la connexion."
+msgstr "Interrompu pendant l''�tablissement de la connexion."
 
 #: src/main/java/org/postgresql/Driver.java:691
 #, java-format
 msgid "Method {0} is not yet implemented."
-msgstr "La fonction {0} n''est pas encore impl�ment�e."
+msgstr "La fonction {0} n''est pas encore impl�ment�e."
 
 #: src/main/java/org/postgresql/PGProperty.java:818
 #: src/main/java/org/postgresql/PGProperty.java:838
@@ -86,7 +86,7 @@ msgstr ""
 #: src/main/java/org/postgresql/copy/PGCopyOutputStream.java:95
 #, fuzzy
 msgid "This copy stream is closed."
-msgstr "Ce ResultSet est ferm�."
+msgstr "Ce ResultSet est ferm�."
 
 #: src/main/java/org/postgresql/copy/PGCopyInputStream.java:109
 msgid "Read from copy failed."
@@ -101,14 +101,14 @@ msgstr ""
 #, fuzzy, java-format
 msgid "Unable to parse the count in command completion tag: {0}."
 msgstr ""
-"Incapable d''interpr�ter le nombre de mise � jour dans la balise de "
-"compl�tion de commande�: {0}."
+"Incapable d''interpr�ter le nombre de mise � jour dans la balise de "
+"compl�tion de commande�: {0}."
 
 #: src/main/java/org/postgresql/core/ConnectionFactory.java:57
 #, java-format
 msgid "A connection could not be made using the requested protocol {0}."
 msgstr ""
-"Aucune connexion n''a pu �tre �tablie en utilisant le protocole demand� {0}. "
+"Aucune connexion n''a pu �tre �tablie en utilisant le protocole demand� {0}. "
 
 #: src/main/java/org/postgresql/core/Oid.java:128
 #, java-format
@@ -119,46 +119,46 @@ msgstr ""
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:129
 #, java-format
 msgid "Illegal UTF-8 sequence: initial byte is {0}: {1}"
-msgstr "S�quence UTF-8 ill�gale: le premier octet est {0}: {1}"
+msgstr "S�quence UTF-8 ill�gale: le premier octet est {0}: {1}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:135
 #, java-format
 msgid "Illegal UTF-8 sequence: final value is out of range: {0}"
 msgstr ""
-"S�quence UTF-8 ill�gale: la valeur finale est en dehors des limites: {0}"
+"S�quence UTF-8 ill�gale: la valeur finale est en dehors des limites: {0}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:148
 #, java-format
 msgid "Illegal UTF-8 sequence: final value is a surrogate value: {0}"
 msgstr ""
-"S�quence UTF-8 ill�gale: la valeur finale est une valeur de remplacement: {0}"
+"S�quence UTF-8 ill�gale: la valeur finale est une valeur de remplacement: {0}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:164
 #, java-format
 msgid ""
 "Illegal UTF-8 sequence: byte {0} of {1} byte sequence is not 10xxxxxx: {2}"
 msgstr ""
-"S�quence UTF-8 ill�gale: l''octet {0} de la s�quence d''octet {1} n''est pas "
+"S�quence UTF-8 ill�gale: l''octet {0} de la s�quence d''octet {1} n''est pas "
 "10xxxxxx: {2}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:200
 #, java-format
 msgid "Illegal UTF-8 sequence: {0} bytes used to encode a {1} byte value: {2}"
 msgstr ""
-"S�quence UTF-8 ill�gale: {0} octets utilis� pour encoder une valeur � {1} "
+"S�quence UTF-8 ill�gale: {0} octets utilis� pour encoder une valeur � {1} "
 "octets: {2}"
 
 #: src/main/java/org/postgresql/core/PGStream.java:565
 #, java-format
 msgid "Premature end of input stream, expected {0} bytes, but only read {1}."
 msgstr ""
-"Fin pr�matur�e du flux en entr�e, {0} octets attendus, mais seulement {1} "
+"Fin pr�matur�e du flux en entr�e, {0} octets attendus, mais seulement {1} "
 "lus."
 
 #: src/main/java/org/postgresql/core/PGStream.java:606
 #, java-format
 msgid "Expected an EOF from server, got: {0}"
-msgstr "Attendait une fin de fichier du serveur, re�u: {0}"
+msgstr "Attendait une fin de fichier du serveur, re�u: {0}"
 
 #: src/main/java/org/postgresql/core/PGStream.java:666
 #, java-format
@@ -170,28 +170,28 @@ msgstr ""
 #, java-format
 msgid "Malformed function or procedure escape syntax at offset {0}."
 msgstr ""
-"Syntaxe de fonction ou d''�chappement de proc�dure malform�e � l''indice {0}."
+"Syntaxe de fonction ou d''�chappement de proc�dure malform�e � l''indice {0}."
 
 #: src/main/java/org/postgresql/core/SetupQueryRunner.java:64
 msgid "An unexpected result was returned by a query."
-msgstr "Un r�sultat inattendu a �t� retourn� par une requ�te."
+msgstr "Un r�sultat inattendu a �t� retourn� par une requ�te."
 
 #: src/main/java/org/postgresql/core/SocketFactoryFactory.java:43
 #, fuzzy, java-format
 msgid "The SocketFactory class provided {0} could not be instantiated."
-msgstr "La classe SSLSocketFactory fournie {0} n''a pas pu �tre instanci�e."
+msgstr "La classe SSLSocketFactory fournie {0} n''a pas pu �tre instanci�e."
 
 #: src/main/java/org/postgresql/core/SocketFactoryFactory.java:68
 #, java-format
 msgid "The SSLSocketFactory class provided {0} could not be instantiated."
-msgstr "La classe SSLSocketFactory fournie {0} n''a pas pu �tre instanci�e."
+msgstr "La classe SSLSocketFactory fournie {0} n''a pas pu �tre instanci�e."
 
 #: src/main/java/org/postgresql/core/Utils.java:93
 #: src/main/java/org/postgresql/core/Utils.java:110
 msgid "Zero bytes may not occur in string parameters."
 msgstr ""
-"Z�ro octets ne devrait pas se produire dans les param�tres de type cha�ne de "
-"caract�res."
+"Z�ro octets ne devrait pas se produire dans les param�tres de type cha�ne de "
+"caract�res."
 
 #: src/main/java/org/postgresql/core/Utils.java:120
 #: src/main/java/org/postgresql/core/Utils.java:170
@@ -200,7 +200,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/core/Utils.java:159
 msgid "Zero bytes may not occur in identifiers."
-msgstr "Des octects � 0 ne devraient pas appara�tre dans les identifiants."
+msgstr "Des octects � 0 ne devraient pas appara�tre dans les identifiants."
 
 #: src/main/java/org/postgresql/core/v3/CompositeParameterList.java:34
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:54
@@ -211,7 +211,7 @@ msgstr "Des octects 
 #, java-format
 msgid "The column index is out of range: {0}, number of columns: {1}."
 msgstr ""
-"L''indice de la colonne est hors limite�: {0}, nombre de colonnes�: {1}."
+"L''indice de la colonne est hors limite�: {0}, nombre de colonnes�: {1}."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:162
 #, fuzzy, java-format
@@ -224,13 +224,13 @@ msgid ""
 "Connection to {0} refused. Check that the hostname and port are correct and "
 "that the postmaster is accepting TCP/IP connections."
 msgstr ""
-"Connexion refus�e. V�rifiez que le nom de machine et le port sont corrects "
+"Connexion refus�e. V�rifiez que le nom de machine et le port sont corrects "
 "et que postmaster accepte les connexions TCP/IP."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:297
 #: src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java:135
 msgid "The connection attempt failed."
-msgstr "La tentative de connexion a �chou�."
+msgstr "La tentative de connexion a �chou�."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:312
 #, java-format
@@ -245,7 +245,7 @@ msgstr "Le serveur ne supporte pas SSL."
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:450
 msgid "An error occurred while setting up the SSL connection."
 msgstr ""
-"Une erreur s''est produite pendant l''�tablissement de la connexion SSL."
+"Une erreur s''est produite pendant l''�tablissement de la connexion SSL."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:546
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:573
@@ -253,8 +253,8 @@ msgid ""
 "The server requested password-based authentication, but no password was "
 "provided."
 msgstr ""
-"Le serveur a demand� une authentification par mots de passe, mais aucun mot "
-"de passe n''a �t� fourni."
+"Le serveur a demand� une authentification par mots de passe, mais aucun mot "
+"de passe n''a �t� fourni."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:680
 msgid ""
@@ -269,9 +269,9 @@ msgid ""
 "the pg_hba.conf file to include the client''s IP address or subnet, and that "
 "it is using an authentication scheme supported by the driver."
 msgstr ""
-"Le type d''authentification {0} n''est pas support�. V�rifiez que vous avez "
-"configur� le fichier pg_hba.conf pour inclure l''adresse IP du client ou le "
-"sous-r�seau et qu''il utilise un sch�ma d''authentification support� par le "
+"Le type d''authentification {0} n''est pas support�. V�rifiez que vous avez "
+"configur� le fichier pg_hba.conf pour inclure l''adresse IP du client ou le "
+"sous-r�seau et qu''il utilise un sch�ma d''authentification support� par le "
 "pilote."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:713
@@ -281,7 +281,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2738
 #: src/main/java/org/postgresql/gss/GssAction.java:125
 msgid "Protocol error.  Session setup failed."
-msgstr "Erreur de protocole. Ouverture de la session en �chec."
+msgstr "Erreur de protocole. Ouverture de la session en �chec."
 
 #: src/main/java/org/postgresql/core/v3/CopyInImpl.java:54
 msgid "CopyIn copy direction can't receive data"
@@ -302,11 +302,11 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:201
 #, fuzzy
 msgid "Interrupted while waiting to obtain lock on database connection"
-msgstr "Interrompu pendant l''�tablissement de la connexion."
+msgstr "Interrompu pendant l''�tablissement de la connexion."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:333
 msgid "Unable to bind parameter values for statement."
-msgstr "Incapable de lier les valeurs des param�tres pour la commande."
+msgstr "Incapable de lier les valeurs des param�tres pour la commande."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:339
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:516
@@ -316,7 +316,7 @@ msgstr "Incapable de lier les valeurs des param
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2465
 #: src/main/java/org/postgresql/util/StreamWrapper.java:141
 msgid "An I/O error occurred while sending to the backend."
-msgstr "Une erreur d''entr�e/sortie a eu lieu lors d''envoi vers le serveur."
+msgstr "Une erreur d''entr�e/sortie a eu lieu lors d''envoi vers le serveur."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:391
 msgid "Error releasing savepoint"
@@ -332,23 +332,23 @@ msgstr "Attendait le statut de commande BEGIN, obtenu {0}."
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1744
 #, java-format
 msgid "Unexpected command status: {0}."
-msgstr "Statut de commande inattendu�: {0}."
+msgstr "Statut de commande inattendu�: {0}."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:729
 #, fuzzy
 msgid "An error occurred while trying to get the socket timeout."
-msgstr "Une erreur d''entr�e/sortie a eu lieu lors d''envoi vers le serveur."
+msgstr "Une erreur d''entr�e/sortie a eu lieu lors d''envoi vers le serveur."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:764
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:840
 #, java-format
 msgid "Unknown Response Type {0}."
-msgstr "Type de r�ponse inconnu {0}."
+msgstr "Type de r�ponse inconnu {0}."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:787
 #, fuzzy
 msgid "An error occurred while trying to reset the socket timeout."
-msgstr "Une erreur d''entr�e/sortie a eu lieu lors d''envoi vers le serveur."
+msgstr "Une erreur d''entr�e/sortie a eu lieu lors d''envoi vers le serveur."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:885
 msgid "Database connection failed when starting copy"
@@ -402,7 +402,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1126
 #, fuzzy
 msgid "PGStream is closed"
-msgstr "Ce ResultSet est ferm�."
+msgstr "Ce ResultSet est ferm�."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1187
 #, java-format
@@ -431,7 +431,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1263
 #, fuzzy, java-format
 msgid "Unexpected copydata from server for {0}"
-msgstr "Attendait une fin de fichier du serveur, re�u: {0}"
+msgstr "Attendait une fin de fichier du serveur, re�u: {0}"
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1323
 #, java-format
@@ -444,13 +444,13 @@ msgid ""
 "Bind message length {0} too long.  This can be caused by very large or "
 "incorrect length specifications on InputStream parameters."
 msgstr ""
-"La longueur du message de liaison {0} est trop grande. Cela peut �tre caus� "
-"par des sp�cification de longueur tr�s grandes ou incorrectes pour les "
-"param�tres de type InputStream."
+"La longueur du message de liaison {0} est trop grande. Cela peut �tre caus� "
+"par des sp�cification de longueur tr�s grandes ou incorrectes pour les "
+"param�tres de type InputStream."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2240
 msgid "Ran out of memory retrieving query results."
-msgstr "Ai manqu� de m�moire en r�cup�rant les r�sultats de la requ�te."
+msgstr "Ai manqu� de m�moire en r�cup�rant les r�sultats de la requ�te."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2402
 msgid "COPY commands are only supported using the CopyManager API."
@@ -462,8 +462,8 @@ msgid ""
 "The server''s client_encoding parameter was changed to {0}. The JDBC driver "
 "requires client_encoding to be UTF8 for correct operation."
 msgstr ""
-"Le param�tre client_encoding du serveur a �t� chang� pour {0}. Le pilote "
-"JDBC n�cessite l''affectation de la valeur UNICODE � client_encoding pour un "
+"Le param�tre client_encoding du serveur a �t� chang� pour {0}. Le pilote "
+"JDBC n�cessite l''affectation de la valeur UNICODE � client_encoding pour un "
 "fonctionnement correct."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2705
@@ -472,8 +472,8 @@ msgid ""
 "The server''s DateStyle parameter was changed to {0}. The JDBC driver "
 "requires DateStyle to begin with ISO for correct operation."
 msgstr ""
-"Le param�tre DateStyle du serveur a �t� chang� pour {0}. Le pilote JDBC "
-"n�cessite que DateStyle commence par ISO pour un fonctionnement correct."
+"Le param�tre DateStyle du serveur a �t� chang� pour {0}. Le pilote JDBC "
+"n�cessite que DateStyle commence par ISO pour un fonctionnement correct."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2718
 #, java-format
@@ -481,19 +481,19 @@ msgid ""
 "The server''s standard_conforming_strings parameter was reported as {0}. The "
 "JDBC driver expected on or off."
 msgstr ""
-"Le param�tre serveur standard_conforming_strings a pour valeur {0}. Le "
+"Le param�tre serveur standard_conforming_strings a pour valeur {0}. Le "
 "driver JDBC attend on ou off."
 
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:270
 #, java-format
 msgid "No value specified for parameter {0}."
-msgstr "Pas de valeur sp�cifi�e pour le param�tre {0}."
+msgstr "Pas de valeur sp�cifi�e pour le param�tre {0}."
 
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:462
 #, fuzzy, java-format
 msgid "Added parameters index out of range: {0}, number of columns: {1}."
 msgstr ""
-"L''indice du param�tre est hors limites�: {0}, nombre de param�tres�: {1}."
+"L''indice du param�tre est hors limites�: {0}, nombre de param�tres�: {1}."
 
 #: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:147
 #, java-format
@@ -503,27 +503,27 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:272
 #, fuzzy
 msgid "This replication stream has been closed."
-msgstr "La connexion a �t� ferm�e."
+msgstr "La connexion a �t� ferm�e."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:118
 msgid "This PooledConnection has already been closed."
-msgstr "Cette PooledConnection a d�j� �t� ferm�e."
+msgstr "Cette PooledConnection a d�j� �t� ferm�e."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:314
 msgid ""
 "Connection has been closed automatically because a new connection was opened "
 "for the same PooledConnection or the PooledConnection has been closed."
 msgstr ""
-"La connexion a �t� ferm�e automatiquement car une nouvelle connexion a �t� "
-"ouverte pour la m�me PooledConnection ou la PooledConnection a �t� ferm�e."
+"La connexion a �t� ferm�e automatiquement car une nouvelle connexion a �t� "
+"ouverte pour la m�me PooledConnection ou la PooledConnection a �t� ferm�e."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:315
 msgid "Connection has been closed."
-msgstr "La connexion a �t� ferm�e."
+msgstr "La connexion a �t� ferm�e."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:420
 msgid "Statement has been closed."
-msgstr "Statement a �t� ferm�."
+msgstr "Statement a �t� ferm�."
 
 #: src/main/java/org/postgresql/ds/PGPoolingDataSource.java:269
 msgid "Failed to setup DataSource."
@@ -531,26 +531,26 @@ msgstr ""
 
 #: src/main/java/org/postgresql/ds/PGPoolingDataSource.java:371
 msgid "DataSource has been closed."
-msgstr "DataSource a �t� ferm�e."
+msgstr "DataSource a �t� ferm�e."
 
 #: src/main/java/org/postgresql/ds/common/BaseDataSource.java:1270
 #: src/main/java/org/postgresql/ds/common/BaseDataSource.java:1280
 #, fuzzy, java-format
 msgid "Unsupported property name: {0}"
-msgstr "Valeur de type non support�e�: {0}"
+msgstr "Valeur de type non support�e�: {0}"
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:84
 #, fuzzy, java-format
 msgid "Fastpath call {0} - No result was returned and we expected a numeric."
 msgstr ""
-"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
+"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
 "entier."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:161
 #, java-format
 msgid "Fastpath call {0} - No result was returned and we expected an integer."
 msgstr ""
-"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
+"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
 "entier."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:169
@@ -559,14 +559,14 @@ msgid ""
 "Fastpath call {0} - No result was returned or wrong size while expecting an "
 "integer."
 msgstr ""
-"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
+"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
 "entier."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:186
 #, fuzzy, java-format
 msgid "Fastpath call {0} - No result was returned and we expected a long."
 msgstr ""
-"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
+"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
 "entier."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:194
@@ -575,7 +575,7 @@ msgid ""
 "Fastpath call {0} - No result was returned or wrong size while expecting a "
 "long."
 msgstr ""
-"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
+"Appel Fastpath {0} - Aucun r�sultat n''a �t� retourn� et nous attendions un "
 "entier."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:297
@@ -592,12 +592,12 @@ msgstr "La fonction fastpath {0} est inconnue."
 #: src/main/java/org/postgresql/geometric/PGpoint.java:76
 #, java-format
 msgid "Conversion to type {0} failed: {1}."
-msgstr "La conversion vers le type {0} a �chou�: {1}."
+msgstr "La conversion vers le type {0} a �chou�: {1}."
 
 #: src/main/java/org/postgresql/geometric/PGpath.java:70
 #, java-format
 msgid "Cannot tell if path is open or closed: {0}."
-msgstr "Impossible de dire si path est ferm� ou ouvert�: {0}."
+msgstr "Impossible de dire si path est ferm� ou ouvert�: {0}."
 
 #: src/main/java/org/postgresql/gss/GssAction.java:136
 #: src/main/java/org/postgresql/gss/MakeGSS.java:65
@@ -609,8 +609,8 @@ msgstr ""
 msgid ""
 "Truncation of large objects is only implemented in 8.3 and later servers."
 msgstr ""
-"Le troncage des large objects n''est impl�ment� que dans les serveurs 8.3 et "
-"sup�rieurs."
+"Le troncage des large objects n''est impl�ment� que dans les serveurs 8.3 et "
+"sup�rieurs."
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:82
 msgid "Cannot truncate LOB to a negative length."
@@ -620,19 +620,19 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:231
 #, java-format
 msgid "PostgreSQL LOBs can only index to: {0}"
-msgstr "Les LOB PostgreSQL peuvent seulement s''indicer �: {0}"
+msgstr "Les LOB PostgreSQL peuvent seulement s''indicer �: {0}"
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:227
 msgid "LOB positioning offsets start at 1."
-msgstr "Les d�calages de position des LOB commencent � 1."
+msgstr "Les d�calages de position des LOB commencent � 1."
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:243
 msgid "free() was called on this LOB previously"
-msgstr "free() a �t� appel�e auparavant sur ce LOB"
+msgstr "free() a �t� appel�e auparavant sur ce LOB"
 
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:95
 msgid "Too many update results were returned."
-msgstr "Trop de r�sultats de mise � jour ont �t� retourn�s."
+msgstr "Trop de r�sultats de mise � jour ont �t� retourn�s."
 
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:152
 #: src/main/java/org/postgresql/jdbc/BatchResultHandler.java:157
@@ -641,8 +641,8 @@ msgid ""
 "Batch entry {0} {1} was aborted: {2}  Call getNextException to see other "
 "errors in the batch."
 msgstr ""
-"L''�l�ment du batch {0} {1} a �t� annul�. Appeler getNextException pour en "
-"conna�tre la cause."
+"L''�l�ment du batch {0} {1} a �t� annul�. Appeler getNextException pour en "
+"conna�tre la cause."
 
 #: src/main/java/org/postgresql/jdbc/BooleanTypeUtil.java:99
 #, java-format
@@ -716,17 +716,17 @@ msgstr "La fonction {0} n''accepte que trois et seulement trois arguments."
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:597
 #, java-format
 msgid "Interval {0} not yet implemented"
-msgstr "L''interval {0} n''est pas encore impl�ment�"
+msgstr "L''interval {0} n''est pas encore impl�ment�"
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:38
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:53
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:71
 msgid "Cannot reference a savepoint after it has been released."
-msgstr "Impossible de r�f�rencer un savepoint apr�s qu''il ait �t� lib�r�."
+msgstr "Impossible de r�f�rencer un savepoint apr�s qu''il ait �t� lib�r�."
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:43
 msgid "Cannot retrieve the id of a named savepoint."
-msgstr "Impossible de retrouver l''identifiant d''un savepoint nomm�."
+msgstr "Impossible de retrouver l''identifiant d''un savepoint nomm�."
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:58
 msgid "Cannot retrieve the name of an unnamed savepoint."
@@ -736,13 +736,13 @@ msgstr "Impossible de retrouver le nom d''un savepoint sans nom."
 #: src/main/java/org/postgresql/jdbc/PgArray.java:847
 #, java-format
 msgid "The array index is out of range: {0}"
-msgstr "L''indice du tableau est hors limites�: {0}"
+msgstr "L''indice du tableau est hors limites�: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgArray.java:177
 #: src/main/java/org/postgresql/jdbc/PgArray.java:864
 #, java-format
 msgid "The array index is out of range: {0}, number of elements: {1}."
-msgstr "L''indice du tableau est hors limites�: {0}, nombre d''�l�ments�: {1}."
+msgstr "L''indice du tableau est hors limites�: {0}, nombre d''�l�ments�: {1}."
 
 #: src/main/java/org/postgresql/jdbc/PgArray.java:209
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1908
@@ -754,21 +754,21 @@ msgid ""
 "was created in.  The most common example of this is storing 8bit data in a "
 "SQL_ASCII database."
 msgstr ""
-"Des donn�es de caract�res invalides ont �t� trouv�es. C''est probablement "
-"caus� par le stockage de caract�res invalides pour le jeu de caract�res de "
-"cr�ation de la base. L''exemple le plus courant est le stockage de donn�es "
+"Des donn�es de caract�res invalides ont �t� trouv�es. C''est probablement "
+"caus� par le stockage de caract�res invalides pour le jeu de caract�res de "
+"cr�ation de la base. L''exemple le plus courant est le stockage de donn�es "
 "8bit dans une base SQL_ASCII."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:85
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:95
 msgid "A CallableStatement was executed with nothing returned."
-msgstr "Un CallableStatement a �t� ex�cut� mais n''a rien retourn�."
+msgstr "Un CallableStatement a �t� ex�cut� mais n''a rien retourn�."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:106
 #, fuzzy
 msgid "A CallableStatement was executed with an invalid number of parameters"
 msgstr ""
-"Un CallableStatement a �t� ex�cut� avec un nombre de param�tres incorrect"
+"Un CallableStatement a �t� ex�cut� avec un nombre de param�tres incorrect"
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:144
 #, java-format
@@ -776,20 +776,20 @@ msgid ""
 "A CallableStatement function was executed and the out parameter {0} was of "
 "type {1} however type {2} was registered."
 msgstr ""
-"Une fonction CallableStatement a �t� ex�cut�e et le param�tre en sortie {0} "
-"�tait du type {1} alors que le type {2} �tait pr�vu."
+"Une fonction CallableStatement a �t� ex�cut�e et le param�tre en sortie {0} "
+"�tait du type {1} alors que le type {2} �tait pr�vu."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:206
 msgid ""
 "This statement does not declare an OUT parameter.  Use '{' ?= call ... '}' "
 "to declare one."
 msgstr ""
-"Cette requ�te ne d�clare pas de param�tre OUT. Utilisez '{' ?= call ... '}' "
-"pour en d�clarer un."
+"Cette requ�te ne d�clare pas de param�tre OUT. Utilisez '{' ?= call ... '}' "
+"pour en d�clarer un."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:229
 msgid "wasNull cannot be call before fetching a result."
-msgstr "wasNull ne peut pas �tre appel� avant la r�cup�ration d''un r�sultat."
+msgstr "wasNull ne peut pas �tre appel� avant la r�cup�ration d''un r�sultat."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:367
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:386
@@ -798,38 +798,38 @@ msgid ""
 "Parameter of type {0} was registered, but call to get{1} (sqltype={2}) was "
 "made."
 msgstr ""
-"Un param�tre de type {0} a �t� enregistr�, mais un appel � get{1} "
-"(sqltype={2}) a �t� fait."
+"Un param�tre de type {0} a �t� enregistr�, mais un appel � get{1} "
+"(sqltype={2}) a �t� fait."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:407
 msgid ""
 "A CallableStatement was declared, but no call to registerOutParameter(1, "
 "<some type>) was made."
 msgstr ""
-"Un CallableStatement a �t� d�clar�, mais aucun appel � "
-"registerOutParameter(1, <un type>) n''a �t� fait."
+"Un CallableStatement a �t� d�clar�, mais aucun appel � "
+"registerOutParameter(1, <un type>) n''a �t� fait."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:413
 msgid "No function outputs were registered."
-msgstr "Aucune fonction outputs n''a �t� enregistr�e."
+msgstr "Aucune fonction outputs n''a �t� enregistr�e."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:419
 msgid ""
 "Results cannot be retrieved from a CallableStatement before it is executed."
 msgstr ""
-"Les r�sultats ne peuvent �tre r�cup�r�s � partir d''un CallableStatement "
-"avant qu''il ne soit ex�cut�."
+"Les r�sultats ne peuvent �tre r�cup�r�s � partir d''un CallableStatement "
+"avant qu''il ne soit ex�cut�."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:686
 #, fuzzy, java-format
 msgid "Unsupported type conversion to {1}."
-msgstr "Valeur de type non support�e�: {0}"
+msgstr "Valeur de type non support�e�: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:262
 #, java-format
 msgid "Unsupported value for stringtype parameter: {0}"
 msgstr ""
-"Valeur non support�e pour les param�tre de type cha�ne de caract�res�: {0}"
+"Valeur non support�e pour les param�tre de type cha�ne de caract�res�: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:463
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:104
@@ -843,13 +843,13 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:563
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:568
 msgid "No results were returned by the query."
-msgstr "Aucun r�sultat retourn� par la requ�te."
+msgstr "Aucun r�sultat retourn� par la requ�te."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:481
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:499
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:258
 msgid "A result was returned when none was expected."
-msgstr "Un r�sultat a �t� retourn� alors qu''aucun n''�tait attendu."
+msgstr "Un r�sultat a �t� retourn� alors qu''aucun n''�tait attendu."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:604
 msgid "Custom type maps are not supported."
@@ -858,18 +858,18 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:646
 #, java-format
 msgid "Failed to create object for: {0}."
-msgstr "�chec � la cr�ation de l''objet pour�: {0}."
+msgstr "�chec � la cr�ation de l''objet pour�: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:700
 #, java-format
 msgid "Unable to load the class {0} responsible for the datatype {1}"
-msgstr "Incapable de charger la classe {0} responsable du type de donn�es {1}"
+msgstr "Incapable de charger la classe {0} responsable du type de donn�es {1}"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:761
 msgid ""
 "Cannot change transaction read-only property in the middle of a transaction."
 msgstr ""
-"Impossible de changer la propri�t� read-only d''une transaction au milieu "
+"Impossible de changer la propri�t� read-only d''une transaction au milieu "
 "d''une transaction."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:846
@@ -881,7 +881,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1529
 #, fuzzy
 msgid "This connection has been closed."
-msgstr "La connexion a �t� ferm�e."
+msgstr "La connexion a �t� ferm�e."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:867
 msgid "Cannot rollback when autoCommit is enabled."
@@ -897,21 +897,21 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:926
 #, java-format
 msgid "Transaction isolation level {0} not supported."
-msgstr "Le niveau d''isolation de transaction {0} n''est pas support�."
+msgstr "Le niveau d''isolation de transaction {0} n''est pas support�."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:975
 msgid "Finalizing a Connection that was never closed:"
-msgstr "Destruction d''une connection qui n''a jamais �t� ferm�e:"
+msgstr "Destruction d''une connection qui n''a jamais �t� ferm�e:"
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1042
 msgid "Unable to translate data into the desired encoding."
-msgstr "Impossible de traduire les donn�es dans l''encodage d�sir�."
+msgstr "Impossible de traduire les donn�es dans l''encodage d�sir�."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1105
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:929
 msgid "Fetch size must be a value greater to or equal to 0."
-msgstr "Fetch size doit �tre une valeur sup�rieur ou �gal � 0."
+msgstr "Fetch size doit �tre une valeur sup�rieur ou �gal � 0."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1425
@@ -936,12 +936,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1506
 #, fuzzy, java-format
 msgid "Failed to set ClientInfo property: {0}"
-msgstr "�chec � la cr�ation de l''objet pour�: {0}."
+msgstr "�chec � la cr�ation de l''objet pour�: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1516
 #, fuzzy
 msgid "ClientInfo property not supported."
-msgstr "Le renvoi des cl�s automatiquement g�n�r�es n''est pas support�."
+msgstr "Le renvoi des cl�s automatiquement g�n�r�es n''est pas support�."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1542
 msgid "One or more ClientInfo failed."
@@ -950,7 +950,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1642
 #, fuzzy
 msgid "Network timeout must be a value greater than or equal to 0."
-msgstr "Query timeout doit �tre une valeur sup�rieure ou �gale � 0."
+msgstr "Query timeout doit �tre une valeur sup�rieure ou �gale � 0."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1654
 msgid "Unable to set network timeout."
@@ -963,35 +963,35 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1682
 #, java-format
 msgid "Unknown ResultSet holdability setting: {0}."
-msgstr "Param�tre holdability du ResultSet inconnu�: {0}."
+msgstr "Param�tre holdability du ResultSet inconnu�: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1700
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1721
 msgid "Cannot establish a savepoint in auto-commit mode."
-msgstr "Impossible d''�tablir un savepoint en mode auto-commit."
+msgstr "Impossible d''�tablir un savepoint en mode auto-commit."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1790
 msgid "Returning autogenerated keys is not supported."
-msgstr "Le renvoi des cl�s automatiquement g�n�r�es n''est pas support�."
+msgstr "Le renvoi des cl�s automatiquement g�n�r�es n''est pas support�."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:65
 msgid ""
 "Unable to determine a value for MaxIndexKeys due to missing system catalog "
 "data."
 msgstr ""
-"Incapable de d�terminer la valeur de MaxIndexKeys en raison de donn�es "
-"manquante dans lecatalogue syst�me."
+"Incapable de d�terminer la valeur de MaxIndexKeys en raison de donn�es "
+"manquante dans lecatalogue syst�me."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:88
 msgid "Unable to find name datatype in the system catalogs."
 msgstr ""
-"Incapable de trouver le type de donn�e name dans les catalogues syst�mes."
+"Incapable de trouver le type de donn�e name dans les catalogues syst�mes."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:327
 #, fuzzy
 msgid "Unable to find keywords in the system catalogs."
 msgstr ""
-"Incapable de trouver le type de donn�e name dans les catalogues syst�mes."
+"Incapable de trouver le type de donn�e name dans les catalogues syst�mes."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1110
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:2819
@@ -1041,7 +1041,7 @@ msgstr ""
 #, java-format
 msgid "The parameter index is out of range: {0}, number of parameters: {1}."
 msgstr ""
-"L''indice du param�tre est hors limites�: {0}, nombre de param�tres�: {1}."
+"L''indice du param�tre est hors limites�: {0}, nombre de param�tres�: {1}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:90
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:113
@@ -1050,8 +1050,8 @@ msgstr ""
 msgid ""
 "Can''t use query methods that take a query string on a PreparedStatement."
 msgstr ""
-"Impossible d''utiliser les fonctions de requ�te qui utilisent une cha�ne de "
-"caract�res sur un PreparedStatement."
+"Impossible d''utiliser les fonctions de requ�te qui utilisent une cha�ne de "
+"caract�res sur un PreparedStatement."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:254
 msgid "Unknown Types value."
@@ -1068,19 +1068,19 @@ msgstr "Longueur de flux invalide {0}."
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:414
 #, java-format
 msgid "The JVM claims not to support the {0} encoding."
-msgstr "La JVM pr�tend ne pas supporter l''encodage {0}."
+msgstr "La JVM pr�tend ne pas supporter l''encodage {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:417
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1124
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1156
 msgid "Provided InputStream failed."
-msgstr "L''InputStream fourni a �chou�."
+msgstr "L''InputStream fourni a �chou�."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:463
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1095
 #, java-format
 msgid "Unknown type {0}."
-msgstr "Type inconnu�: {0}."
+msgstr "Type inconnu�: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:480
 msgid "No hstore extension installed."
@@ -1097,7 +1097,7 @@ msgstr "Impossible de convertir une instance de {0} vers le type {1}"
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:685
 #, java-format
 msgid "Unsupported Types value: {0}"
-msgstr "Valeur de type non support�e�: {0}"
+msgstr "Valeur de type non support�e�: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:897
 #, java-format
@@ -1110,19 +1110,19 @@ msgid ""
 "Can''t infer the SQL type to use for an instance of {0}. Use setObject() "
 "with an explicit Types value to specify the type to use."
 msgstr ""
-"Impossible de d�duire le type SQL � utiliser pour une instance de {0}. "
-"Utilisez setObject() avec une valeur de type explicite pour sp�cifier le "
-"type � utiliser."
+"Impossible de d�duire le type SQL � utiliser pour une instance de {0}. "
+"Utilisez setObject() avec une valeur de type explicite pour sp�cifier le "
+"type � utiliser."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1132
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1233
 msgid "Unexpected error writing large object to database."
-msgstr "Erreur inattendue pendant l''�criture de large object dans la base."
+msgstr "Erreur inattendue pendant l''�criture de large object dans la base."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1177
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1206
 msgid "Provided Reader failed."
-msgstr "Le Reader fourni a �chou�."
+msgstr "Le Reader fourni a �chou�."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1456
 #: src/main/java/org/postgresql/util/StreamWrapper.java:56
@@ -1134,7 +1134,7 @@ msgid ""
 "Operation requires a scrollable ResultSet, but this ResultSet is "
 "FORWARD_ONLY."
 msgstr ""
-"L''op�ration n�cessite un scrollable ResultSet, mais ce ResultSet est "
+"L''op�ration n�cessite un scrollable ResultSet, mais ce ResultSet est "
 "FORWARD_ONLY."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:479
@@ -1154,14 +1154,14 @@ msgstr "Impossible de convertir une instance de type {0} vers le type {1}"
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1799
 msgid "Can''t use relative move methods while on the insert row."
 msgstr ""
-"Impossible d''utiliser les fonctions de d�placement relatif pendant "
+"Impossible d''utiliser les fonctions de d�placement relatif pendant "
 "l''insertion d''une ligne."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:895
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:921
 #, java-format
 msgid "Invalid fetch direction constant: {0}."
-msgstr "Constante de direction pour la r�cup�ration invalide�: {0}."
+msgstr "Constante de direction pour la r�cup�ration invalide�: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:905
 msgid "Cannot call cancelRowUpdates() when on the insert row."
@@ -1177,7 +1177,7 @@ msgid ""
 "Currently positioned before the start of the ResultSet.  You cannot call "
 "deleteRow() here."
 msgstr ""
-"Actuellement positionn� avant le d�but du ResultSet. Vous ne pouvez pas "
+"Actuellement positionn� avant le d�but du ResultSet. Vous ne pouvez pas "
 "appeler deleteRow() ici."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:933
@@ -1185,7 +1185,7 @@ msgid ""
 "Currently positioned after the end of the ResultSet.  You cannot call "
 "deleteRow() here."
 msgstr ""
-"Actuellement positionn� apr�s la fin du ResultSet. Vous ne pouvez pas "
+"Actuellement positionn� apr�s la fin du ResultSet. Vous ne pouvez pas "
 "appeler deleteRow() ici."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:937
@@ -1199,7 +1199,7 @@ msgstr "Pas sur la ligne en insertion."
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:976
 msgid "You must specify at least one column value to insert a row."
 msgstr ""
-"Vous devez sp�cifier au moins une valeur de colonne pour ins�rer une ligne."
+"Vous devez sp�cifier au moins une valeur de colonne pour ins�rer une ligne."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1121
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1718
@@ -1207,16 +1207,16 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2439
 #, java-format
 msgid "The JVM claims not to support the encoding: {0}"
-msgstr "La JVM pr�tend ne pas supporter l''encodage: {0}"
+msgstr "La JVM pr�tend ne pas supporter l''encodage: {0}"
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1248
 msgid "Can''t refresh the insert row."
-msgstr "Impossible de rafra�chir la ligne ins�r�e."
+msgstr "Impossible de rafra�chir la ligne ins�r�e."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1313
 msgid "Cannot call updateRow() when on the insert row."
 msgstr ""
-"Impossible d''appeler updateRow() tant que l''on est sur la ligne ins�r�e."
+"Impossible d''appeler updateRow() tant que l''on est sur la ligne ins�r�e."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1320
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3082
@@ -1224,19 +1224,19 @@ msgid ""
 "Cannot update the ResultSet because it is either before the start or after "
 "the end of the results."
 msgstr ""
-"Impossible de mettre � jour le ResultSet car c''est soit avant le d�but ou "
-"apr�s la fin des r�sultats."
+"Impossible de mettre � jour le ResultSet car c''est soit avant le d�but ou "
+"apr�s la fin des r�sultats."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1496
 msgid "ResultSets with concurrency CONCUR_READ_ONLY cannot be updated."
 msgstr ""
-"Les ResultSets avec la concurrence CONCUR_READ_ONLY ne peuvent �tre mis � "
+"Les ResultSets avec la concurrence CONCUR_READ_ONLY ne peuvent �tre mis � "
 "jour."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1564
 #, java-format
 msgid "No primary key found for table {0}."
-msgstr "Pas de cl� primaire trouv�e pour la table {0}."
+msgstr "Pas de cl� primaire trouv�e pour la table {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1995
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2000
@@ -1253,12 +1253,12 @@ msgstr "Pas de cl
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3070
 #, java-format
 msgid "Bad value for type {0} : {1}"
-msgstr "Mauvaise valeur pour le type {0}�: {1}"
+msgstr "Mauvaise valeur pour le type {0}�: {1}"
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2592
 #, java-format
 msgid "The column name {0} was not found in this ResultSet."
-msgstr "Le nom de colonne {0} n''a pas �t� trouv� dans ce ResultSet."
+msgstr "Le nom de colonne {0} n''a pas �t� trouv� dans ce ResultSet."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2728
 msgid ""
@@ -1266,19 +1266,19 @@ msgid ""
 "select only one table, and must select all primary keys from that table. See "
 "the JDBC 2.1 API Specification, section 5.6 for more details."
 msgstr ""
-"Le ResultSet n''est pas modifiable. La requ�te qui a g�n�r� ce r�sultat doit "
-"s�lectionner seulement une table, et doit s�lectionner toutes les cl�s "
-"primaires de cette table. Voir la sp�cification de l''API JDBC 2.1, section "
-"5.6 pour plus de d�tails."
+"Le ResultSet n''est pas modifiable. La requ�te qui a g�n�r� ce r�sultat doit "
+"s�lectionner seulement une table, et doit s�lectionner toutes les cl�s "
+"primaires de cette table. Voir la sp�cification de l''API JDBC 2.1, section "
+"5.6 pour plus de d�tails."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2740
 msgid "This ResultSet is closed."
-msgstr "Ce ResultSet est ferm�."
+msgstr "Ce ResultSet est ferm�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2771
 msgid "ResultSet not positioned properly, perhaps you need to call next."
 msgstr ""
-"Le ResultSet n''est pas positionn� correctement, vous devez peut-�tre "
+"Le ResultSet n''est pas positionn� correctement, vous devez peut-�tre "
 "appeler next()."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3102
@@ -1311,7 +1311,7 @@ msgstr "Drapeau invalide"
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3430
 #, fuzzy, java-format
 msgid "conversion to {0} from {1} not supported"
-msgstr "Le niveau d''isolation de transaction {0} n''est pas support�."
+msgstr "Le niveau d''isolation de transaction {0} n''est pas support�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3370
 #, fuzzy
@@ -1330,7 +1330,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:202
 #, fuzzy
 msgid "Unable to create SAXResult for SQLXML."
-msgstr "�chec � la cr�ation de l''objet pour�: {0}."
+msgstr "�chec � la cr�ation de l''objet pour�: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:217
 msgid "Unable to create StAXResult for SQLXML"
@@ -1339,12 +1339,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:222
 #, fuzzy, java-format
 msgid "Unknown XML Result class: {0}"
-msgstr "Param�tre holdability du ResultSet inconnu�: {0}."
+msgstr "Param�tre holdability du ResultSet inconnu�: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:235
 #, fuzzy
 msgid "This SQLXML object has already been freed."
-msgstr "Cette PooledConnection a d�j� �t� ferm�e."
+msgstr "Cette PooledConnection a d�j� �t� ferm�e."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:244
 msgid ""
@@ -1369,7 +1369,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:237
 msgid "Multiple ResultSets were returned by the query."
-msgstr "Plusieurs ResultSets ont �t� retourn�s par la requ�te."
+msgstr "Plusieurs ResultSets ont �t� retourn�s par la requ�te."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:319
 msgid "Can''t use executeWithFlags(int) on a Statement."
@@ -1378,27 +1378,27 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:518
 msgid "Maximum number of rows must be a value grater than or equal to 0."
 msgstr ""
-"Le nombre maximum de lignes doit �tre une valeur sup�rieure ou �gale � 0."
+"Le nombre maximum de lignes doit �tre une valeur sup�rieure ou �gale � 0."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:564
 msgid "Query timeout must be a value greater than or equals to 0."
-msgstr "Query timeout doit �tre une valeur sup�rieure ou �gale � 0."
+msgstr "Query timeout doit �tre une valeur sup�rieure ou �gale � 0."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:606
 msgid "The maximum field size must be a value greater than or equal to 0."
 msgstr ""
-"La taille maximum des champs doit �tre une valeur sup�rieure ou �gale � 0."
+"La taille maximum des champs doit �tre une valeur sup�rieure ou �gale � 0."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:709
 msgid "This statement has been closed."
-msgstr "Ce statement a �t� ferm�."
+msgstr "Ce statement a �t� ferm�."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1078
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1208
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1236
 #, fuzzy
 msgid "Returning autogenerated keys by column index is not supported."
-msgstr "Le renvoi des cl�s automatiquement g�n�r�es n''est pas support�."
+msgstr "Le renvoi des cl�s automatiquement g�n�r�es n''est pas support�."
 
 #: src/main/java/org/postgresql/jdbc/SslMode.java:78
 #, fuzzy, java-format
@@ -1409,7 +1409,7 @@ msgstr "Longueur de flux invalide {0}."
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:424
 #, fuzzy, java-format
 msgid "Bad value for type timestamp/date/time: {1}"
-msgstr "Mauvaise valeur pour le type {0}�: {1}"
+msgstr "Mauvaise valeur pour le type {0}�: {1}"
 
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:981
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1038
@@ -1417,7 +1417,7 @@ msgstr "Mauvaise valeur pour le type {0}
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1131
 #, fuzzy, java-format
 msgid "Unsupported binary encoding of {0}."
-msgstr "Valeur de type non support�e�: {0}"
+msgstr "Valeur de type non support�e�: {0}"
 
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:244
 msgid "typname"
@@ -1453,12 +1453,12 @@ msgstr ""
 #: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:249
 #: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:290
 msgid "Large Objects may not be used in auto-commit mode."
-msgstr "Les Large Objects ne devraient pas �tre utilis�s en mode auto-commit."
+msgstr "Les Large Objects ne devraient pas �tre utilis�s en mode auto-commit."
 
 #: src/main/java/org/postgresql/osgi/PGDataSourceFactory.java:82
 #, fuzzy, java-format
 msgid "Unsupported properties: {0}"
-msgstr "Valeur de type non support�e�: {0}"
+msgstr "Valeur de type non support�e�: {0}"
 
 #: src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java:38
 msgid "Server does not support temporary replication slots"
@@ -1538,7 +1538,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/LibPQFactory.java:55
 #, fuzzy, java-format
 msgid "The password callback class provided {0} could not be instantiated."
-msgstr "La classe SSLSocketFactory fournie {0} n''a pas pu �tre instanci�e."
+msgstr "La classe SSLSocketFactory fournie {0} n''a pas pu �tre instanci�e."
 
 #: src/main/java/org/postgresql/ssl/LibPQFactory.java:140
 #, java-format
@@ -1567,7 +1567,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/MakeSSL.java:70
 #, fuzzy, java-format
 msgid "The HostnameVerifier class provided {0} could not be instantiated."
-msgstr "La classe SSLSocketFactory fournie {0} n''a pas pu �tre instanci�e."
+msgstr "La classe SSLSocketFactory fournie {0} n''a pas pu �tre instanci�e."
 
 #: src/main/java/org/postgresql/ssl/MakeSSL.java:81
 #, java-format
@@ -1582,7 +1582,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:90
 #, fuzzy, java-format
 msgid "No certificates found for hostname {0}"
-msgstr "Pas de cl� primaire trouv�e pour la table {0}."
+msgstr "Pas de cl� primaire trouv�e pour la table {0}."
 
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:109
 #, java-format
@@ -1592,7 +1592,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:126
 #, fuzzy, java-format
 msgid "Unable to parse certificates for hostname {0}"
-msgstr "Incapable de lier les valeurs des param�tres pour la commande."
+msgstr "Incapable de lier les valeurs des param�tres pour la commande."
 
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:157
 #, java-format
@@ -1631,7 +1631,7 @@ msgstr ""
 #, fuzzy
 msgid "Unable to find pkcs12 keystore."
 msgstr ""
-"Incapable de trouver le type de donn�e name dans les catalogues syst�mes."
+"Incapable de trouver le type de donn�e name dans les catalogues syst�mes."
 
 #: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:91
 msgid "The sslfactoryarg property may not be empty."
@@ -1659,7 +1659,7 @@ msgstr ""
 #, fuzzy
 msgid "An error occurred reading the certificate"
 msgstr ""
-"Une erreur s''est produite pendant l''�tablissement de la connexion SSL."
+"Une erreur s''est produite pendant l''�tablissement de la connexion SSL."
 
 #: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:167
 msgid "No X509TrustManager found"
@@ -1667,7 +1667,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/util/PGInterval.java:210
 msgid "Conversion of interval failed"
-msgstr "La conversion de l''intervalle a �chou�"
+msgstr "La conversion de l''intervalle a �chou�"
 
 #: src/main/java/org/postgresql/util/PGPropertyMaxResultBufferParser.java:204
 #, java-format
@@ -1678,7 +1678,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/util/PGmoney.java:61
 msgid "Conversion of money failed."
-msgstr "La conversion de money a �chou�."
+msgstr "La conversion de money a �chou�."
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:43
 #, java-format
@@ -1691,42 +1691,42 @@ msgstr ""
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:187
 #, java-format
 msgid "Detail: {0}"
-msgstr "D�tail�: {0}"
+msgstr "D�tail�: {0}"
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:192
 #, java-format
 msgid "Hint: {0}"
-msgstr "Indice�: {0}"
+msgstr "Indice�: {0}"
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:196
 #, java-format
 msgid "Position: {0}"
-msgstr "Position�: {0}"
+msgstr "Position�: {0}"
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:200
 #, java-format
 msgid "Where: {0}"
-msgstr "O��: {0}"
+msgstr "O��: {0}"
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:206
 #, java-format
 msgid "Internal Query: {0}"
-msgstr "Requ�te interne: {0}"
+msgstr "Requ�te interne: {0}"
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:210
 #, java-format
 msgid "Internal Position: {0}"
-msgstr "Position interne�: {0}"
+msgstr "Position interne�: {0}"
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:217
 #, java-format
 msgid "Location: File: {0}, Routine: {1}, Line: {2}"
-msgstr "Localisation�: Fichier�: {0}, Routine�: {1}, Ligne�: {2}"
+msgstr "Localisation�: Fichier�: {0}, Routine�: {1}, Ligne�: {2}"
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:222
 #, java-format
 msgid "Server SQLState: {0}"
-msgstr "SQLState serveur�: {0}"
+msgstr "SQLState serveur�: {0}"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:128
 msgid ""
@@ -1745,16 +1745,16 @@ msgstr "Drapeaux invalides"
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:276
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:491
 msgid "xid must not be null"
-msgstr "xid ne doit pas �tre nul"
+msgstr "xid ne doit pas �tre nul"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:194
 msgid "Connection is busy with another transaction"
-msgstr "La connection est occup�e avec une autre transaction"
+msgstr "La connection est occup�e avec une autre transaction"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:203
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:286
 msgid "suspend/resume not implemented"
-msgstr "suspend/resume pas impl�ment�"
+msgstr "suspend/resume pas impl�ment�"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:211
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:218
@@ -1767,7 +1767,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:233
 msgid "Error disabling autocommit"
-msgstr "Erreur en d�sactivant autocommit"
+msgstr "Erreur en d�sactivant autocommit"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:280
 #, fuzzy, java-format
@@ -1793,18 +1793,18 @@ msgid ""
 "Not implemented: Prepare must be issued using the same connection that "
 "started the transaction. currentXid={0}, prepare xid={1}"
 msgstr ""
-"Pas impl�ment�: Prepare doit �tre envoy� sur la m�me connection qui a "
-"d�marr� la transaction"
+"Pas impl�ment�: Prepare doit �tre envoy� sur la m�me connection qui a "
+"d�marr� la transaction"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:340
 #, fuzzy, java-format
 msgid "Prepare called before end. prepare xid={0}, state={1}"
-msgstr "Pr�paration appel�e avant la fin"
+msgstr "Pr�paration appel�e avant la fin"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:360
 #, fuzzy, java-format
 msgid "Error preparing transaction. prepare xid={0}"
-msgstr "Erreur en pr�parant la transaction"
+msgstr "Erreur en pr�parant la transaction"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:416
 msgid "Error during recover"
@@ -1815,7 +1815,7 @@ msgstr "Erreur durant la restauration"
 msgid ""
 "Error rolling back prepared transaction. rollback xid={0}, preparedXid={1}, "
 "currentXid={2}"
-msgstr "Erreur en annulant une transaction pr�par�e"
+msgstr "Erreur en annulant une transaction pr�par�e"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:521
 #, java-format
@@ -1828,8 +1828,8 @@ msgid ""
 "Not implemented: one-phase commit must be issued using the same connection "
 "that was used to start it"
 msgstr ""
-"Pas impl�ment�: le commit � une phase doit avoir lieu en utilisant la m�me "
-"connection que celle o� il a commenc�"
+"Pas impl�ment�: le commit � une phase doit avoir lieu en utilisant la m�me "
+"connection que celle o� il a commenc�"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:533
 #, java-format
@@ -1839,12 +1839,12 @@ msgstr ""
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:537
 #, fuzzy, java-format
 msgid "commit called before end. commit xid={0}, state={1}"
-msgstr "Commit appel� avant la fin"
+msgstr "Commit appel� avant la fin"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:548
 #, fuzzy, java-format
 msgid "Error during one-phase commit. commit xid={0}"
-msgstr "Erreur pendant le commit � une phase"
+msgstr "Erreur pendant le commit � une phase"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:576
 #, fuzzy, java-format
@@ -1852,20 +1852,20 @@ msgid ""
 "Not implemented: 2nd phase commit must be issued using an idle connection. "
 "commit xid={0}, currentXid={1}, state={2}, transactionState={3}"
 msgstr ""
-"Pas impl�ment�: le commit � deux phase doit �tre envoy� sur une connection "
-"inutilis�e"
+"Pas impl�ment�: le commit � deux phase doit �tre envoy� sur une connection "
+"inutilis�e"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:609
 #, fuzzy, java-format
 msgid ""
 "Error committing prepared transaction. commit xid={0}, preparedXid={1}, "
 "currentXid={2}"
-msgstr "Erreur en annulant une transaction pr�par�e"
+msgstr "Erreur en annulant une transaction pr�par�e"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:626
 #, fuzzy, java-format
 msgid "Heuristic commit/rollback not supported. forget xid={0}"
-msgstr "Heuristic commit/rollback non support�"
+msgstr "Heuristic commit/rollback non support�"
 
 #~ msgid "The driver currently does not support COPY operations."
-#~ msgstr "Le pilote ne supporte pas actuellement les op�rations COPY."
+#~ msgstr "Le pilote ne supporte pas actuellement les op�rations COPY."
diff --git a/src/main/java/org/postgresql/translation/it.po b/src/main/java/org/postgresql/translation/it.po
index 85912030aea8d062eb251ada3ac7e7d7d3096999..d226e2310199cee71ae1368039bc7bfa398be731 100644
--- a/src/main/java/org/postgresql/translation/it.po
+++ b/src/main/java/org/postgresql/translation/it.po
@@ -20,8 +20,8 @@ msgstr ""
 #: src/main/java/org/postgresql/Driver.java:221
 msgid "Error loading default settings from driverconfig.properties"
 msgstr ""
-"Si � verificato un errore caricando le impostazioni predefinite da "
-"�driverconfig.properties�."
+"Si � verificato un errore caricando le impostazioni predefinite da "
+"�driverconfig.properties�."
 
 #: src/main/java/org/postgresql/Driver.java:233
 msgid "Properties for the driver contains a non-string value for the key "
@@ -40,21 +40,21 @@ msgid ""
 "Something unusual has occurred to cause the driver to fail. Please report "
 "this exception."
 msgstr ""
-"Qualcosa di insolito si � verificato causando il fallimento del driver. Per "
+"Qualcosa di insolito si � verificato causando il fallimento del driver. Per "
 "favore riferire all''autore del driver questa eccezione."
 
 #: src/main/java/org/postgresql/Driver.java:422
 msgid "Connection attempt timed out."
-msgstr "Il tentativo di connessione � scaduto."
+msgstr "Il tentativo di connessione � scaduto."
 
 #: src/main/java/org/postgresql/Driver.java:435
 msgid "Interrupted while attempting to connect."
-msgstr "Si � verificata una interruzione durante il tentativo di connessione."
+msgstr "Si � verificata una interruzione durante il tentativo di connessione."
 
 #: src/main/java/org/postgresql/Driver.java:691
 #, java-format
 msgid "Method {0} is not yet implemented."
-msgstr "Il metodo �{0}� non � stato ancora implementato."
+msgstr "Il metodo �{0}� non � stato ancora implementato."
 
 #: src/main/java/org/postgresql/PGProperty.java:818
 #: src/main/java/org/postgresql/PGProperty.java:838
@@ -86,7 +86,7 @@ msgstr "Fallita la conversione di un ``box'': {0}."
 #: src/main/java/org/postgresql/copy/PGCopyOutputStream.java:95
 #, fuzzy
 msgid "This copy stream is closed."
-msgstr "Questo �ResultSet� � chiuso."
+msgstr "Questo �ResultSet� � chiuso."
 
 #: src/main/java/org/postgresql/copy/PGCopyInputStream.java:109
 msgid "Read from copy failed."
@@ -101,14 +101,14 @@ msgstr ""
 #, fuzzy, java-format
 msgid "Unable to parse the count in command completion tag: {0}."
 msgstr ""
-"Impossibile interpretare il numero degli aggiornamenti nel �tag� di "
+"Impossibile interpretare il numero degli aggiornamenti nel �tag� di "
 "completamento del comando: {0}."
 
 #: src/main/java/org/postgresql/core/ConnectionFactory.java:57
 #, java-format
 msgid "A connection could not be made using the requested protocol {0}."
 msgstr ""
-"Non � stato possibile attivare la connessione utilizzando il protocollo "
+"Non � stato possibile attivare la connessione utilizzando il protocollo "
 "richiesto {0}."
 
 #: src/main/java/org/postgresql/core/Oid.java:128
@@ -120,26 +120,26 @@ msgstr ""
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:129
 #, java-format
 msgid "Illegal UTF-8 sequence: initial byte is {0}: {1}"
-msgstr "Sequenza UTF-8 illegale: il byte iniziale � {0}: {1}"
+msgstr "Sequenza UTF-8 illegale: il byte iniziale � {0}: {1}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:135
 #, java-format
 msgid "Illegal UTF-8 sequence: final value is out of range: {0}"
 msgstr ""
-"Sequenza UTF-8 illegale: il valore finale � fuori dall''intervallo permesso: "
+"Sequenza UTF-8 illegale: il valore finale � fuori dall''intervallo permesso: "
 "{0}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:148
 #, java-format
 msgid "Illegal UTF-8 sequence: final value is a surrogate value: {0}"
-msgstr "Sequenza UTF-8 illegale: il valore � finale � un surrogato: {0}"
+msgstr "Sequenza UTF-8 illegale: il valore � finale � un surrogato: {0}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:164
 #, java-format
 msgid ""
 "Illegal UTF-8 sequence: byte {0} of {1} byte sequence is not 10xxxxxx: {2}"
 msgstr ""
-"Sequenza UTF-8 illegale: il byte {0} di una sequenza di {1} byte non � "
+"Sequenza UTF-8 illegale: il byte {0} di una sequenza di {1} byte non � "
 "10xxxxxx: {2}"
 
 #: src/main/java/org/postgresql/core/OptimizedUTF8Encoder.java:200
@@ -153,13 +153,13 @@ msgstr ""
 #, java-format
 msgid "Premature end of input stream, expected {0} bytes, but only read {1}."
 msgstr ""
-"Il flusso di input � stato interrotto, sono arrivati {1} byte al posto dei "
+"Il flusso di input � stato interrotto, sono arrivati {1} byte al posto dei "
 "{0} attesi."
 
 #: src/main/java/org/postgresql/core/PGStream.java:606
 #, java-format
 msgid "Expected an EOF from server, got: {0}"
-msgstr "Ricevuto dal server �{0}� mentre era atteso un EOF"
+msgstr "Ricevuto dal server �{0}� mentre era atteso un EOF"
 
 #: src/main/java/org/postgresql/core/PGStream.java:666
 #, java-format
@@ -176,19 +176,19 @@ msgstr ""
 
 #: src/main/java/org/postgresql/core/SetupQueryRunner.java:64
 msgid "An unexpected result was returned by a query."
-msgstr "Un risultato inaspettato � stato ricevuto dalla query."
+msgstr "Un risultato inaspettato � stato ricevuto dalla query."
 
 #: src/main/java/org/postgresql/core/SocketFactoryFactory.java:43
 #, fuzzy, java-format
 msgid "The SocketFactory class provided {0} could not be instantiated."
 msgstr ""
-"La classe �SSLSocketFactory� specificata, �{0}�, non pu� essere istanziata."
+"La classe �SSLSocketFactory� specificata, �{0}�, non pu� essere istanziata."
 
 #: src/main/java/org/postgresql/core/SocketFactoryFactory.java:68
 #, java-format
 msgid "The SSLSocketFactory class provided {0} could not be instantiated."
 msgstr ""
-"La classe �SSLSocketFactory� specificata, �{0}�, non pu� essere istanziata."
+"La classe �SSLSocketFactory� specificata, �{0}�, non pu� essere istanziata."
 
 #: src/main/java/org/postgresql/core/Utils.java:93
 #: src/main/java/org/postgresql/core/Utils.java:110
@@ -215,12 +215,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSetMetaData.java:393
 #, java-format
 msgid "The column index is out of range: {0}, number of columns: {1}."
-msgstr "Indice di colonna, {0}, � maggiore del numero di colonne {1}."
+msgstr "Indice di colonna, {0}, � maggiore del numero di colonne {1}."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:162
 #, fuzzy, java-format
 msgid "Invalid targetServerType value: {0}"
-msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
+msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:286
 #, fuzzy, java-format
@@ -235,7 +235,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:297
 #: src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java:135
 msgid "The connection attempt failed."
-msgstr "Il tentativo di connessione � fallito."
+msgstr "Il tentativo di connessione � fallito."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:312
 #, java-format
@@ -249,7 +249,7 @@ msgstr "Il server non supporta SSL."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:450
 msgid "An error occurred while setting up the SSL connection."
-msgstr "Si � verificato un errore impostando la connessione SSL."
+msgstr "Si � verificato un errore impostando la connessione SSL."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:546
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:573
@@ -258,7 +258,7 @@ msgid ""
 "provided."
 msgstr ""
 "Il server ha richiesto l''autenticazione con password, ma tale password non "
-"� stata fornita."
+"� stata fornita."
 
 #: src/main/java/org/postgresql/core/v3/ConnectionFactoryImpl.java:680
 msgid ""
@@ -273,7 +273,7 @@ msgid ""
 "the pg_hba.conf file to include the client''s IP address or subnet, and that "
 "it is using an authentication scheme supported by the driver."
 msgstr ""
-"L''autenticazione di tipo {0} non � supportata. Verificare che nel file di "
+"L''autenticazione di tipo {0} non � supportata. Verificare che nel file di "
 "configurazione pg_hba.conf sia presente l''indirizzo IP o la sottorete del "
 "client, e che lo schema di autenticazione utilizzato sia supportato dal "
 "driver."
@@ -306,12 +306,12 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:201
 #, fuzzy
 msgid "Interrupted while waiting to obtain lock on database connection"
-msgstr "Si � verificata una interruzione durante il tentativo di connessione."
+msgstr "Si � verificata una interruzione durante il tentativo di connessione."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:333
 msgid "Unable to bind parameter values for statement."
 msgstr ""
-"Impossibile fare il �bind� dei valori passati come parametri per lo "
+"Impossibile fare il �bind� dei valori passati come parametri per lo "
 "statement."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:339
@@ -322,7 +322,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2465
 #: src/main/java/org/postgresql/util/StreamWrapper.java:141
 msgid "An I/O error occurred while sending to the backend."
-msgstr "Si � verificato un errore di I/O nella spedizione di dati al server."
+msgstr "Si � verificato un errore di I/O nella spedizione di dati al server."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:391
 msgid "Error releasing savepoint"
@@ -332,7 +332,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:614
 #, java-format
 msgid "Expected command status BEGIN, got {0}."
-msgstr "Lo stato del comando avrebbe dovuto essere BEGIN, mentre invece � {0}."
+msgstr "Lo stato del comando avrebbe dovuto essere BEGIN, mentre invece � {0}."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:619
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1744
@@ -343,7 +343,7 @@ msgstr "Stato del comando non previsto: {0}."
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:729
 #, fuzzy
 msgid "An error occurred while trying to get the socket timeout."
-msgstr "Si � verificato un errore di I/O nella spedizione di dati al server."
+msgstr "Si � verificato un errore di I/O nella spedizione di dati al server."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:764
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:840
@@ -354,7 +354,7 @@ msgstr "Risposta di tipo sconosciuto {0}."
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:787
 #, fuzzy
 msgid "An error occurred while trying to reset the socket timeout."
-msgstr "Si � verificato un errore di I/O nella spedizione di dati al server."
+msgstr "Si � verificato un errore di I/O nella spedizione di dati al server."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:885
 msgid "Database connection failed when starting copy"
@@ -408,7 +408,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1126
 #, fuzzy
 msgid "PGStream is closed"
-msgstr "Questo �ResultSet� � chiuso."
+msgstr "Questo �ResultSet� � chiuso."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1187
 #, java-format
@@ -437,7 +437,7 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1263
 #, fuzzy, java-format
 msgid "Unexpected copydata from server for {0}"
-msgstr "Ricevuto dal server �{0}� mentre era atteso un EOF"
+msgstr "Ricevuto dal server �{0}� mentre era atteso un EOF"
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:1323
 #, java-format
@@ -450,8 +450,8 @@ msgid ""
 "Bind message length {0} too long.  This can be caused by very large or "
 "incorrect length specifications on InputStream parameters."
 msgstr ""
-"Il messaggio di �bind� � troppo lungo ({0}). Questo pu� essere causato da "
-"una dimensione eccessiva o non corretta dei parametri dell''�InputStream�."
+"Il messaggio di �bind� � troppo lungo ({0}). Questo pu� essere causato da "
+"una dimensione eccessiva o non corretta dei parametri dell''�InputStream�."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2240
 msgid "Ran out of memory retrieving query results."
@@ -467,8 +467,8 @@ msgid ""
 "The server''s client_encoding parameter was changed to {0}. The JDBC driver "
 "requires client_encoding to be UTF8 for correct operation."
 msgstr ""
-"Il parametro �client_encoding� del server � stato cambiato in {0}. Il driver "
-"JDBC richiede che �client_encoding� sia UNICODE per un corretto "
+"Il parametro �client_encoding� del server � stato cambiato in {0}. Il driver "
+"JDBC richiede che �client_encoding� sia UNICODE per un corretto "
 "funzionamento."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2705
@@ -477,8 +477,8 @@ msgid ""
 "The server''s DateStyle parameter was changed to {0}. The JDBC driver "
 "requires DateStyle to begin with ISO for correct operation."
 msgstr ""
-"Il parametro del server �DateStyle� � stato cambiato in {0}. Il driver JDBC "
-"richiede che �DateStyle� cominci con �ISO� per un corretto funzionamento."
+"Il parametro del server �DateStyle� � stato cambiato in {0}. Il driver JDBC "
+"richiede che �DateStyle� cominci con �ISO� per un corretto funzionamento."
 
 #: src/main/java/org/postgresql/core/v3/QueryExecutorImpl.java:2718
 #, fuzzy, java-format
@@ -486,8 +486,8 @@ msgid ""
 "The server''s standard_conforming_strings parameter was reported as {0}. The "
 "JDBC driver expected on or off."
 msgstr ""
-"Il parametro �client_encoding� del server � stato cambiato in {0}. Il driver "
-"JDBC richiede che �client_encoding� sia UNICODE per un corretto "
+"Il parametro �client_encoding� del server � stato cambiato in {0}. Il driver "
+"JDBC richiede che �client_encoding� sia UNICODE per un corretto "
 "funzionamento."
 
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:270
@@ -498,7 +498,7 @@ msgstr "Nessun valore specificato come parametro {0}."
 #: src/main/java/org/postgresql/core/v3/SimpleParameterList.java:462
 #, fuzzy, java-format
 msgid "Added parameters index out of range: {0}, number of columns: {1}."
-msgstr "Il parametro indice � fuori intervallo: {0}, numero di elementi: {1}."
+msgstr "Il parametro indice � fuori intervallo: {0}, numero di elementi: {1}."
 
 #: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:147
 #, java-format
@@ -508,28 +508,28 @@ msgstr ""
 #: src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java:272
 #, fuzzy
 msgid "This replication stream has been closed."
-msgstr "Questo �Connection� � stato chiuso."
+msgstr "Questo �Connection� � stato chiuso."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:118
 msgid "This PooledConnection has already been closed."
-msgstr "Questo �PooledConnection� � stato chiuso."
+msgstr "Questo �PooledConnection� � stato chiuso."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:314
 msgid ""
 "Connection has been closed automatically because a new connection was opened "
 "for the same PooledConnection or the PooledConnection has been closed."
 msgstr ""
-"La �Connection� � stata chiusa automaticamente perch� una nuova l''ha "
-"sostituita nello stesso �PooledConnection�, oppure il �PooledConnection� � "
+"La �Connection� � stata chiusa automaticamente perch� una nuova l''ha "
+"sostituita nello stesso �PooledConnection�, oppure il �PooledConnection� � "
 "stato chiuso."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:315
 msgid "Connection has been closed."
-msgstr "Questo �Connection� � stato chiuso."
+msgstr "Questo �Connection� � stato chiuso."
 
 #: src/main/java/org/postgresql/ds/PGPooledConnection.java:420
 msgid "Statement has been closed."
-msgstr "Questo �Statement� � stato chiuso."
+msgstr "Questo �Statement� � stato chiuso."
 
 #: src/main/java/org/postgresql/ds/PGPoolingDataSource.java:269
 msgid "Failed to setup DataSource."
@@ -537,26 +537,26 @@ msgstr ""
 
 #: src/main/java/org/postgresql/ds/PGPoolingDataSource.java:371
 msgid "DataSource has been closed."
-msgstr "Questo �DataSource� � stato chiuso."
+msgstr "Questo �DataSource� � stato chiuso."
 
 #: src/main/java/org/postgresql/ds/common/BaseDataSource.java:1270
 #: src/main/java/org/postgresql/ds/common/BaseDataSource.java:1280
 #, fuzzy, java-format
 msgid "Unsupported property name: {0}"
-msgstr "Valore di tipo �{0}� non supportato."
+msgstr "Valore di tipo �{0}� non supportato."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:84
 #, fuzzy, java-format
 msgid "Fastpath call {0} - No result was returned and we expected a numeric."
 msgstr ""
-"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
+"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
 "un intero."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:161
 #, java-format
 msgid "Fastpath call {0} - No result was returned and we expected an integer."
 msgstr ""
-"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
+"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
 "un intero."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:169
@@ -565,14 +565,14 @@ msgid ""
 "Fastpath call {0} - No result was returned or wrong size while expecting an "
 "integer."
 msgstr ""
-"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
+"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
 "un intero."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:186
 #, fuzzy, java-format
 msgid "Fastpath call {0} - No result was returned and we expected a long."
 msgstr ""
-"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
+"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
 "un intero."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:194
@@ -581,13 +581,13 @@ msgid ""
 "Fastpath call {0} - No result was returned or wrong size while expecting a "
 "long."
 msgstr ""
-"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
+"Chiamata Fastpath �{0}�: Nessun risultato restituito mentre ci si aspettava "
 "un intero."
 
 #: src/main/java/org/postgresql/fastpath/Fastpath.java:297
 #, java-format
 msgid "The fastpath function {0} is unknown."
-msgstr "La funzione fastpath �{0}� � sconosciuta."
+msgstr "La funzione fastpath �{0}� � sconosciuta."
 
 #: src/main/java/org/postgresql/geometric/PGbox.java:77
 #: src/main/java/org/postgresql/geometric/PGcircle.java:74
@@ -603,7 +603,7 @@ msgstr "Conversione al tipo {0} fallita: {1}."
 #: src/main/java/org/postgresql/geometric/PGpath.java:70
 #, java-format
 msgid "Cannot tell if path is open or closed: {0}."
-msgstr "Impossibile stabilire se il percorso � aperto o chiuso: {0}."
+msgstr "Impossibile stabilire se il percorso � aperto o chiuso: {0}."
 
 #: src/main/java/org/postgresql/gss/GssAction.java:136
 #: src/main/java/org/postgresql/gss/MakeGSS.java:65
@@ -624,7 +624,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:231
 #, java-format
 msgid "PostgreSQL LOBs can only index to: {0}"
-msgstr "Il massimo valore per l''indice dei LOB di PostgreSQL � {0}. "
+msgstr "Il massimo valore per l''indice dei LOB di PostgreSQL � {0}. "
 
 #: src/main/java/org/postgresql/jdbc/AbstractBlobClob.java:227
 msgid "LOB positioning offsets start at 1."
@@ -645,8 +645,8 @@ msgid ""
 "Batch entry {0} {1} was aborted: {2}  Call getNextException to see other "
 "errors in the batch."
 msgstr ""
-"L''operazione �batch� {0} {1} � stata interrotta. Chiamare "
-"�getNextException� per scoprirne il motivo."
+"L''operazione �batch� {0} {1} � stata interrotta. Chiamare "
+"�getNextException� per scoprirne il motivo."
 
 #: src/main/java/org/postgresql/jdbc/BooleanTypeUtil.java:99
 #, java-format
@@ -657,7 +657,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:168
 #, java-format
 msgid "{0} function takes four and only four argument."
-msgstr "Il metodo �{0}� accetta quattro e solo quattro argomenti."
+msgstr "Il metodo �{0}� accetta quattro e solo quattro argomenti."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:268
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:335
@@ -667,7 +667,7 @@ msgstr "Il metodo 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:665
 #, java-format
 msgid "{0} function takes two and only two arguments."
-msgstr "Il metodo �{0}� accetta due e solo due argomenti."
+msgstr "Il metodo �{0}� accetta due e solo due argomenti."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:286
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:439
@@ -681,7 +681,7 @@ msgstr "Il metodo 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:654
 #, java-format
 msgid "{0} function takes one and only one argument."
-msgstr "Il metodo �{0}� accetta un ed un solo argomento."
+msgstr "Il metodo �{0}� accetta un ed un solo argomento."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:310
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:384
@@ -689,7 +689,7 @@ msgstr "Il metodo 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:308
 #, java-format
 msgid "{0} function takes two or three arguments."
-msgstr "Il metodo �{0}� accetta due o tre argomenti."
+msgstr "Il metodo �{0}� accetta due o tre argomenti."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:409
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:424
@@ -698,7 +698,7 @@ msgstr "Il metodo 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:645
 #, java-format
 msgid "{0} function doesn''t take any argument."
-msgstr "Il metodo �{0}� non accetta argomenti."
+msgstr "Il metodo �{0}� non accetta argomenti."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:584
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:636
@@ -706,7 +706,7 @@ msgstr "Il metodo 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:571
 #, java-format
 msgid "{0} function takes three and only three arguments."
-msgstr "Il metodo �{0}� accetta tre e solo tre argomenti."
+msgstr "Il metodo �{0}� accetta tre e solo tre argomenti."
 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:597
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions.java:618
@@ -720,23 +720,23 @@ msgstr "Il metodo 
 #: src/main/java/org/postgresql/jdbc/EscapedFunctions2.java:597
 #, java-format
 msgid "Interval {0} not yet implemented"
-msgstr "L''intervallo �{0}� non � stato ancora implementato."
+msgstr "L''intervallo �{0}� non � stato ancora implementato."
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:38
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:53
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:71
 msgid "Cannot reference a savepoint after it has been released."
 msgstr ""
-"Non � possibile utilizzare un punto di ripristino successivamente al suo "
+"Non � possibile utilizzare un punto di ripristino successivamente al suo "
 "rilascio."
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:43
 msgid "Cannot retrieve the id of a named savepoint."
-msgstr "Non � possibile trovare l''id del punto di ripristino indicato."
+msgstr "Non � possibile trovare l''id del punto di ripristino indicato."
 
 #: src/main/java/org/postgresql/jdbc/PSQLSavepoint.java:58
 msgid "Cannot retrieve the name of an unnamed savepoint."
-msgstr "Non � possibile trovare il nome di un punto di ripristino anonimo."
+msgstr "Non � possibile trovare il nome di un punto di ripristino anonimo."
 
 #: src/main/java/org/postgresql/jdbc/PgArray.java:156
 #: src/main/java/org/postgresql/jdbc/PgArray.java:847
@@ -749,7 +749,7 @@ msgstr "Indice di colonna fuori dall''intervallo ammissibile: {0}"
 #, java-format
 msgid "The array index is out of range: {0}, number of elements: {1}."
 msgstr ""
-"L''indice dell''array � fuori intervallo: {0}, numero di elementi: {1}."
+"L''indice dell''array � fuori intervallo: {0}, numero di elementi: {1}."
 
 #: src/main/java/org/postgresql/jdbc/PgArray.java:209
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1908
@@ -763,7 +763,7 @@ msgid ""
 msgstr ""
 "Sono stati trovati caratteri non validi tra i dati. Molto probabilmente sono "
 "stati memorizzati dei caratteri che non sono validi per la codifica dei "
-"caratteri impostata alla creazione del database. Il caso pi� diffuso � "
+"caratteri impostata alla creazione del database. Il caso pi� diffuso � "
 "quello nel quale si memorizzano caratteri a 8bit in un database con codifica "
 "SQL_ASCII."
 
@@ -771,13 +771,13 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:95
 msgid "A CallableStatement was executed with nothing returned."
 msgstr ""
-"Un �CallableStatement� � stato eseguito senza produrre alcun risultato. "
+"Un �CallableStatement� � stato eseguito senza produrre alcun risultato. "
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:106
 #, fuzzy
 msgid "A CallableStatement was executed with an invalid number of parameters"
 msgstr ""
-"Un �CallableStatement� � stato eseguito con un numero errato di parametri."
+"Un �CallableStatement� � stato eseguito con un numero errato di parametri."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:144
 #, java-format
@@ -785,16 +785,16 @@ msgid ""
 "A CallableStatement function was executed and the out parameter {0} was of "
 "type {1} however type {2} was registered."
 msgstr ""
-"� stato eseguito un �CallableStatement� ma il parametro in uscita �{0}� era "
-"di tipo �{1}� al posto di �{2}�, che era stato dichiarato."
+"� stato eseguito un �CallableStatement� ma il parametro in uscita �{0}� era "
+"di tipo �{1}� al posto di �{2}�, che era stato dichiarato."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:206
 msgid ""
 "This statement does not declare an OUT parameter.  Use '{' ?= call ... '}' "
 "to declare one."
 msgstr ""
-"Questo statement non dichiara il parametro in uscita. Usare �{ ?= "
-"call ... }� per farlo."
+"Questo statement non dichiara il parametro in uscita. Usare �{ ?= "
+"call ... }� per farlo."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:229
 msgid "wasNull cannot be call before fetching a result."
@@ -807,16 +807,16 @@ msgid ""
 "Parameter of type {0} was registered, but call to get{1} (sqltype={2}) was "
 "made."
 msgstr ""
-"� stato definito il parametro di tipo �{0}�, ma poi � stato invocato il "
-"metodo �get{1}()� (sqltype={2})."
+"� stato definito il parametro di tipo �{0}�, ma poi � stato invocato il "
+"metodo �get{1}()� (sqltype={2})."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:407
 msgid ""
 "A CallableStatement was declared, but no call to registerOutParameter(1, "
 "<some type>) was made."
 msgstr ""
-"� stato definito un �CallableStatement� ma non � stato invocato il metodo "
-"�registerOutParameter(1, <tipo>)�."
+"� stato definito un �CallableStatement� ma non � stato invocato il metodo "
+"�registerOutParameter(1, <tipo>)�."
 
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:413
 msgid "No function outputs were registered."
@@ -830,12 +830,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgCallableStatement.java:686
 #, fuzzy, java-format
 msgid "Unsupported type conversion to {1}."
-msgstr "Valore di tipo �{0}� non supportato."
+msgstr "Valore di tipo �{0}� non supportato."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:262
 #, java-format
 msgid "Unsupported value for stringtype parameter: {0}"
-msgstr "Il valore per il parametro di tipo string �{0}� non � supportato."
+msgstr "Il valore per il parametro di tipo string �{0}� non � supportato."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:463
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:104
@@ -849,13 +849,13 @@ msgstr "Il valore per il parametro di tipo string 
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:563
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:568
 msgid "No results were returned by the query."
-msgstr "Nessun risultato � stato restituito dalla query."
+msgstr "Nessun risultato � stato restituito dalla query."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:481
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:499
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:258
 msgid "A result was returned when none was expected."
-msgstr "� stato restituito un valore nonostante non ne fosse atteso nessuno."
+msgstr "� stato restituito un valore nonostante non ne fosse atteso nessuno."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:604
 msgid "Custom type maps are not supported."
@@ -869,13 +869,13 @@ msgstr "Fallita la creazione dell''oggetto per: {0}."
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:700
 #, java-format
 msgid "Unable to load the class {0} responsible for the datatype {1}"
-msgstr "Non � possibile caricare la class �{0}� per gestire il tipo �{1}�."
+msgstr "Non � possibile caricare la class �{0}� per gestire il tipo �{1}�."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:761
 msgid ""
 "Cannot change transaction read-only property in the middle of a transaction."
 msgstr ""
-"Non � possibile modificare la propriet� �read-only� delle transazioni nel "
+"Non � possibile modificare la propriet� �read-only� delle transazioni nel "
 "mezzo di una transazione."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:846
@@ -887,7 +887,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1529
 #, fuzzy
 msgid "This connection has been closed."
-msgstr "Questo �Connection� � stato chiuso."
+msgstr "Questo �Connection� � stato chiuso."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:867
 msgid "Cannot rollback when autoCommit is enabled."
@@ -897,17 +897,17 @@ msgstr ""
 msgid ""
 "Cannot change transaction isolation level in the middle of a transaction."
 msgstr ""
-"Non � possibile cambiare il livello di isolamento delle transazioni nel "
+"Non � possibile cambiare il livello di isolamento delle transazioni nel "
 "mezzo di una transazione."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:926
 #, java-format
 msgid "Transaction isolation level {0} not supported."
-msgstr "Il livello di isolamento delle transazioni �{0}� non � supportato."
+msgstr "Il livello di isolamento delle transazioni �{0}� non � supportato."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:975
 msgid "Finalizing a Connection that was never closed:"
-msgstr "Finalizzazione di una �Connection� che non � stata chiusa."
+msgstr "Finalizzazione di una �Connection� che non � stata chiusa."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1042
 msgid "Unable to translate data into the desired encoding."
@@ -917,7 +917,7 @@ msgstr "Impossibile tradurre i dati nella codifica richiesta."
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1783
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:929
 msgid "Fetch size must be a value greater to or equal to 0."
-msgstr "La dimensione dell''area di �fetch� deve essere maggiore o eguale a 0."
+msgstr "La dimensione dell''area di �fetch� deve essere maggiore o eguale a 0."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1384
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1425
@@ -928,12 +928,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1407
 #, fuzzy, java-format
 msgid "Invalid elements {0}"
-msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
+msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1443
 #, fuzzy, java-format
 msgid "Invalid timeout ({0}<0)."
-msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
+msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1473
 msgid "Validating connection."
@@ -947,7 +947,7 @@ msgstr "Fallita la creazione dell''oggetto per: {0}."
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1516
 #, fuzzy
 msgid "ClientInfo property not supported."
-msgstr "La restituzione di chiavi autogenerate non � supportata."
+msgstr "La restituzione di chiavi autogenerate non � supportata."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1542
 msgid "One or more ClientInfo failed."
@@ -969,33 +969,33 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1682
 #, java-format
 msgid "Unknown ResultSet holdability setting: {0}."
-msgstr "Il parametro �holdability� per il �ResultSet� � sconosciuto: {0}."
+msgstr "Il parametro �holdability� per il �ResultSet� � sconosciuto: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1700
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1721
 msgid "Cannot establish a savepoint in auto-commit mode."
 msgstr ""
-"Non � possibile impostare i punti di ripristino in modalit� �auto-commit�."
+"Non � possibile impostare i punti di ripristino in modalit� �auto-commit�."
 
 #: src/main/java/org/postgresql/jdbc/PgConnection.java:1790
 msgid "Returning autogenerated keys is not supported."
-msgstr "La restituzione di chiavi autogenerate non � supportata."
+msgstr "La restituzione di chiavi autogenerate non � supportata."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:65
 msgid ""
 "Unable to determine a value for MaxIndexKeys due to missing system catalog "
 "data."
 msgstr ""
-"Non � possibile trovare il valore di �MaxIndexKeys� nel catalogo si sistema."
+"Non � possibile trovare il valore di �MaxIndexKeys� nel catalogo si sistema."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:88
 msgid "Unable to find name datatype in the system catalogs."
-msgstr "Non � possibile trovare il datatype �name� nel catalogo di sistema."
+msgstr "Non � possibile trovare il datatype �name� nel catalogo di sistema."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:327
 #, fuzzy
 msgid "Unable to find keywords in the system catalogs."
-msgstr "Non � possibile trovare il datatype �name� nel catalogo di sistema."
+msgstr "Non � possibile trovare il datatype �name� nel catalogo di sistema."
 
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:1110
 #: src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java:2819
@@ -1044,7 +1044,7 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgParameterMetaData.java:88
 #, java-format
 msgid "The parameter index is out of range: {0}, number of parameters: {1}."
-msgstr "Il parametro indice � fuori intervallo: {0}, numero di elementi: {1}."
+msgstr "Il parametro indice � fuori intervallo: {0}, numero di elementi: {1}."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:90
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:113
@@ -1054,7 +1054,7 @@ msgid ""
 "Can''t use query methods that take a query string on a PreparedStatement."
 msgstr ""
 "Non si possono utilizzare i metodi \"query\" che hanno come argomento una "
-"stringa nel caso di �PreparedStatement�."
+"stringa nel caso di �PreparedStatement�."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:254
 msgid "Unknown Types value."
@@ -1066,7 +1066,7 @@ msgstr "Valore di tipo sconosciuto."
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1497
 #, java-format
 msgid "Invalid stream length {0}."
-msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
+msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:414
 #, java-format
@@ -1077,7 +1077,7 @@ msgstr "La JVM sostiene di non supportare la codifica {0}."
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1124
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1156
 msgid "Provided InputStream failed."
-msgstr "L''�InputStream� fornito � fallito."
+msgstr "L''�InputStream� fornito � fallito."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:463
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1095
@@ -1095,17 +1095,17 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:667
 #, java-format
 msgid "Cannot cast an instance of {0} to type {1}"
-msgstr "Non � possibile fare il cast di una istanza di �{0}� al tipo �{1}�."
+msgstr "Non � possibile fare il cast di una istanza di �{0}� al tipo �{1}�."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:685
 #, java-format
 msgid "Unsupported Types value: {0}"
-msgstr "Valore di tipo �{0}� non supportato."
+msgstr "Valore di tipo �{0}� non supportato."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:897
 #, java-format
 msgid "Cannot convert an instance of {0} to type {1}"
-msgstr "Non � possibile convertire una istanza di �{0}� nel tipo �{1}�"
+msgstr "Non � possibile convertire una istanza di �{0}� nel tipo �{1}�"
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:973
 #, java-format
@@ -1113,19 +1113,19 @@ msgid ""
 "Can''t infer the SQL type to use for an instance of {0}. Use setObject() "
 "with an explicit Types value to specify the type to use."
 msgstr ""
-"Non � possibile identificare il tipo SQL da usare per l''istanza di tipo "
-"�{0}�. Usare �setObject()� specificando esplicitamente il tipo da usare per "
+"Non � possibile identificare il tipo SQL da usare per l''istanza di tipo "
+"�{0}�. Usare �setObject()� specificando esplicitamente il tipo da usare per "
 "questo valore."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1132
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1233
 msgid "Unexpected error writing large object to database."
-msgstr "Errore inatteso inviando un �large object� al database."
+msgstr "Errore inatteso inviando un �large object� al database."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1177
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1206
 msgid "Provided Reader failed."
-msgstr "Il �Reader� fornito � fallito."
+msgstr "Il �Reader� fornito � fallito."
 
 #: src/main/java/org/postgresql/jdbc/PgPreparedStatement.java:1456
 #: src/main/java/org/postgresql/util/StreamWrapper.java:56
@@ -1137,8 +1137,8 @@ msgid ""
 "Operation requires a scrollable ResultSet, but this ResultSet is "
 "FORWARD_ONLY."
 msgstr ""
-"L''operazione richiete un �ResultSet� scorribile mentre questo � "
-"�FORWARD_ONLY�."
+"L''operazione richiete un �ResultSet� scorribile mentre questo � "
+"�FORWARD_ONLY�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:479
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:518
@@ -1150,14 +1150,14 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3065
 #, fuzzy, java-format
 msgid "Cannot convert the column of type {0} to requested type {1}."
-msgstr "Non � possibile convertire una istanza di �{0}� nel tipo �{1}�"
+msgstr "Non � possibile convertire una istanza di �{0}� nel tipo �{1}�"
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:851
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:872
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1799
 msgid "Can''t use relative move methods while on the insert row."
 msgstr ""
-"Non � possibile utilizzare gli spostamenti relativi durante l''inserimento "
+"Non � possibile utilizzare gli spostamenti relativi durante l''inserimento "
 "di una riga."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:895
@@ -1169,37 +1169,37 @@ msgstr "Costante per la direzione dell''estrazione non valida: {0}."
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:905
 msgid "Cannot call cancelRowUpdates() when on the insert row."
 msgstr ""
-"Non � possibile invocare �cancelRowUpdates()� durante l''inserimento di una "
+"Non � possibile invocare �cancelRowUpdates()� durante l''inserimento di una "
 "riga."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:920
 msgid "Cannot call deleteRow() when on the insert row."
 msgstr ""
-"Non � possibile invocare �deleteRow()� durante l''inserimento di una riga."
+"Non � possibile invocare �deleteRow()� durante l''inserimento di una riga."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:927
 msgid ""
 "Currently positioned before the start of the ResultSet.  You cannot call "
 "deleteRow() here."
 msgstr ""
-"La posizione attuale � precedente all''inizio del ResultSet. Non � possibile "
-"invocare �deleteRow()� qui."
+"La posizione attuale � precedente all''inizio del ResultSet. Non � possibile "
+"invocare �deleteRow()� qui."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:933
 msgid ""
 "Currently positioned after the end of the ResultSet.  You cannot call "
 "deleteRow() here."
 msgstr ""
-"La posizione attuale � successiva alla fine del ResultSet. Non � possibile "
-"invocare �deleteRow()� qui."
+"La posizione attuale � successiva alla fine del ResultSet. Non � possibile "
+"invocare �deleteRow()� qui."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:937
 msgid "There are no rows in this ResultSet."
-msgstr "Non ci sono righe in questo �ResultSet�."
+msgstr "Non ci sono righe in questo �ResultSet�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:974
 msgid "Not on the insert row."
-msgstr "Non si � in una nuova riga."
+msgstr "Non si � in una nuova riga."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:976
 msgid "You must specify at least one column value to insert a row."
@@ -1216,12 +1216,12 @@ msgstr "La JVM sostiene di non supportare la codifica: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1248
 msgid "Can''t refresh the insert row."
-msgstr "Non � possibile aggiornare la riga in inserimento."
+msgstr "Non � possibile aggiornare la riga in inserimento."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1313
 msgid "Cannot call updateRow() when on the insert row."
 msgstr ""
-"Non � possibile invocare �updateRow()� durante l''inserimento di una riga."
+"Non � possibile invocare �updateRow()� durante l''inserimento di una riga."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1320
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3082
@@ -1229,18 +1229,18 @@ msgid ""
 "Cannot update the ResultSet because it is either before the start or after "
 "the end of the results."
 msgstr ""
-"Non � possibile aggiornare il �ResultSet� perch� la posizione attuale � "
+"Non � possibile aggiornare il �ResultSet� perch� la posizione attuale � "
 "precedente all''inizio o successiva alla file dei risultati."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1496
 msgid "ResultSets with concurrency CONCUR_READ_ONLY cannot be updated."
 msgstr ""
-"I �ResultSet� in modalit� CONCUR_READ_ONLY non possono essere aggiornati."
+"I �ResultSet� in modalit� CONCUR_READ_ONLY non possono essere aggiornati."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1564
 #, java-format
 msgid "No primary key found for table {0}."
-msgstr "Non � stata trovata la chiave primaria della tabella �{0}�."
+msgstr "Non � stata trovata la chiave primaria della tabella �{0}�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:1995
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2000
@@ -1257,12 +1257,12 @@ msgstr "Non 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3070
 #, java-format
 msgid "Bad value for type {0} : {1}"
-msgstr "Il valore �{1}� non � adeguato al tipo �{0}�."
+msgstr "Il valore �{1}� non � adeguato al tipo �{0}�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2592
 #, java-format
 msgid "The column name {0} was not found in this ResultSet."
-msgstr "Colonna denominata �{0}� non � presente in questo �ResultSet�."
+msgstr "Colonna denominata �{0}� non � presente in questo �ResultSet�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2728
 msgid ""
@@ -1270,20 +1270,20 @@ msgid ""
 "select only one table, and must select all primary keys from that table. See "
 "the JDBC 2.1 API Specification, section 5.6 for more details."
 msgstr ""
-"Il �ResultSet� non � aggiornabile. La query che lo genera deve selezionare "
+"Il �ResultSet� non � aggiornabile. La query che lo genera deve selezionare "
 "una sola tabella e deve selezionarne tutti i campi che ne compongono la "
 "chiave primaria. Si vedano le specifiche dell''API JDBC 2.1, sezione 5.6, "
 "per ulteriori dettagli."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2740
 msgid "This ResultSet is closed."
-msgstr "Questo �ResultSet� � chiuso."
+msgstr "Questo �ResultSet� � chiuso."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:2771
 msgid "ResultSet not positioned properly, perhaps you need to call next."
 msgstr ""
-"Il �ResultSet� non � correttamente posizionato; forse � necessario invocare "
-"�next()�."
+"Il �ResultSet� non � correttamente posizionato; forse � necessario invocare "
+"�next()�."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3102
 #, fuzzy
@@ -1315,7 +1315,7 @@ msgstr "Flag non valido"
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3430
 #, fuzzy, java-format
 msgid "conversion to {0} from {1} not supported"
-msgstr "Il livello di isolamento delle transazioni �{0}� non � supportato."
+msgstr "Il livello di isolamento delle transazioni �{0}� non � supportato."
 
 #: src/main/java/org/postgresql/jdbc/PgResultSet.java:3370
 #, fuzzy
@@ -1343,12 +1343,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:222
 #, fuzzy, java-format
 msgid "Unknown XML Result class: {0}"
-msgstr "Il parametro �holdability� per il �ResultSet� � sconosciuto: {0}."
+msgstr "Il parametro �holdability� per il �ResultSet� � sconosciuto: {0}."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:235
 #, fuzzy
 msgid "This SQLXML object has already been freed."
-msgstr "Questo �PooledConnection� � stato chiuso."
+msgstr "Questo �PooledConnection� � stato chiuso."
 
 #: src/main/java/org/postgresql/jdbc/PgSQLXML.java:244
 msgid ""
@@ -1373,7 +1373,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:237
 msgid "Multiple ResultSets were returned by the query."
-msgstr "La query ha restituito �ResultSet� multipli."
+msgstr "La query ha restituito �ResultSet� multipli."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:319
 msgid "Can''t use executeWithFlags(int) on a Statement."
@@ -1393,25 +1393,25 @@ msgstr "La dimensione massima del campo deve essere maggiore o eguale a 0."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:709
 msgid "This statement has been closed."
-msgstr "Questo statement � stato chiuso."
+msgstr "Questo statement � stato chiuso."
 
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1078
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1208
 #: src/main/java/org/postgresql/jdbc/PgStatement.java:1236
 #, fuzzy
 msgid "Returning autogenerated keys by column index is not supported."
-msgstr "La restituzione di chiavi autogenerate non � supportata."
+msgstr "La restituzione di chiavi autogenerate non � supportata."
 
 #: src/main/java/org/postgresql/jdbc/SslMode.java:78
 #, fuzzy, java-format
 msgid "Invalid sslmode value: {0}"
-msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
+msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
 
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:356
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:424
 #, fuzzy, java-format
 msgid "Bad value for type timestamp/date/time: {1}"
-msgstr "Il valore �{1}� non � adeguato al tipo �{0}�."
+msgstr "Il valore �{1}� non � adeguato al tipo �{0}�."
 
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:981
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1038
@@ -1419,7 +1419,7 @@ msgstr "Il valore 
 #: src/main/java/org/postgresql/jdbc/TimestampUtils.java:1131
 #, fuzzy, java-format
 msgid "Unsupported binary encoding of {0}."
-msgstr "Valore di tipo �{0}� non supportato."
+msgstr "Valore di tipo �{0}� non supportato."
 
 #: src/main/java/org/postgresql/jdbc/TypeInfoCache.java:244
 msgid "typname"
@@ -1436,12 +1436,12 @@ msgstr ""
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:120
 #, fuzzy, java-format
 msgid "Invalid server-first-message: {0}"
-msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
+msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
 
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:157
 #, fuzzy, java-format
 msgid "Invalid server-final-message: {0}"
-msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
+msgstr "La dimensione specificata, {0}, per lo �stream� non � valida."
 
 #: src/main/java/org/postgresql/jre7/sasl/ScramAuthenticator.java:163
 #, java-format
@@ -1455,12 +1455,12 @@ msgstr ""
 #: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:249
 #: src/main/java/org/postgresql/largeobject/LargeObjectManager.java:290
 msgid "Large Objects may not be used in auto-commit mode."
-msgstr "Non � possibile impostare i �Large Object� in modalit� �auto-commit�."
+msgstr "Non � possibile impostare i �Large Object� in modalit� �auto-commit�."
 
 #: src/main/java/org/postgresql/osgi/PGDataSourceFactory.java:82
 #, fuzzy, java-format
 msgid "Unsupported properties: {0}"
-msgstr "Valore di tipo �{0}� non supportato."
+msgstr "Valore di tipo �{0}� non supportato."
 
 #: src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java:38
 msgid "Server does not support temporary replication slots"
@@ -1541,7 +1541,7 @@ msgstr ""
 #, fuzzy, java-format
 msgid "The password callback class provided {0} could not be instantiated."
 msgstr ""
-"La classe �SSLSocketFactory� specificata, �{0}�, non pu� essere istanziata."
+"La classe �SSLSocketFactory� specificata, �{0}�, non pu� essere istanziata."
 
 #: src/main/java/org/postgresql/ssl/LibPQFactory.java:140
 #, java-format
@@ -1571,7 +1571,7 @@ msgstr ""
 #, fuzzy, java-format
 msgid "The HostnameVerifier class provided {0} could not be instantiated."
 msgstr ""
-"La classe �SSLSocketFactory� specificata, �{0}�, non pu� essere istanziata."
+"La classe �SSLSocketFactory� specificata, �{0}�, non pu� essere istanziata."
 
 #: src/main/java/org/postgresql/ssl/MakeSSL.java:81
 #, java-format
@@ -1586,7 +1586,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:90
 #, fuzzy, java-format
 msgid "No certificates found for hostname {0}"
-msgstr "Non � stata trovata la chiave primaria della tabella �{0}�."
+msgstr "Non � stata trovata la chiave primaria della tabella �{0}�."
 
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:109
 #, java-format
@@ -1597,7 +1597,7 @@ msgstr ""
 #, fuzzy, java-format
 msgid "Unable to parse certificates for hostname {0}"
 msgstr ""
-"Impossibile fare il �bind� dei valori passati come parametri per lo "
+"Impossibile fare il �bind� dei valori passati come parametri per lo "
 "statement."
 
 #: src/main/java/org/postgresql/ssl/PGjdbcHostnameVerifier.java:157
@@ -1636,7 +1636,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/PKCS12KeyManager.java:44
 #, fuzzy
 msgid "Unable to find pkcs12 keystore."
-msgstr "Non � possibile trovare il datatype �name� nel catalogo di sistema."
+msgstr "Non � possibile trovare il datatype �name� nel catalogo di sistema."
 
 #: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:91
 msgid "The sslfactoryarg property may not be empty."
@@ -1663,7 +1663,7 @@ msgstr ""
 #: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:134
 #, fuzzy
 msgid "An error occurred reading the certificate"
-msgstr "Si � verificato un errore impostando la connessione SSL."
+msgstr "Si � verificato un errore impostando la connessione SSL."
 
 #: src/main/java/org/postgresql/ssl/SingleCertValidatingFactory.java:167
 msgid "No X509TrustManager found"
@@ -1671,7 +1671,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/util/PGInterval.java:210
 msgid "Conversion of interval failed"
-msgstr "Fallita la conversione di un �interval�."
+msgstr "Fallita la conversione di un �interval�."
 
 #: src/main/java/org/postgresql/util/PGPropertyMaxResultBufferParser.java:204
 #, java-format
@@ -1682,7 +1682,7 @@ msgstr ""
 
 #: src/main/java/org/postgresql/util/PGmoney.java:61
 msgid "Conversion of money failed."
-msgstr "Fallita la conversione di un �money�."
+msgstr "Fallita la conversione di un �money�."
 
 #: src/main/java/org/postgresql/util/ServerErrorMessage.java:43
 #, java-format
@@ -1749,16 +1749,16 @@ msgstr "Flag non validi"
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:276
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:491
 msgid "xid must not be null"
-msgstr "xid non pu� essere NULL"
+msgstr "xid non pu� essere NULL"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:194
 msgid "Connection is busy with another transaction"
-msgstr "La connessione � utilizzata da un''altra transazione"
+msgstr "La connessione � utilizzata da un''altra transazione"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:203
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:286
 msgid "suspend/resume not implemented"
-msgstr "�suspend�/�resume� non implementato"
+msgstr "�suspend�/�resume� non implementato"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:211
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:218
@@ -1779,7 +1779,7 @@ msgstr "Errore durante il commit \"one-phase\""
 msgid ""
 "tried to call end without corresponding start call. state={0}, start "
 "xid={1}, currentXid={2}, preparedXid={3}"
-msgstr "� stata chiamata �end� senza la corrispondente chiamata a �start�"
+msgstr "� stata chiamata �end� senza la corrispondente chiamata a �start�"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:326
 #, java-format
@@ -1798,13 +1798,13 @@ msgid ""
 "Not implemented: Prepare must be issued using the same connection that "
 "started the transaction. currentXid={0}, prepare xid={1}"
 msgstr ""
-"Non implementato: �Prepare� deve essere eseguito nella stessa connessione "
+"Non implementato: �Prepare� deve essere eseguito nella stessa connessione "
 "che ha iniziato la transazione."
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:340
 #, fuzzy, java-format
 msgid "Prepare called before end. prepare xid={0}, state={1}"
-msgstr "�Prepare� invocato prima della fine"
+msgstr "�Prepare� invocato prima della fine"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:360
 #, fuzzy, java-format
@@ -1820,7 +1820,7 @@ msgstr "Errore durante il ripristino"
 msgid ""
 "Error rolling back prepared transaction. rollback xid={0}, preparedXid={1}, "
 "currentXid={2}"
-msgstr "Errore durante il �rollback� di una transazione preparata"
+msgstr "Errore durante il �rollback� di una transazione preparata"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:521
 #, java-format
@@ -1844,7 +1844,7 @@ msgstr ""
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:537
 #, fuzzy, java-format
 msgid "commit called before end. commit xid={0}, state={1}"
-msgstr "�Commit� � stato chiamato prima della fine"
+msgstr "�Commit� � stato chiamato prima della fine"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:548
 #, fuzzy, java-format
@@ -1857,7 +1857,7 @@ msgid ""
 "Not implemented: 2nd phase commit must be issued using an idle connection. "
 "commit xid={0}, currentXid={1}, state={2}, transactionState={3}"
 msgstr ""
-"Non implementato: la seconda fase del �commit� deve essere effettuata con "
+"Non implementato: la seconda fase del �commit� deve essere effettuata con "
 "una connessione non in uso"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:609
@@ -1865,12 +1865,12 @@ msgstr ""
 msgid ""
 "Error committing prepared transaction. commit xid={0}, preparedXid={1}, "
 "currentXid={2}"
-msgstr "Errore durante il �rollback� di una transazione preparata"
+msgstr "Errore durante il �rollback� di una transazione preparata"
 
 #: src/main/java/org/postgresql/xa/PGXAConnection.java:626
 #, fuzzy, java-format
 msgid "Heuristic commit/rollback not supported. forget xid={0}"
-msgstr "�Commit� e �rollback� euristici non sono supportati"
+msgstr "�Commit� e �rollback� euristici non sono supportati"
 
 #~ msgid "The driver currently does not support COPY operations."
-#~ msgstr "Il driver non supporta al momento l''operazione �COPY�."
+#~ msgstr "Il driver non supporta al momento l''operazione �COPY�."
diff --git a/src/main/java/org/postgresql/translation/ru_notes.txt b/src/main/java/org/postgresql/translation/ru_notes.txt
deleted file mode 100644
index dc35b0d4fc76a8a0467129705eaa1794df7c8a3e..0000000000000000000000000000000000000000
--- a/src/main/java/org/postgresql/translation/ru_notes.txt
+++ /dev/null
@@ -1,74 +0,0 @@
-msgid "Properties for the driver contains a non-string value for the key "
-msgid "CommandComplete expected COPY but got: "
-msgid "Got CopyInResponse from server during an active {0}"
-
-#: org/postgresql/core/v3/QueryExecutorImpl.java:2318
-msgid "The driver currently does not support COPY operations."
-
-#: org/postgresql/ds/PGPooledConnection.java:314
-msgid ""
-"Connection has been closed automatically because a new connection was opened "
-"for the same PooledConnection or the PooledConnection has been closed."
-
-#: org/postgresql/fastpath/Fastpath.java:169
-#, java-format
-msgid ""
-"Fastpath call {0} - No result was returned or wrong size while expecting an "
-"integer."
-
-# key: postgresql.geo.path
-#: org/postgresql/geometric/PGpath.java:70
-#, java-format
-msgid "Cannot tell if path is open or closed: {0}."
-
-#: org/postgresql/jdbc/AbstractBlobClob.java:230
-msgid "LOB positioning offsets start at 1."
-
-#: org/postgresql/jdbc/BatchResultHandler.java:92
-msgid "Too many update results were returned."
-
-#: org/postgresql/jdbc/EscapedFunctions.java:240
-#: org/postgresql/jdbc/EscapedFunctions2.java:167
-#, java-format
-msgid "{0} function takes four and only four argument."
-
-#: org/postgresql/jdbc/PgCallableStatement.java:106
-msgid "A CallableStatement was executed with an invalid number of parameters"
-
-#: org/postgresql/jdbc/PgCallableStatement.java:386
-#, java-format
-msgid ""
-"Parameter of type {0} was registered, but call to get{1} (sqltype={2}) was "
-"made."
-
-
-#== This statement does not declare an OUT parameter
-msgid ""
-"A CallableStatement was declared, but no call to registerOutParameter(1, "
-"<some type>) was made."
-
-
-#: org/postgresql/jdbc/PgCallableStatement.java:413
-msgid "No function outputs were registered."
-
-#: org/postgresql/jdbc/PgConnection.java:241
-#, java-format
-msgid "Unsupported value for stringtype parameter: {0}"
-
----
-
-
-#: org/postgresql/jdbc/PgConnection.java:962
-msgid "Unable to translate data into the desired encoding."
-
-
-#: org/postgresql/jdbc/PgConnection.java:1329
-#, fuzzy, java-format
-msgid "Invalid elements {0}" => "{0} should be array"
-
-
-#: org/postgresql/jdbc/PgConnection.java:1432
-msgid "ClientInfo property not supported."
-
-which one?
-
diff --git a/src/main/java/org/postgresql/util/Base64.java b/src/main/java/org/postgresql/util/Base64.java
index b993de845233f7592e6e354e113a3b42e68f70ec..48d78f14a492670e5f042d6ad03ad5fcbd48949b 100644
--- a/src/main/java/org/postgresql/util/Base64.java
+++ b/src/main/java/org/postgresql/util/Base64.java
@@ -463,8 +463,8 @@ public class Base64 {
 
       } else {
         // end if: white space, equals sign or better
-        System.err.println("Bad Base64 input character at " + i + ": " + source[i] + "(decimal)");
-        return null;
+        throw new IllegalArgumentException("Bad Base64 input character at "
+            + i + ": " + source[i] + "(decimal)");
       } // end else:
     } // each input character
 
diff --git a/src/main/java/org/postgresql/util/DriverInfo.java b/src/main/java/org/postgresql/util/DriverInfo.java
index ac43c96edf99d534750f266b5e6ee7c6427bed15..0e9266f7fc4008d6e9537ff6e9bc1cf98b2efa7f 100644
--- a/src/main/java/org/postgresql/util/DriverInfo.java
+++ b/src/main/java/org/postgresql/util/DriverInfo.java
@@ -16,13 +16,13 @@ public final class DriverInfo {
   // Driver name
   public static final String DRIVER_NAME = "PostgreSQL JDBC Driver";
   public static final String DRIVER_SHORT_NAME = "PgJDBC";
-  public static final String DRIVER_VERSION = "42.2.14";
+  public static final String DRIVER_VERSION = "42.2.15";
   public static final String DRIVER_FULL_NAME = DRIVER_NAME + " " + DRIVER_VERSION;
 
   // Driver version
   public static final int MAJOR_VERSION = 42;
   public static final int MINOR_VERSION = 2;
-  public static final int PATCH_VERSION = 14;
+  public static final int PATCH_VERSION = 15;
 
   // JDBC specification
   public static final String JDBC_VERSION = "4.2";
diff --git a/src/main/java/org/postgresql/util/ExpressionProperties.java b/src/main/java/org/postgresql/util/ExpressionProperties.java
index 23590c2109b322d6d77e675fe8c7b0d13c5ba721..bf8a7c063581bd2c1c47c536056748ea77478777 100644
--- a/src/main/java/org/postgresql/util/ExpressionProperties.java
+++ b/src/main/java/org/postgresql/util/ExpressionProperties.java
@@ -5,13 +5,19 @@
 
 package org.postgresql.util;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.checker.nullness.qual.PolyNull;
+// import org.checkerframework.checker.regex.qual.Regex;
+
 import java.util.Properties;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class ExpressionProperties extends Properties {
 
-  private static final Pattern EXPRESSION = Pattern.compile("\\$\\{([^}]+)\\}");
+  private static final /* @Regex(1) */ Pattern EXPRESSION = Pattern.compile("\\$\\{([^}]+)\\}");
 
   private final Properties[] defaults;
 
@@ -36,13 +42,13 @@ public class ExpressionProperties extends Properties {
    *         the specified key value.
    */
   @Override
-  public String getProperty(String key) {
+  public /* @Nullable */ String getProperty(String key) {
     String value = getRawPropertyValue(key);
     return replaceProperties(value);
   }
 
   @Override
-  public String getProperty(String key, String defaultValue) {
+  public /* @PolyNull */ String getProperty(String key, /* @PolyNull */ String defaultValue) {
     String value = getRawPropertyValue(key);
     if (value == null) {
       value = defaultValue;
@@ -55,7 +61,7 @@ public class ExpressionProperties extends Properties {
    * @param key property name
    * @return raw property value
    */
-  public String getRawPropertyValue(String key) {
+  public /* @Nullable */ String getRawPropertyValue(String key) {
     String value = super.getProperty(key);
     if (value != null) {
       return value;
@@ -69,7 +75,7 @@ public class ExpressionProperties extends Properties {
     return null;
   }
 
-  private String replaceProperties(String value) {
+  private /* @PolyNull */ String replaceProperties(/* @PolyNull */ String value) {
     if (value == null) {
       return null;
     }
@@ -79,7 +85,7 @@ public class ExpressionProperties extends Properties {
       if (sb == null) {
         sb = new StringBuffer();
       }
-      String propValue = getProperty(matcher.group(1));
+      String propValue = getProperty(castNonNull(matcher.group(1)));
       if (propValue == null) {
         // Use original content like ${propKey} if property is not found
         propValue = matcher.group();
diff --git a/src/main/java/org/postgresql/util/GT.java b/src/main/java/org/postgresql/util/GT.java
index 8d8b50279a996bbc2a7afd08eb022457d289ea95..d6d7288d1397168a834ff950c28f1989e6b1a1b8 100644
--- a/src/main/java/org/postgresql/util/GT.java
+++ b/src/main/java/org/postgresql/util/GT.java
@@ -5,6 +5,9 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.dataflow.qual.Pure;
+
 import java.text.MessageFormat;
 import java.util.Locale;
 import java.util.MissingResourceException;
@@ -21,11 +24,11 @@ public class GT {
   private static final GT _gt = new GT();
   private static final Object[] noargs = new Object[0];
 
-  public static String tr(String message, Object... args) {
+  public static /* @Pure */ String tr(String message, /* @Nullable */ Object... args) {
     return _gt.translate(message, args);
   }
 
-  private ResourceBundle bundle;
+  private /* @Nullable */ ResourceBundle bundle;
 
   private GT() {
     try {
@@ -40,7 +43,7 @@ public class GT {
     }
   }
 
-  private String translate(String message, Object[] args) {
+  private String translate(String message, /* @Nullable */ Object[] args) {
     if (bundle != null && message != null) {
       try {
         message = bundle.getString(message);
diff --git a/src/main/java/org/postgresql/util/Gettable.java b/src/main/java/org/postgresql/util/Gettable.java
index c31b9e8ee25debfb5ebbbf1db9cf2c2ca2db5a73..eef4260d197fa900cd0206ab740ea671a638479e 100644
--- a/src/main/java/org/postgresql/util/Gettable.java
+++ b/src/main/java/org/postgresql/util/Gettable.java
@@ -5,6 +5,9 @@
 
 package org.postgresql.util;
 
-public interface Gettable<K,V> {
-  V get(K key);
+// import org.checkerframework.checker.nullness.qual.NonNull;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
+public interface Gettable<K extends /* @NonNull */ Object, V extends /* @NonNull */ Object> {
+  /* @Nullable */ V get(K key);
 }
diff --git a/src/main/java/org/postgresql/util/GettableHashMap.java b/src/main/java/org/postgresql/util/GettableHashMap.java
index 3ab91b4e7b255ab78e60e8bd8ca08335c6605879..2d252ffdfa6ebee1d896adee2a93e383c8e1e1db 100644
--- a/src/main/java/org/postgresql/util/GettableHashMap.java
+++ b/src/main/java/org/postgresql/util/GettableHashMap.java
@@ -5,8 +5,12 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.NonNull;
+
 import java.util.HashMap;
 
-public class GettableHashMap<K,V> extends HashMap<K,V> implements Gettable<K,V> {
+public class GettableHashMap<K extends /* @NonNull */ Object, V extends /* @NonNull */ Object>
+    extends HashMap<K,V>
+    implements Gettable<K,V> {
 
 }
diff --git a/src/main/java/org/postgresql/util/HStoreConverter.java b/src/main/java/org/postgresql/util/HStoreConverter.java
index d4ce31e3418571cf160c55b9f0a1ab6d81325c9f..2a112bc76e85deba12dfe36caa0b7bc9fd13f52c 100644
--- a/src/main/java/org/postgresql/util/HStoreConverter.java
+++ b/src/main/java/org/postgresql/util/HStoreConverter.java
@@ -7,6 +7,8 @@ package org.postgresql.util;
 
 import org.postgresql.core.Encoding;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
 import java.sql.SQLException;
@@ -15,8 +17,9 @@ import java.util.Map;
 import java.util.Map.Entry;
 
 public class HStoreConverter {
-  public static Map<String, String> fromBytes(byte[] b, Encoding encoding) throws SQLException {
-    Map<String, String> m = new HashMap<String, String>();
+  public static Map<String, /* @Nullable */ String> fromBytes(byte[] b, Encoding encoding)
+      throws SQLException {
+    Map<String, /* @Nullable */ String> m = new HashMap<String, /* @Nullable */ String>();
     int pos = 0;
     int numElements = ByteConverter.int4(b, pos);
     pos += 4;
@@ -53,7 +56,12 @@ public class HStoreConverter {
       ByteConverter.int4(lenBuf, 0, m.size());
       baos.write(lenBuf);
       for (Entry<?, ?> e : m.entrySet()) {
-        byte[] key = encoding.encode(e.getKey().toString());
+        Object mapKey = e.getKey();
+        if (mapKey == null) {
+          throw new PSQLException(GT.tr("hstore key must not be null"),
+              PSQLState.INVALID_PARAMETER_VALUE);
+        }
+        byte[] key = encoding.encode(mapKey.toString());
         ByteConverter.int4(lenBuf, 0, key.length);
         baos.write(lenBuf);
         baos.write(key);
@@ -92,7 +100,7 @@ public class HStoreConverter {
     return sb.toString();
   }
 
-  private static void appendEscaped(StringBuilder sb, Object val) {
+  private static void appendEscaped(StringBuilder sb, /* @Nullable */ Object val) {
     if (val != null) {
       sb.append('"');
       String s = val.toString();
@@ -109,8 +117,8 @@ public class HStoreConverter {
     }
   }
 
-  public static Map<String, String> fromString(String s) {
-    Map<String, String> m = new HashMap<String, String>();
+  public static Map<String, /* @Nullable */ String> fromString(String s) {
+    Map<String, /* @Nullable */ String> m = new HashMap<String, /* @Nullable */ String>();
     int pos = 0;
     StringBuilder sb = new StringBuilder();
     while (pos < s.length()) {
diff --git a/src/main/java/org/postgresql/util/HostSpec.java b/src/main/java/org/postgresql/util/HostSpec.java
index 32dd9b78dfaac4d3c0d473fac7aa2a878cb97130..06bc6e92fc23d926e13fecef5a72f32751c837dd 100644
--- a/src/main/java/org/postgresql/util/HostSpec.java
+++ b/src/main/java/org/postgresql/util/HostSpec.java
@@ -7,6 +7,8 @@ package org.postgresql.util;
 
 import static java.util.regex.Pattern.compile;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
@@ -37,7 +39,7 @@ public class HostSpec {
   }
 
   @Override
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     return obj instanceof HostSpec && port == ((HostSpec) obj).port
         && host.equals(((HostSpec) obj).host);
   }
@@ -66,7 +68,8 @@ public class HostSpec {
     return matcher != null && matcher.matches();
   }
 
-  private Pattern toPattern(String mask) {
+  @SuppressWarnings("regex")
+  private /* @Nullable */ Pattern toPattern(String mask) {
     StringBuilder joiner = new StringBuilder();
     String separator = "";
     for (String disjunct : mask.split("\\|")) {
diff --git a/src/main/java/org/postgresql/util/JdbcBlackHole.java b/src/main/java/org/postgresql/util/JdbcBlackHole.java
index 8779955410939ac47bae0409bf845c845e91bba0..199d035b3e9bc7b63da40ab3667a282b663988fa 100644
--- a/src/main/java/org/postgresql/util/JdbcBlackHole.java
+++ b/src/main/java/org/postgresql/util/JdbcBlackHole.java
@@ -5,13 +5,15 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 
 public class JdbcBlackHole {
-  public static void close(Connection con) {
+  public static void close(/* @Nullable */ Connection con) {
     try {
       if (con != null) {
         con.close();
@@ -21,7 +23,7 @@ public class JdbcBlackHole {
     }
   }
 
-  public static void close(Statement s) {
+  public static void close(/* @Nullable */ Statement s) {
     try {
       if (s != null) {
         s.close();
@@ -31,7 +33,7 @@ public class JdbcBlackHole {
     }
   }
 
-  public static void close(ResultSet rs) {
+  public static void close(/* @Nullable */ ResultSet rs) {
     try {
       if (rs != null) {
         rs.close();
diff --git a/src/main/java/org/postgresql/util/LogWriterHandler.java b/src/main/java/org/postgresql/util/LogWriterHandler.java
new file mode 100644
index 0000000000000000000000000000000000000000..181fc5e78712575e51fe013400d52a130414481f
--- /dev/null
+++ b/src/main/java/org/postgresql/util/LogWriterHandler.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2004, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.util;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
+import java.io.Writer;
+import java.util.logging.ErrorManager;
+import java.util.logging.Formatter;
+import java.util.logging.Handler;
+import java.util.logging.Level;
+import java.util.logging.LogRecord;
+import java.util.logging.SimpleFormatter;
+
+public class LogWriterHandler extends Handler {
+
+  private /* @Nullable */ Writer writer;
+  private final Object lock = new Object();
+
+  @SuppressWarnings("method.invocation.invalid")
+  public LogWriterHandler(Writer inWriter) {
+    super();
+    setLevel(Level.INFO);
+    setFilter(null);
+    setFormatter(new SimpleFormatter());
+    setWriter(inWriter);
+  }
+
+  @Override
+  public void publish(LogRecord record) {
+    final String formatted;
+    final Formatter formatter = getFormatter();
+
+    try {
+      formatted = formatter.format(record);
+    } catch (Exception ex) {
+      reportError("Error Formatting record", ex, ErrorManager.FORMAT_FAILURE);
+      return;
+    }
+
+    if (formatted.length() == 0) {
+      return;
+    }
+    try {
+      synchronized (lock) {
+        Writer writer = this.writer;
+        if (writer != null) {
+          writer.write(formatted);
+        }
+      }
+    } catch (Exception ex) {
+      reportError("Error writing message", ex, ErrorManager.WRITE_FAILURE);
+    }
+  }
+
+  @Override
+  public void flush() {
+    try {
+      Writer writer = this.writer;
+      if (writer != null) {
+        writer.flush();
+      }
+    } catch ( Exception ex ) {
+      reportError("Error on flush", ex, ErrorManager.WRITE_FAILURE);
+    }
+  }
+
+  @Override
+  public void close() throws SecurityException {
+    try {
+      Writer writer = this.writer;
+      if (writer != null) {
+        writer.close();
+      }
+    } catch ( Exception ex ) {
+      reportError("Error closing writer", ex, ErrorManager.WRITE_FAILURE);
+    }
+  }
+
+  private void setWriter(Writer writer) throws IllegalArgumentException {
+    if (writer == null) {
+      throw new IllegalArgumentException("Writer cannot be null");
+    }
+    this.writer = writer;
+
+    try {
+      writer.write(getFormatter().getHead(this));
+    } catch ( Exception ex) {
+      reportError("Error writing head section", ex, ErrorManager.WRITE_FAILURE);
+    }
+  }
+}
diff --git a/src/main/java/org/postgresql/util/LruCache.java b/src/main/java/org/postgresql/util/LruCache.java
index cabd4d6134f02a135cc029103e47563d7eaaa803..4e7204d8f1cba1f53e6e248d7beed00f8eff5b34 100644
--- a/src/main/java/org/postgresql/util/LruCache.java
+++ b/src/main/java/org/postgresql/util/LruCache.java
@@ -5,6 +5,9 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.NonNull;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLException;
 import java.util.Iterator;
 import java.util.LinkedHashMap;
@@ -13,7 +16,8 @@ import java.util.Map;
 /**
  * Caches values in simple least-recently-accessed order.
  */
-public class LruCache<Key, Value extends CanEstimateSize> implements Gettable<Key, Value> {
+public class LruCache<Key extends /* @NonNull */ Object, Value extends /* @NonNull */ CanEstimateSize>
+    implements Gettable<Key, Value> {
   /**
    * Action that is invoked when the entry is removed from the cache.
    *
@@ -32,8 +36,8 @@ public class LruCache<Key, Value extends CanEstimateSize> implements Gettable<Ke
     Value create(Key key) throws SQLException;
   }
 
-  private final EvictAction<Value> onEvict;
-  private final CreateAction<Key, Value> createAction;
+  private final /* @Nullable */ EvictAction<Value> onEvict;
+  private final /* @Nullable */ CreateAction<Key, Value> createAction;
   private final int maxSizeEntries;
   private final long maxSizeBytes;
   private long currentSize;
@@ -72,19 +76,21 @@ public class LruCache<Key, Value extends CanEstimateSize> implements Gettable<Ke
 
   private void evictValue(Value value) {
     try {
-      onEvict.evict(value);
+      if (onEvict != null) {
+        onEvict.evict(value);
+      }
     } catch (SQLException e) {
       /* ignore */
     }
   }
 
   public LruCache(int maxSizeEntries, long maxSizeBytes, boolean accessOrder) {
-    this(maxSizeEntries, maxSizeBytes, accessOrder, NOOP_CREATE_ACTION, NOOP_EVICT_ACTION);
+    this(maxSizeEntries, maxSizeBytes, accessOrder, null, null);
   }
 
   public LruCache(int maxSizeEntries, long maxSizeBytes, boolean accessOrder,
-      CreateAction<Key, Value> createAction,
-      EvictAction<Value> onEvict) {
+      /* @Nullable */ CreateAction<Key, Value> createAction,
+      /* @Nullable */ EvictAction<Value> onEvict) {
     this.maxSizeEntries = maxSizeEntries;
     this.maxSizeBytes = maxSizeBytes;
     this.createAction = createAction;
@@ -98,7 +104,7 @@ public class LruCache<Key, Value extends CanEstimateSize> implements Gettable<Ke
    * @param key cache key
    * @return entry from cache or null if cache does not contain given key.
    */
-  public synchronized Value get(Key key) {
+  public synchronized /* @Nullable */ Value get(Key key) {
     return cache.get(key);
   }
 
@@ -112,6 +118,9 @@ public class LruCache<Key, Value extends CanEstimateSize> implements Gettable<Ke
   public synchronized Value borrow(Key key) throws SQLException {
     Value value = cache.remove(key);
     if (value == null) {
+      if (createAction == null) {
+        throw new UnsupportedOperationException("createAction == null, so can't create object");
+      }
       return createAction.create(key);
     }
     currentSize -= value.getSize();
@@ -133,7 +142,7 @@ public class LruCache<Key, Value extends CanEstimateSize> implements Gettable<Ke
       return;
     }
     currentSize += valueSize;
-    Value prev = cache.put(key, value);
+    /* @Nullable */ Value prev = cache.put(key, value);
     if (prev == null) {
       return;
     }
@@ -154,18 +163,4 @@ public class LruCache<Key, Value extends CanEstimateSize> implements Gettable<Ke
       this.put(entry.getKey(), entry.getValue());
     }
   }
-
-  public static final CreateAction NOOP_CREATE_ACTION = new CreateAction() {
-    @Override
-    public Object create(Object o) throws SQLException {
-      return null;
-    }
-  };
-
-  public static final EvictAction NOOP_EVICT_ACTION = new EvictAction() {
-    @Override
-    public void evict(Object o) throws SQLException {
-      return;
-    }
-  };
 }
diff --git a/src/main/java/org/postgresql/util/ObjectFactory.java b/src/main/java/org/postgresql/util/ObjectFactory.java
index db22df3f448a5cf8e36cd91be7425492e6bb8006..055670f7cd772c9f14ebfb4232c5d2d81b123f10 100644
--- a/src/main/java/org/postgresql/util/ObjectFactory.java
+++ b/src/main/java/org/postgresql/util/ObjectFactory.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.reflect.Constructor;
 import java.lang.reflect.InvocationTargetException;
 import java.util.Properties;
@@ -35,29 +37,28 @@ public class ObjectFactory {
    * @throws InvocationTargetException if something goes wrong
    */
   public static Object instantiate(String classname, Properties info, boolean tryString,
-      String stringarg) throws ClassNotFoundException, SecurityException, NoSuchMethodException,
+      /* @Nullable */ String stringarg)
+      throws ClassNotFoundException, SecurityException, NoSuchMethodException,
           IllegalArgumentException, InstantiationException, IllegalAccessException,
           InvocationTargetException {
-    Object[] args = {info};
+    /* @Nullable */ Object[] args = {info};
     Constructor<?> ctor = null;
     Class<?> cls = Class.forName(classname);
     try {
       ctor = cls.getConstructor(Properties.class);
-    } catch (NoSuchMethodException nsme) {
-      if (tryString) {
-        try {
-          ctor = cls.getConstructor(String.class);
-          args = new String[]{stringarg};
-        } catch (NoSuchMethodException nsme2) {
-          tryString = false;
-        }
-      }
-      if (!tryString) {
-        ctor = cls.getConstructor((Class[]) null);
-        args = null;
+    } catch (NoSuchMethodException ignored) {
+    }
+    if (tryString && ctor == null) {
+      try {
+        ctor = cls.getConstructor(String.class);
+        args = new String[]{stringarg};
+      } catch (NoSuchMethodException ignored) {
       }
     }
+    if (ctor == null) {
+      ctor = cls.getConstructor();
+      args = new Object[0];
+    }
     return ctor.newInstance(args);
   }
-
 }
diff --git a/src/main/java/org/postgresql/util/PGInterval.java b/src/main/java/org/postgresql/util/PGInterval.java
index 7df831f3727aa338538c72eb7000a7426a42ca0e..e61b3c810400c981cf5c4bc3fbf10730863d3fb0 100644
--- a/src/main/java/org/postgresql/util/PGInterval.java
+++ b/src/main/java/org/postgresql/util/PGInterval.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 import java.text.DecimalFormat;
@@ -33,7 +35,7 @@ public class PGInterval extends PGobject implements Serializable, Cloneable {
    * required by the driver.
    */
   public PGInterval() {
-    setType("interval");
+    type = "interval";
   }
 
   /**
@@ -41,8 +43,9 @@ public class PGInterval extends PGobject implements Serializable, Cloneable {
    *
    * @param value String representated interval (e.g. '3 years 2 mons')
    * @throws SQLException Is thrown if the string representation has an unknown format
-   * @see #setValue(String)
+   * @see PGobject#setValue(String)
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGInterval(String value) throws SQLException {
     this();
     setValue(value);
@@ -119,6 +122,7 @@ public class PGInterval extends PGobject implements Serializable, Cloneable {
    * @param seconds seconds
    * @see #setValue(int, int, int, int, int, double)
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGInterval(int years, int months, int days, int hours, int minutes, double seconds) {
     this();
     setValue(years, months, days, hours, minutes, seconds);
@@ -444,7 +448,7 @@ public class PGInterval extends PGobject implements Serializable, Cloneable {
    * @return integer parsed from string value
    * @throws NumberFormatException if the string contains invalid chars
    */
-  private static int nullSafeIntGet(String value) throws NumberFormatException {
+  private static int nullSafeIntGet(/* @Nullable */ String value) throws NumberFormatException {
     return (value == null) ? 0 : Integer.parseInt(value);
   }
 
@@ -455,7 +459,7 @@ public class PGInterval extends PGobject implements Serializable, Cloneable {
    * @return double parsed from string value
    * @throws NumberFormatException if the string contains invalid chars
    */
-  private static double nullSafeDoubleGet(String value) throws NumberFormatException {
+  private static double nullSafeDoubleGet(/* @Nullable */ String value) throws NumberFormatException {
     return (value == null) ? 0 : Double.parseDouble(value);
   }
 
@@ -465,7 +469,7 @@ public class PGInterval extends PGobject implements Serializable, Cloneable {
    * @param obj Object to compare with
    * @return true if the two intervals are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj == null) {
       return false;
     }
diff --git a/src/main/java/org/postgresql/util/PGPropertyMaxResultBufferParser.java b/src/main/java/org/postgresql/util/PGPropertyMaxResultBufferParser.java
index 353095ce2e129c06b1678bacbec771fd33677e33..8d0be80f3e957a2653ffbfc54e0dadf3e862ae63 100644
--- a/src/main/java/org/postgresql/util/PGPropertyMaxResultBufferParser.java
+++ b/src/main/java/org/postgresql/util/PGPropertyMaxResultBufferParser.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.management.ManagementFactory;
 import java.util.logging.Level;
 import java.util.logging.Logger;
@@ -27,11 +29,14 @@ public class PGPropertyMaxResultBufferParser {
    * @return value of max result buffer size.
    * @throws PSQLException Exception when given value can't be parsed.
    */
-  public static long parseProperty(String value) throws PSQLException {
+  public static long parseProperty(/* @Nullable */ String value) throws PSQLException {
     long result = -1;
-    if (checkIfValueContainsPercent(value)) {
+    //noinspection StatementWithEmptyBody
+    if (value == null) {
+      // default branch
+    } else if (checkIfValueContainsPercent(value)) {
       result = parseBytePercentValue(value);
-    } else if (checkIfValueExistsToBeParsed(value)) {
+    } else if (!value.isEmpty()) {
       result = parseByteValue(value);
     }
     result = adjustResultSize(result);
@@ -45,7 +50,7 @@ public class PGPropertyMaxResultBufferParser {
    * @return Result if value contains percent.
    */
   private static boolean checkIfValueContainsPercent(String value) {
-    return (value != null) && (getPercentPhraseLengthIfContains(value) != -1);
+    return getPercentPhraseLengthIfContains(value) != -1;
   }
 
   /**
@@ -60,7 +65,7 @@ public class PGPropertyMaxResultBufferParser {
     long result = -1;
     int length;
 
-    if (checkIfValueExistsToBeParsed(value)) {
+    if (!value.isEmpty()) {
       length = getPercentPhraseLengthIfContains(value);
 
       if (length == -1) {
@@ -127,16 +132,6 @@ public class PGPropertyMaxResultBufferParser {
     return result;
   }
 
-  /**
-   * Method to check if given value has any chars to be parsed.
-   *
-   * @param value Value to be checked.
-   * @return Result if value can be parsed.
-   */
-  private static boolean checkIfValueExistsToBeParsed(String value) {
-    return value != null && value.length() != 0;
-  }
-
   /**
    * Method to get size based on given string value. String can contains just a number or number +
    * multiplier sign (like T, G, M or K).
diff --git a/src/main/java/org/postgresql/util/PGTime.java b/src/main/java/org/postgresql/util/PGTime.java
index f98bb134c3441ced773d2d81afd467a6d8514251..451de125ef2e3671edb2752484ebd2b5b8b4bcae 100644
--- a/src/main/java/org/postgresql/util/PGTime.java
+++ b/src/main/java/org/postgresql/util/PGTime.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.PreparedStatement;
 import java.sql.Time;
 import java.util.Calendar;
@@ -21,7 +23,7 @@ public class PGTime extends Time {
   /**
    * The optional calendar for this time.
    */
-  private Calendar calendar;
+  private /* @Nullable */ Calendar calendar;
 
   /**
    * Constructs a <code>PGTime</code> without a time zone.
@@ -46,9 +48,9 @@ public class PGTime extends Time {
    * @param calendar the calendar object containing the time zone or <code>null</code>.
    * @see Time#Time(long)
    */
-  public PGTime(long time, Calendar calendar) {
+  public PGTime(long time, /* @Nullable */ Calendar calendar) {
     super(time);
-    this.setCalendar(calendar);
+    this.calendar = calendar;
   }
 
   /**
@@ -56,7 +58,7 @@ public class PGTime extends Time {
    *
    * @param calendar the calendar object or <code>null</code>.
    */
-  public void setCalendar(Calendar calendar) {
+  public void setCalendar(/* @Nullable */ Calendar calendar) {
     this.calendar = calendar;
   }
 
@@ -65,7 +67,7 @@ public class PGTime extends Time {
    *
    * @return the calendar or <code>null</code>.
    */
-  public Calendar getCalendar() {
+  public /* @Nullable */ Calendar getCalendar() {
     return calendar;
   }
 
@@ -78,32 +80,28 @@ public class PGTime extends Time {
   }
 
   @Override
-  public boolean equals(Object obj) {
-    if (this == obj) {
+  public boolean equals(/* @Nullable */ Object o) {
+    if (this == o) {
       return true;
     }
-    if (!super.equals(obj)) {
-      return false;
-    }
-    if (!(obj instanceof PGTime)) {
+    if (o == null || getClass() != o.getClass()) {
       return false;
     }
-    PGTime other = (PGTime) obj;
-    if (calendar == null) {
-      if (other.calendar != null) {
-        return false;
-      }
-    } else if (!calendar.equals(other.calendar)) {
+    if (!super.equals(o)) {
       return false;
     }
-    return true;
+
+    PGTime pgTime = (PGTime) o;
+
+    return calendar != null ? calendar.equals(pgTime.calendar) : pgTime.calendar == null;
   }
 
   @Override
   public Object clone() {
     PGTime clone = (PGTime) super.clone();
-    if (getCalendar() != null) {
-      clone.setCalendar((Calendar) getCalendar().clone());
+    Calendar calendar = getCalendar();
+    if (calendar != null) {
+      clone.setCalendar((Calendar) calendar.clone());
     }
     return clone;
   }
diff --git a/src/main/java/org/postgresql/util/PGTimestamp.java b/src/main/java/org/postgresql/util/PGTimestamp.java
index 25ed910d5465cc99f72934528fa3e392b9a04285..dd1890db7b2b6a9849298d3dac43bd12145dddb8 100644
--- a/src/main/java/org/postgresql/util/PGTimestamp.java
+++ b/src/main/java/org/postgresql/util/PGTimestamp.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.Timestamp;
 import java.util.Calendar;
 
@@ -20,7 +22,7 @@ public class PGTimestamp extends Timestamp {
   /**
    * The optional calendar for this timestamp.
    */
-  private Calendar calendar;
+  private /* @Nullable */ Calendar calendar;
 
   /**
    * Constructs a <code>PGTimestamp</code> without a time zone. The integral seconds are stored in
@@ -51,9 +53,9 @@ public class PGTimestamp extends Timestamp {
    * @param calendar the calendar object containing the time zone or <code>null</code>.
    * @see Timestamp#Timestamp(long)
    */
-  public PGTimestamp(long time, Calendar calendar) {
+  public PGTimestamp(long time, /* @Nullable */ Calendar calendar) {
     super(time);
-    this.setCalendar(calendar);
+    this.calendar = calendar;
   }
 
   /**
@@ -61,7 +63,7 @@ public class PGTimestamp extends Timestamp {
    *
    * @param calendar the calendar object or <code>null</code>.
    */
-  public void setCalendar(Calendar calendar) {
+  public void setCalendar(/* @Nullable */ Calendar calendar) {
     this.calendar = calendar;
   }
 
@@ -70,7 +72,7 @@ public class PGTimestamp extends Timestamp {
    *
    * @return the calendar object or <code>null</code>.
    */
-  public Calendar getCalendar() {
+  public /* @Nullable */ Calendar getCalendar() {
     return calendar;
   }
 
@@ -83,32 +85,28 @@ public class PGTimestamp extends Timestamp {
   }
 
   @Override
-  public boolean equals(Object obj) {
-    if (this == obj) {
+  public boolean equals(/* @Nullable */ Object o) {
+    if (this == o) {
       return true;
     }
-    if (!super.equals(obj)) {
-      return false;
-    }
-    if (!(obj instanceof PGTimestamp)) {
+    if (o == null || getClass() != o.getClass()) {
       return false;
     }
-    PGTimestamp other = (PGTimestamp) obj;
-    if (calendar == null) {
-      if (other.calendar != null) {
-        return false;
-      }
-    } else if (!calendar.equals(other.calendar)) {
+    if (!super.equals(o)) {
       return false;
     }
-    return true;
+
+    PGTimestamp that = (PGTimestamp) o;
+
+    return calendar != null ? calendar.equals(that.calendar) : that.calendar == null;
   }
 
   @Override
   public Object clone() {
     PGTimestamp clone = (PGTimestamp) super.clone();
-    if (getCalendar() != null) {
-      clone.setCalendar((Calendar) getCalendar().clone());
+    Calendar calendar = getCalendar();
+    if (calendar != null) {
+      clone.setCalendar((Calendar) calendar.clone());
     }
     return clone;
   }
diff --git a/src/main/java/org/postgresql/util/PGbytea.java b/src/main/java/org/postgresql/util/PGbytea.java
index 0d4efb9f1a959621c0678655478e4cea2af9ed78..5931a39579f118abef6a1303dfcb2087bec702b1 100644
--- a/src/main/java/org/postgresql/util/PGbytea.java
+++ b/src/main/java/org/postgresql/util/PGbytea.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.PolyNull;
+
 import java.sql.SQLException;
 
 /**
@@ -17,7 +19,7 @@ public class PGbytea {
    * Converts a PG bytea raw value (i.e. the raw binary representation of the bytea data type) into
    * a java byte[]
    */
-  public static byte[] toBytes(byte[] s) throws SQLException {
+  public static byte /* @PolyNull */ [] toBytes(byte /* @PolyNull */[] s) throws SQLException {
     if (s == null) {
       return null;
     }
@@ -116,7 +118,7 @@ public class PGbytea {
    * Converts a java byte[] into a PG bytea string (i.e. the text representation of the bytea data
    * type)
    */
-  public static String toPGString(byte[] buf) {
+  public static /* @PolyNull */ String toPGString(byte /* @PolyNull */[] buf) {
     if (buf == null) {
       return null;
     }
diff --git a/src/main/java/org/postgresql/util/PGmoney.java b/src/main/java/org/postgresql/util/PGmoney.java
index 03310516bd72ea89665217141d282f9eee6afee5..b0b7b543af21d85866ed253e817ba00d27efe41e 100644
--- a/src/main/java/org/postgresql/util/PGmoney.java
+++ b/src/main/java/org/postgresql/util/PGmoney.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 
@@ -25,6 +27,7 @@ public class PGmoney extends PGobject implements Serializable, Cloneable {
     val = value;
   }
 
+  @SuppressWarnings("method.invocation.invalid")
   public PGmoney(String value) throws SQLException {
     this();
     setValue(value);
@@ -34,7 +37,7 @@ public class PGmoney extends PGobject implements Serializable, Cloneable {
    * Required by the driver
    */
   public PGmoney() {
-    setType("money");
+    type = "money";
   }
 
   public void setValue(String s) throws SQLException {
@@ -73,7 +76,7 @@ public class PGmoney extends PGobject implements Serializable, Cloneable {
     return result;
   }
 
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj instanceof PGmoney) {
       PGmoney p = (PGmoney) obj;
       return val == p.val;
diff --git a/src/main/java/org/postgresql/util/PGobject.java b/src/main/java/org/postgresql/util/PGobject.java
index 0d417ae02d744b8c4de43c56f53346f77800c557..147731d417501fb8ce88eb8ad9fe95fef7bd821e 100644
--- a/src/main/java/org/postgresql/util/PGobject.java
+++ b/src/main/java/org/postgresql/util/PGobject.java
@@ -5,6 +5,10 @@
 
 package org.postgresql.util;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.sql.SQLException;
 
@@ -13,8 +17,8 @@ import java.sql.SQLException;
  * JDBC Standards.
  */
 public class PGobject implements Serializable, Cloneable {
-  protected String type;
-  protected String value;
+  protected /* @Nullable */ String type;
+  protected /* @Nullable */ String value;
 
   /**
    * This is called by org.postgresql.Connection.getObject() to create the object.
@@ -49,7 +53,7 @@ public class PGobject implements Serializable, Cloneable {
    * @return the type name of this object
    */
   public final String getType() {
-    return type;
+    return castNonNull(type);
   }
 
   /**
@@ -58,7 +62,7 @@ public class PGobject implements Serializable, Cloneable {
    *
    * @return the value of this object
    */
-  public String getValue() {
+  public /* @Nullable */ String getValue() {
     return value;
   }
 
@@ -68,7 +72,7 @@ public class PGobject implements Serializable, Cloneable {
    * @param obj Object to compare with
    * @return true if the two boxes are identical
    */
-  public boolean equals(Object obj) {
+  public boolean equals(/* @Nullable */ Object obj) {
     if (obj instanceof PGobject) {
       final Object otherValue = ((PGobject) obj).getValue();
 
@@ -92,6 +96,7 @@ public class PGobject implements Serializable, Cloneable {
    *
    * @return the value of this object, in the syntax expected by org.postgresql
    */
+  @SuppressWarnings("nullness")
   public String toString() {
     return getValue();
   }
@@ -103,6 +108,11 @@ public class PGobject implements Serializable, Cloneable {
    */
   @Override
   public int hashCode() {
-    return getValue() != null ? getValue().hashCode() : 0;
+    String value = getValue();
+    return value != null ? value.hashCode() : 0;
+  }
+
+  protected static boolean equals(/* @Nullable */ Object a, /* @Nullable */ Object b) {
+    return a == b || a != null && a.equals(b);
   }
 }
diff --git a/src/main/java/org/postgresql/util/PGtokenizer.java b/src/main/java/org/postgresql/util/PGtokenizer.java
index d2c926708ca60d0c6c6adf629f81605095fb70b1..530bc206da0a85bfe2888cc011198b26a5cbd51b 100644
--- a/src/main/java/org/postgresql/util/PGtokenizer.java
+++ b/src/main/java/org/postgresql/util/PGtokenizer.java
@@ -21,7 +21,7 @@ import java.util.List;
  */
 public class PGtokenizer {
   // Our tokens
-  protected List<String> tokens;
+  protected List<String> tokens = new ArrayList<String>();
 
   /**
    * <p>Create a tokeniser.</p>
@@ -32,6 +32,7 @@ public class PGtokenizer {
    * @param string containing tokens
    * @param delim single character to split the tokens
    */
+  @SuppressWarnings("method.invocation.invalid")
   public PGtokenizer(String string, char delim) {
     tokenize(string, delim);
   }
@@ -44,7 +45,7 @@ public class PGtokenizer {
    * @return number of tokens
    */
   public int tokenize(String string, char delim) {
-    tokens = new ArrayList<String>();
+    tokens.clear();
 
     // nest holds how many levels we are in the current token.
     // if this is > 0 then we don't split a token when delim is matched.
diff --git a/src/main/java/org/postgresql/util/PSQLException.java b/src/main/java/org/postgresql/util/PSQLException.java
index 0e3cd476770ff9630ed6a39f0e2dd4daebcddf08..44bb34331cb8e3cd98ab6301178c18929bc2c1c0 100644
--- a/src/main/java/org/postgresql/util/PSQLException.java
+++ b/src/main/java/org/postgresql/util/PSQLException.java
@@ -5,30 +5,37 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.dataflow.qual.Pure;
+
 import java.sql.SQLException;
 
 public class PSQLException extends SQLException {
 
-  private ServerErrorMessage serverError;
+  private /* @Nullable */ ServerErrorMessage serverError;
 
-  public PSQLException(String msg, PSQLState state, Throwable cause) {
+  /* @Pure */
+  public PSQLException(/* @Nullable */ String msg, /* @Nullable */ PSQLState state, /* @Nullable */ Throwable cause) {
     super(msg, state == null ? null : state.getState(), cause);
   }
 
-  public PSQLException(String msg, PSQLState state) {
+  /* @Pure */
+  public PSQLException(/* @Nullable */ String msg, /* @Nullable */ PSQLState state) {
     super(msg, state == null ? null : state.getState());
   }
 
+  /* @Pure */
   public PSQLException(ServerErrorMessage serverError) {
     this(serverError, true);
   }
 
+  /* @Pure */
   public PSQLException(ServerErrorMessage serverError, boolean detail) {
     super(detail ? serverError.toString() : serverError.getNonSensitiveErrorMessage(), serverError.getSQLState());
     this.serverError = serverError;
   }
 
-  public ServerErrorMessage getServerErrorMessage() {
+  public /* @Pure */ /* @Nullable */ ServerErrorMessage getServerErrorMessage() {
     return serverError;
   }
 }
diff --git a/src/main/java/org/postgresql/util/PSQLState.java b/src/main/java/org/postgresql/util/PSQLState.java
index 7a263d135a8f46493f759d8817ac181a768c975f..f16b3c5b6cd979f60057a1892a737af84b0f1d8c 100644
--- a/src/main/java/org/postgresql/util/PSQLState.java
+++ b/src/main/java/org/postgresql/util/PSQLState.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 /**
  * This class is used for holding SQLState codes.
  */
@@ -115,7 +117,7 @@ public enum PSQLState {
     return this.state;
   }
 
-  public static boolean isConnectionError(String psqlState) {
+  public static boolean isConnectionError(/* @Nullable */ String psqlState) {
     return PSQLState.CONNECTION_UNABLE_TO_CONNECT.getState().equals(psqlState)
         || PSQLState.CONNECTION_DOES_NOT_EXIST.getState().equals(psqlState)
         || PSQLState.CONNECTION_REJECTED.getState().equals(psqlState)
diff --git a/src/main/java/org/postgresql/util/PSQLWarning.java b/src/main/java/org/postgresql/util/PSQLWarning.java
index 440ee642fc9ae361b0d26e742f9342a7668ca763..dc0c23eb9130feb6f3f40541a2562d50a4f2ab74 100644
--- a/src/main/java/org/postgresql/util/PSQLWarning.java
+++ b/src/main/java/org/postgresql/util/PSQLWarning.java
@@ -5,18 +5,20 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.SQLWarning;
 
 public class PSQLWarning extends SQLWarning {
 
-  private ServerErrorMessage serverError;
+  private final ServerErrorMessage serverError;
 
   public PSQLWarning(ServerErrorMessage err) {
     super(err.toString(), err.getSQLState());
     this.serverError = err;
   }
 
-  public String getMessage() {
+  public /* @Nullable */ String getMessage() {
     return serverError.getMessage();
   }
 
diff --git a/src/main/java/org/postgresql/util/ServerErrorMessage.java b/src/main/java/org/postgresql/util/ServerErrorMessage.java
index 5921b88ff18e92da4737bdb82c8a328e1ee10f82..5c33a46036ced16c40f49f3a67bdb90455e6aed9 100644
--- a/src/main/java/org/postgresql/util/ServerErrorMessage.java
+++ b/src/main/java/org/postgresql/util/ServerErrorMessage.java
@@ -7,6 +7,8 @@ package org.postgresql.util;
 
 import org.postgresql.core.EncodingPredictor;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.Serializable;
 import java.util.HashMap;
 import java.util.Map;
@@ -66,23 +68,23 @@ public class ServerErrorMessage implements Serializable {
     }
   }
 
-  public String getSQLState() {
+  public /* @Nullable */ String getSQLState() {
     return mesgParts.get(SQLSTATE);
   }
 
-  public String getMessage() {
+  public /* @Nullable */ String getMessage() {
     return mesgParts.get(MESSAGE);
   }
 
-  public String getSeverity() {
+  public /* @Nullable */ String getSeverity() {
     return mesgParts.get(SEVERITY);
   }
 
-  public String getDetail() {
+  public /* @Nullable */ String getDetail() {
     return mesgParts.get(DETAIL);
   }
 
-  public String getHint() {
+  public /* @Nullable */ String getHint() {
     return mesgParts.get(HINT);
   }
 
@@ -90,31 +92,31 @@ public class ServerErrorMessage implements Serializable {
     return getIntegerPart(POSITION);
   }
 
-  public String getWhere() {
+  public /* @Nullable */ String getWhere() {
     return mesgParts.get(WHERE);
   }
 
-  public String getSchema() {
+  public /* @Nullable */ String getSchema() {
     return mesgParts.get(SCHEMA);
   }
 
-  public String getTable() {
+  public /* @Nullable */ String getTable() {
     return mesgParts.get(TABLE);
   }
 
-  public String getColumn() {
+  public /* @Nullable */ String getColumn() {
     return mesgParts.get(COLUMN);
   }
 
-  public String getDatatype() {
+  public /* @Nullable */ String getDatatype() {
     return mesgParts.get(DATATYPE);
   }
 
-  public String getConstraint() {
+  public /* @Nullable */ String getConstraint() {
     return mesgParts.get(CONSTRAINT);
   }
 
-  public String getFile() {
+  public /* @Nullable */ String getFile() {
     return mesgParts.get(FILE);
   }
 
@@ -122,11 +124,11 @@ public class ServerErrorMessage implements Serializable {
     return getIntegerPart(LINE);
   }
 
-  public String getRoutine() {
+  public /* @Nullable */ String getRoutine() {
     return mesgParts.get(ROUTINE);
   }
 
-  public String getInternalQuery() {
+  public /* @Nullable */ String getInternalQuery() {
     return mesgParts.get(INTERNAL_QUERY);
   }
 
diff --git a/src/main/java/org/postgresql/util/SharedTimer.java b/src/main/java/org/postgresql/util/SharedTimer.java
index bde4ab29f076b7fc376c9ba91b9840441e7a2579..fb0720c661e2faefb2a9fb64f5afe10129b1f1d0 100644
--- a/src/main/java/org/postgresql/util/SharedTimer.java
+++ b/src/main/java/org/postgresql/util/SharedTimer.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.util;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.Timer;
 import java.util.concurrent.atomic.AtomicInteger;
 import java.util.logging.Level;
@@ -15,7 +17,7 @@ public class SharedTimer {
   private static final AtomicInteger timerCount = new AtomicInteger(0);
 
   private static final Logger LOGGER = Logger.getLogger(SharedTimer.class.getName());
-  private volatile Timer timer = null;
+  private volatile /* @Nullable */ Timer timer;
   private final AtomicInteger refCount = new AtomicInteger(0);
 
   public SharedTimer() {
@@ -26,6 +28,7 @@ public class SharedTimer {
   }
 
   public synchronized Timer getTimer() {
+    Timer timer = this.timer;
     if (timer == null) {
       int index = timerCount.incrementAndGet();
 
@@ -40,7 +43,7 @@ public class SharedTimer {
          */
         Thread.currentThread().setContextClassLoader(null);
 
-        timer = new Timer("PostgreSQL-JDBC-SharedTimer-" + index, true);
+        this.timer = timer = new Timer("PostgreSQL-JDBC-SharedTimer-" + index, true);
       } finally {
         Thread.currentThread().setContextClassLoader(prevContextCL);
       }
diff --git a/src/main/java/org/postgresql/util/StreamWrapper.java b/src/main/java/org/postgresql/util/StreamWrapper.java
index c9a94b9a4bf6906038808ed1cdd2a25fb74ae92e..f449c5f2030833a48c59ca2aadaa1a1adc42aeb8 100644
--- a/src/main/java/org/postgresql/util/StreamWrapper.java
+++ b/src/main/java/org/postgresql/util/StreamWrapper.java
@@ -6,6 +6,10 @@
 
 package org.postgresql.util;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.io.ByteArrayOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
@@ -148,7 +152,7 @@ public class StreamWrapper {
       return stream;
     }
 
-    return new java.io.ByteArrayInputStream(rawData, offset, length);
+    return new java.io.ByteArrayInputStream(castNonNull(rawData), offset, length);
   }
 
   public int getLength() {
@@ -159,7 +163,7 @@ public class StreamWrapper {
     return offset;
   }
 
-  public byte[] getBytes() {
+  public byte /* @Nullable */ [] getBytes() {
     return rawData;
   }
 
@@ -183,8 +187,8 @@ public class StreamWrapper {
     return totalLength;
   }
 
-  private final InputStream stream;
-  private final byte[] rawData;
+  private final /* @Nullable */ InputStream stream;
+  private final byte /* @Nullable */ [] rawData;
   private final int offset;
   private final int length;
 }
diff --git a/src/main/java/org/postgresql/util/WriterHandler.java b/src/main/java/org/postgresql/util/WriterHandler.java
deleted file mode 100644
index 4a98d4f52b7fbd2bd7901adbb30dbd02516fadcc..0000000000000000000000000000000000000000
--- a/src/main/java/org/postgresql/util/WriterHandler.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- * <p>
- * http://www.apache.org/licenses/LICENSE-2.0
- * <p>
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-package org.postgresql.util;
-
-import java.io.Writer;
-import java.util.logging.ErrorManager;
-import java.util.logging.Formatter;
-import java.util.logging.Handler;
-import java.util.logging.Level;
-import java.util.logging.LogRecord;
-import java.util.logging.SimpleFormatter;
-
-public class WriterHandler extends Handler {
-
-  private boolean doneHeader;
-
-  private Writer writer;
-
-  public WriterHandler(Writer inWriter) {
-    super();
-    setLevel(Level.INFO);
-    setFilter(null);
-    setFormatter(new SimpleFormatter());
-    writer = inWriter;
-  }
-
-  public WriterHandler(Writer inWriter, Formatter formatter) {
-    super();
-    setLevel(Level.INFO);
-    setFilter(null);
-    setFormatter(formatter);
-    writer = inWriter;
-
-  }
-
-  public synchronized void publish(final LogRecord record) {
-    if (!isLoggable(record)) {
-      return;
-    }
-    String msg;
-    try {
-      msg = getFormatter().format(record);
-    } catch (Exception ex) {
-      // We don't want to throw an exception here, but we
-      // report the exception to any registered ErrorManager.
-      reportError(null, ex, ErrorManager.FORMAT_FAILURE);
-      return;
-    }
-
-    try {
-      if (!doneHeader) {
-        writer.write(getFormatter().getHead(this));
-        doneHeader = true;
-      }
-      writer.write(msg);
-    } catch (Exception ex) {
-      // We don't want to throw an exception here, but we
-      // report the exception to any registered ErrorManager.
-      reportError(null, ex, ErrorManager.WRITE_FAILURE);
-    }
-  }
-
-  public boolean isLoggable(final LogRecord record) {
-    if (writer == null || record == null) {
-      return false;
-    }
-    return super.isLoggable(record);
-  }
-
-  public synchronized void flush() {
-    if (writer != null) {
-      try {
-        writer.flush();
-      } catch (Exception ex) {
-        // We don't want to throw an exception here, but we
-        // report the exception to any registered ErrorManager.
-        reportError(null, ex, ErrorManager.FLUSH_FAILURE);
-      }
-    }
-  }
-
-  private synchronized void flushAndClose() throws SecurityException {
-
-    if (writer != null) {
-      try {
-        if (!doneHeader) {
-          writer.write(getFormatter().getHead(this));
-          doneHeader = true;
-        }
-        writer.write(getFormatter().getTail(this));
-        writer.flush();
-        writer.close();
-      } catch (Exception ex) {
-        // We don't want to throw an exception here, but we
-        // report the exception to any registered ErrorManager.
-        reportError(null, ex, ErrorManager.CLOSE_FAILURE);
-      }
-      writer = null;
-
-    }
-  }
-
-  public synchronized void close() throws SecurityException {
-    flushAndClose();
-  }
-}
diff --git a/src/main/java/org/postgresql/util/internal/Nullness.java b/src/main/java/org/postgresql/util/internal/Nullness.java
new file mode 100644
index 0000000000000000000000000000000000000000..952c29cf710530dd16ede7513f72dc9bc900ee0e
--- /dev/null
+++ b/src/main/java/org/postgresql/util/internal/Nullness.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.util.internal;
+
+// import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
+// import org.checkerframework.checker.nullness.qual.NonNull;
+// import org.checkerframework.checker.nullness.qual.Nullable;
+// import org.checkerframework.dataflow.qual.Pure;
+
+/**
+ * The methods in this class allow to cast nullable reference to a non-nullable one.
+ * This is an internal class, and it is not meant to be used as a public API.
+ */
+@SuppressWarnings({"cast.unsafe", "NullableProblems", "contracts.postcondition.not.satisfied"})
+public class Nullness {
+  /* @Pure */
+  public static /* @EnsuresNonNull("#1") */ <T extends /* @Nullable */ Object> /* @NonNull */ T castNonNull(
+      /* @Nullable */ T ref) {
+    assert ref != null : "Misuse of castNonNull: called with a null argument";
+    return (/* @NonNull */ T) ref;
+  }
+
+  /* @Pure */
+  public static /* @EnsuresNonNull("#1") */ <T extends /* @Nullable */ Object> /* @NonNull */ T castNonNull(
+      /* @Nullable */ T ref, String message) {
+    assert ref != null : "Misuse of castNonNull: called with a null argument " + message;
+    return (/* @NonNull */ T) ref;
+  }
+}
diff --git a/src/main/java/org/postgresql/xa/PGXAConnection.java b/src/main/java/org/postgresql/xa/PGXAConnection.java
index a746f77988a885dcfbf32495e5436743d065f268..6d13a92902a79e3314c007a2d2f4000715b54163 100644
--- a/src/main/java/org/postgresql/xa/PGXAConnection.java
+++ b/src/main/java/org/postgresql/xa/PGXAConnection.java
@@ -5,6 +5,8 @@
 
 package org.postgresql.xa;
 
+import static org.postgresql.util.internal.Nullness.castNonNull;
+
 import org.postgresql.PGConnection;
 import org.postgresql.core.BaseConnection;
 import org.postgresql.core.TransactionState;
@@ -13,6 +15,8 @@ import org.postgresql.util.GT;
 import org.postgresql.util.PSQLException;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.lang.reflect.InvocationHandler;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
@@ -50,10 +54,10 @@ public class PGXAConnection extends PGPooledConnection implements XAConnection,
    */
   private final BaseConnection conn;
 
-  private Xid currentXid;
+  private /* @Nullable */ Xid currentXid;
 
   private State state;
-  private Xid preparedXid;
+  private /* @Nullable */ Xid preparedXid;
   private boolean committedOrRolledBack;
 
   /*
@@ -116,13 +120,14 @@ public class PGXAConnection extends PGPooledConnection implements XAConnection,
     }
 
     @Override
-    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+    @SuppressWarnings("throwing.nullable")
+    public /* @Nullable */ Object invoke(Object proxy, Method method, /* @Nullable */ Object[] args) throws Throwable {
       if (state != State.IDLE) {
         String methodName = method.getName();
         if (methodName.equals("commit")
             || methodName.equals("rollback")
             || methodName.equals("setSavePoint")
-            || (methodName.equals("setAutoCommit") && (Boolean) args[0])) {
+            || (methodName.equals("setAutoCommit") && castNonNull((Boolean) args[0]))) {
           throw new PSQLException(
               GT.tr(
                   "Transaction control methods setAutoCommit(true), commit, rollback and setSavePoint not allowed while an XA transaction is active."),
@@ -276,7 +281,7 @@ public class PGXAConnection extends PGPooledConnection implements XAConnection,
       throw new PGXAException(GT.tr("xid must not be null"), XAException.XAER_INVAL);
     }
 
-    if (state != State.ACTIVE || !currentXid.equals(xid)) {
+    if (state != State.ACTIVE || !xid.equals(currentXid)) {
       throw new PGXAException(GT.tr("tried to call end without corresponding start call. state={0}, start xid={1}, currentXid={2}, preparedXid={3}", state, xid, currentXid, preparedXid),
           XAException.XAER_PROTO);
     }
@@ -401,7 +406,7 @@ public class PGXAConnection extends PGPooledConnection implements XAConnection,
               "SELECT gid FROM pg_prepared_xacts where database = current_database()");
           LinkedList<Xid> l = new LinkedList<Xid>();
           while (rs.next()) {
-            Xid recoveredXid = RecoveredXid.stringToXid(rs.getString(1));
+            Xid recoveredXid = RecoveredXid.stringToXid(castNonNull(rs.getString(1)));
             if (recoveredXid != null) {
               l.add(recoveredXid);
             }
@@ -652,9 +657,13 @@ public class PGXAConnection extends PGPooledConnection implements XAConnection,
   }
 
   private boolean isPostgreSQLIntegrityConstraintViolation(SQLException sqlException) {
-    return sqlException instanceof PSQLException
-        && sqlException.getSQLState().length() == 5
-        && sqlException.getSQLState().startsWith("23"); // Class 23 - Integrity Constraint Violation
+    if (!(sqlException instanceof PSQLException)) {
+      return false;
+    }
+    String sqlState = sqlException.getSQLState();
+    return sqlState != null
+        && sqlState.length() == 5
+        && sqlState.startsWith("23"); // Class 23 - Integrity Constraint Violation
   }
 
   private enum State {
diff --git a/src/main/java/org/postgresql/xa/PGXADataSource.java b/src/main/java/org/postgresql/xa/PGXADataSource.java
index 30059cba764aea19300b26d5fccdfe9132a3cfd4..ed067f8133f1d44a3ef8a8939e39df6fa1428ade 100644
--- a/src/main/java/org/postgresql/xa/PGXADataSource.java
+++ b/src/main/java/org/postgresql/xa/PGXADataSource.java
@@ -8,6 +8,8 @@ package org.postgresql.xa;
 import org.postgresql.core.BaseConnection;
 import org.postgresql.ds.common.BaseDataSource;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.sql.Connection;
 import java.sql.SQLException;
 
@@ -42,7 +44,8 @@ public class PGXADataSource extends BaseDataSource implements XADataSource {
    * @return A valid database connection.
    * @throws SQLException Occurs when the database connection cannot be established.
    */
-  public XAConnection getXAConnection(String user, String password) throws SQLException {
+  public XAConnection getXAConnection(/* @Nullable */ String user, /* @Nullable */ String password)
+      throws SQLException {
     Connection con = super.getConnection(user, password);
     return new PGXAConnection((BaseConnection) con);
   }
diff --git a/src/main/java/org/postgresql/xa/PGXADataSourceFactory.java b/src/main/java/org/postgresql/xa/PGXADataSourceFactory.java
index 11af7bc73daff8a6f8a7a21a463d5904f483533c..454f427375f55e6bbdebd15e250675bd4ad9a9b0 100644
--- a/src/main/java/org/postgresql/xa/PGXADataSourceFactory.java
+++ b/src/main/java/org/postgresql/xa/PGXADataSourceFactory.java
@@ -7,6 +7,8 @@ package org.postgresql.xa;
 
 import org.postgresql.ds.common.PGObjectFactory;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.Hashtable;
 
 import javax.naming.Context;
@@ -24,7 +26,7 @@ public class PGXADataSourceFactory extends PGObjectFactory {
    * "JDBC2 Enterprise" edition build which doesn't include PGXADataSource.
    */
 
-  public Object getObjectInstance(Object obj, Name name, Context nameCtx,
+  public /* @Nullable */ Object getObjectInstance(Object obj, Name name, Context nameCtx,
       Hashtable<?, ?> environment) throws Exception {
     Reference ref = (Reference) obj;
     String className = ref.getClassName();
diff --git a/src/main/java/org/postgresql/xa/RecoveredXid.java b/src/main/java/org/postgresql/xa/RecoveredXid.java
index 7229dd7ffb7cecb800c1c150d342d0ce4f488cd5..f32a1d9aee34145f10597ff9aa0c229f881b01e7 100644
--- a/src/main/java/org/postgresql/xa/RecoveredXid.java
+++ b/src/main/java/org/postgresql/xa/RecoveredXid.java
@@ -7,6 +7,8 @@ package org.postgresql.xa;
 
 import org.postgresql.util.Base64;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
+
 import java.util.Arrays;
 
 import javax.transaction.xa.Xid;
@@ -16,6 +18,12 @@ class RecoveredXid implements Xid {
   byte[] globalTransactionId;
   byte[] branchQualifier;
 
+  RecoveredXid(int formatId, byte[] globalTransactionId, byte[] branchQualifier) {
+    this.formatId = formatId;
+    this.globalTransactionId = globalTransactionId;
+    this.branchQualifier = branchQualifier;
+  }
+
   public int getFormatId() {
     return formatId;
   }
@@ -38,7 +46,7 @@ class RecoveredXid implements Xid {
     return result;
   }
 
-  public boolean equals(Object o) {
+  public boolean equals(/* @Nullable */ Object o) {
     if (o == this) {
       // optimization for the common case.
       return true;
@@ -80,9 +88,7 @@ class RecoveredXid implements Xid {
   /**
    * @return recovered xid, or null if s does not represent a valid xid encoded by the driver.
    */
-  static Xid stringToXid(String s) {
-    RecoveredXid xid = new RecoveredXid();
-
+  static /* @Nullable */ Xid stringToXid(String s) {
     int a = s.indexOf("_");
     int b = s.lastIndexOf("_");
 
@@ -92,17 +98,13 @@ class RecoveredXid implements Xid {
     }
 
     try {
-      xid.formatId = Integer.parseInt(s.substring(0, a));
-      xid.globalTransactionId = Base64.decode(s.substring(a + 1, b));
-      xid.branchQualifier = Base64.decode(s.substring(b + 1));
-
-      if (xid.globalTransactionId == null || xid.branchQualifier == null) {
-        return null;
-      }
+      int formatId = Integer.parseInt(s.substring(0, a));
+      byte[] globalTransactionId = Base64.decode(s.substring(a + 1, b));
+      byte[] branchQualifier = Base64.decode(s.substring(b + 1));
+      return new RecoveredXid(formatId, globalTransactionId, branchQualifier);
     } catch (Exception ex) {
+      // TODO: log warning
       return null; // Doesn't seem to be an xid generated by this driver.
     }
-
-    return xid;
   }
 }
diff --git a/src/main/java/org/postgresql/xml/EmptyStringEntityResolver.java b/src/main/java/org/postgresql/xml/EmptyStringEntityResolver.java
index e96a39b89570e7e1199e2cf5545db5243ae26bd9..3e71420ea8a1a1764104c742c173fe4cc35a763f 100644
--- a/src/main/java/org/postgresql/xml/EmptyStringEntityResolver.java
+++ b/src/main/java/org/postgresql/xml/EmptyStringEntityResolver.java
@@ -5,6 +5,7 @@
 
 package org.postgresql.xml;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
 import org.xml.sax.EntityResolver;
 import org.xml.sax.InputSource;
 import org.xml.sax.SAXException;
@@ -16,7 +17,7 @@ public class EmptyStringEntityResolver implements EntityResolver {
   public static final EmptyStringEntityResolver INSTANCE = new EmptyStringEntityResolver();
 
   @Override
-  public InputSource resolveEntity(String publicId, String systemId)
+  public InputSource resolveEntity(/* @Nullable */ String publicId, String systemId)
       throws SAXException, IOException {
     return new InputSource(new StringReader(""));
   }
diff --git a/src/main/resources/LICENSE b/src/main/resources/LICENSE
deleted file mode 100644
index 5d3a0018c70cc0879c56f1f35370a923a7f00a74..0000000000000000000000000000000000000000
--- a/src/main/resources/LICENSE
+++ /dev/null
@@ -1,23 +0,0 @@
-Copyright (c) 1997, PostgreSQL Global Development Group
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice,
-   this list of conditions and the following disclaimer.
-2. Redistributions in binary form must reproduce the above copyright notice,
-   this list of conditions and the following disclaimer in the documentation
-   and/or other materials provided with the distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
-LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
-CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
-SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
-CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/main/resources/META-INF/LICENSE b/src/main/resources/META-INF/LICENSE
index 5d3a0018c70cc0879c56f1f35370a923a7f00a74..daa7e320f247b6895ea38fabfb72702be31001a8 100644
--- a/src/main/resources/META-INF/LICENSE
+++ b/src/main/resources/META-INF/LICENSE
@@ -21,3 +21,13 @@ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 POSSIBILITY OF SUCH DAMAGE.
+
+Additional License files can be found in the 'licenses' folder located in the same directory as the LICENSE file (i.e. this file)
+
+- Software produced outside the ASF which is available under other licenses (not Apache-2.0)
+
+BSD-2-Clause
+* com.ongres.scram:client:2.1
+* com.ongres.scram:common:2.1
+* com.ongres.stringprep:saslprep:1.1
+* com.ongres.stringprep:stringprep:1.1
diff --git a/src/main/resources/META-INF/MANIFEST.MF b/src/main/resources/META-INF/MANIFEST.MF
index 78102fb476c627a61ee90a9a84b2f49937f25f06..454a4cea2aa6981fc0fe790046be6dde2baab2e5 100644
--- a/src/main/resources/META-INF/MANIFEST.MF
+++ b/src/main/resources/META-INF/MANIFEST.MF
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
 Implementation-Title: PostgreSQL JDBC Driver
 Bundle-License: BSD-2-Clause
 Automatic-Module-Name: org.postgresql.jdbc
-Implementation-Version: 42.2.14
+Implementation-Version: 42.2.15
 Specification-Vendor: Oracle Corporation
 Specification-Title: JDBC
 Implementation-Vendor-Id: org.postgresql
diff --git a/src/main/resources/META-INF/licenses/client-2.1.jar/LICENSE b/src/main/resources/META-INF/licenses/client-2.1.jar/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..13fdb102a3a749bd124d68af68e5b36a4e29deaa
--- /dev/null
+++ b/src/main/resources/META-INF/licenses/client-2.1.jar/LICENSE
@@ -0,0 +1,22 @@
+Copyright 2017, OnGres.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/main/resources/META-INF/licenses/common-2.1.jar/LICENSE b/src/main/resources/META-INF/licenses/common-2.1.jar/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..13fdb102a3a749bd124d68af68e5b36a4e29deaa
--- /dev/null
+++ b/src/main/resources/META-INF/licenses/common-2.1.jar/LICENSE
@@ -0,0 +1,22 @@
+Copyright 2017, OnGres.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/main/resources/META-INF/licenses/saslprep-1.1.jar/LICENSE b/src/main/resources/META-INF/licenses/saslprep-1.1.jar/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..84b3fe0cd5df6bd2688210bf3207230e87ebc8c3
--- /dev/null
+++ b/src/main/resources/META-INF/licenses/saslprep-1.1.jar/LICENSE
@@ -0,0 +1,22 @@
+Copyright 2019, OnGres.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/main/resources/META-INF/licenses/stringprep-1.1.jar/LICENSE b/src/main/resources/META-INF/licenses/stringprep-1.1.jar/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..84b3fe0cd5df6bd2688210bf3207230e87ebc8c3
--- /dev/null
+++ b/src/main/resources/META-INF/licenses/stringprep-1.1.jar/LICENSE
@@ -0,0 +1,22 @@
+Copyright 2019, OnGres.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
+ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/src/test/java/org/postgresql/jdbc/AbstractArraysTest.java b/src/test/java/org/postgresql/jdbc/AbstractArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f892712b288faf9942e241a40fbf91a861c991d5
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/AbstractArraysTest.java
@@ -0,0 +1,946 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import static org.junit.Assert.assertEquals;
+
+import org.postgresql.PGNotification;
+import org.postgresql.copy.CopyManager;
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.CachedQuery;
+import org.postgresql.core.Encoding;
+import org.postgresql.core.QueryExecutor;
+import org.postgresql.core.ReplicationProtocol;
+import org.postgresql.core.TransactionState;
+import org.postgresql.core.TypeInfo;
+import org.postgresql.core.Version;
+import org.postgresql.fastpath.Fastpath;
+import org.postgresql.jdbc.FieldMetadata.Key;
+import org.postgresql.largeobject.LargeObjectManager;
+import org.postgresql.replication.PGReplicationConnection;
+import org.postgresql.util.LruCache;
+import org.postgresql.util.PGobject;
+import org.postgresql.xml.PGXmlFactoryFactory;
+
+import org.junit.Test;
+
+import java.lang.reflect.Array;
+import java.sql.Blob;
+import java.sql.CallableStatement;
+import java.sql.Clob;
+import java.sql.DatabaseMetaData;
+import java.sql.NClob;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLClientInfoException;
+import java.sql.SQLException;
+import java.sql.SQLWarning;
+import java.sql.SQLXML;
+import java.sql.Savepoint;
+import java.sql.Statement;
+import java.sql.Struct;
+import java.util.Map;
+import java.util.Properties;
+import java.util.TimerTask;
+import java.util.concurrent.Executor;
+import java.util.logging.Logger;
+
+public abstract class AbstractArraysTest<A> {
+
+  private static final BaseConnection ENCODING_CONNECTION = new EncodingConnection(Encoding.getJVMEncoding("utf-8"));
+
+  private final A[][] testData;
+
+  private final boolean binarySupported;
+
+  private final int arrayTypeOid;
+
+  /**
+   *
+   * @param testData
+   *          3 dimensional array to use for testing.
+   * @param binarySupported
+   *          Indicates if binary support is epxected for the type.
+   */
+  public AbstractArraysTest(A[][] testData, boolean binarySupported, int arrayTypeOid) {
+    super();
+    this.testData = testData;
+    this.binarySupported = binarySupported;
+    this.arrayTypeOid = arrayTypeOid;
+  }
+
+  protected void assertArraysEquals(String message, A expected, Object actual) {
+    final int expectedLength = Array.getLength(expected);
+    assertEquals(message + " size", expectedLength, Array.getLength(actual));
+    for (int i = 0; i < expectedLength; ++i) {
+      assertEquals(message + " value at " + i, Array.get(expected, i), Array.get(actual, i));
+    }
+  }
+
+  @Test
+  public void testBinary() throws Exception {
+
+    A data = testData[0][0];
+
+    ArrayEncoding.ArrayEncoder<A> support = ArrayEncoding.getArrayEncoder(data);
+
+    final int defaultArrayTypeOid = support.getDefaultArrayTypeOid();
+
+    assertEquals(binarySupported, support.supportBinaryRepresentation(defaultArrayTypeOid));
+
+    if (binarySupported) {
+
+      final PgArray pgArray = new PgArray(ENCODING_CONNECTION, defaultArrayTypeOid,
+          support.toBinaryRepresentation(ENCODING_CONNECTION, data, defaultArrayTypeOid));
+
+      Object actual = pgArray.getArray();
+
+      assertArraysEquals("", data, actual);
+    }
+  }
+
+  @Test
+  public void testString() throws Exception {
+
+    A data = testData[0][0];
+
+    ArrayEncoding.ArrayEncoder<A> support = ArrayEncoding.getArrayEncoder(data);
+
+    final String arrayString = support.toArrayString(',', data);
+
+    final PgArray pgArray = new PgArray(ENCODING_CONNECTION, arrayTypeOid, arrayString);
+
+    Object actual = pgArray.getArray();
+
+    assertArraysEquals("", data, actual);
+  }
+
+  @Test
+  public void test2dBinary() throws Exception {
+
+    A[] data = testData[0];
+
+    ArrayEncoding.ArrayEncoder<A[]> support = ArrayEncoding.getArrayEncoder(data);
+
+    final int defaultArrayTypeOid = support.getDefaultArrayTypeOid();
+
+    assertEquals(binarySupported, support.supportBinaryRepresentation(defaultArrayTypeOid));
+
+    if (binarySupported) {
+
+      final PgArray pgArray = new PgArray(ENCODING_CONNECTION, support.getDefaultArrayTypeOid(),
+          support.toBinaryRepresentation(ENCODING_CONNECTION, data, defaultArrayTypeOid));
+
+      Object[] actual = (Object[]) pgArray.getArray();
+
+      assertEquals(data.length, actual.length);
+
+      for (int i = 0; i < data.length; ++i) {
+        assertArraysEquals("array at position " + i, data[i], actual[i]);
+      }
+    }
+  }
+
+  @Test
+  public void test2dString() throws Exception {
+
+    final A[] data = testData[0];
+
+    final ArrayEncoding.ArrayEncoder<A[]> support = ArrayEncoding.getArrayEncoder(data);
+
+    final String arrayString = support.toArrayString(',', data);
+
+    final PgArray pgArray = new PgArray(ENCODING_CONNECTION, arrayTypeOid, arrayString);
+
+    Object[] actual = (Object[]) pgArray.getArray();
+
+    assertEquals(data.length, actual.length);
+
+    for (int i = 0; i < data.length; ++i) {
+      assertArraysEquals("array at position " + i, data[i], actual[i]);
+    }
+  }
+
+  @Test
+  public void test3dBinary() throws Exception {
+
+    ArrayEncoding.ArrayEncoder<A[][]> support = ArrayEncoding.getArrayEncoder(testData);
+
+    final int defaultArrayTypeOid = support.getDefaultArrayTypeOid();
+
+    assertEquals(binarySupported, support.supportBinaryRepresentation(defaultArrayTypeOid));
+
+    if (binarySupported) {
+
+      final PgArray pgArray = new PgArray(ENCODING_CONNECTION, support.getDefaultArrayTypeOid(),
+          support.toBinaryRepresentation(ENCODING_CONNECTION, testData, defaultArrayTypeOid));
+
+      Object[][] actual = (Object[][]) pgArray.getArray();
+
+      assertEquals(testData.length, actual.length);
+
+      for (int i = 0; i < testData.length; ++i) {
+        assertEquals("array length at " + i, testData[i].length, actual[i].length);
+        for (int j = 0; j < testData[i].length; ++j) {
+          assertArraysEquals("array at " + i + ',' + j, testData[i][j], actual[i][j]);
+        }
+      }
+    }
+  }
+
+  @Test
+  public void test3dString() throws Exception {
+
+    final ArrayEncoding.ArrayEncoder<A[][]> support = ArrayEncoding.getArrayEncoder(testData);
+
+    final String arrayString = support.toArrayString(',', testData);
+
+    final PgArray pgArray = new PgArray(ENCODING_CONNECTION, arrayTypeOid, arrayString);
+
+    Object[][] actual = (Object[][]) pgArray.getArray();
+
+    assertEquals(testData.length, actual.length);
+
+    for (int i = 0; i < testData.length; ++i) {
+      assertEquals("array length at " + i, testData[i].length, actual[i].length);
+      for (int j = 0; j < testData[i].length; ++j) {
+        assertArraysEquals("array at " + i + ',' + j, testData[i][j], actual[i][j]);
+      }
+    }
+  }
+
+  private static final class EncodingConnection implements BaseConnection {
+    private final Encoding encoding;
+    private final TypeInfo typeInfo = new TypeInfoCache(this, -1);
+
+    EncodingConnection(Encoding encoding) {
+      this.encoding = encoding;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Encoding getEncoding() throws SQLException {
+      return encoding;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TypeInfo getTypeInfo() {
+      return typeInfo;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void cancelQuery() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ResultSet execSQLQuery(String s) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ResultSet execSQLQuery(String s, int resultSetType, int resultSetConcurrency) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void execSQLUpdate(String s) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public QueryExecutor getQueryExecutor() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public ReplicationProtocol getReplicationProtocol() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object getObject(String type, String value, byte[] byteValue) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean haveMinimumServerVersion(int ver) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean haveMinimumServerVersion(Version ver) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public byte[] encodeString(String str) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String escapeString(String str) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean getStandardConformingStrings() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TimestampUtils getTimestampUtils() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Logger getLogger() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean getStringVarcharFlag() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public TransactionState getTransactionState() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean binaryTransferSend(int oid) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isColumnSanitiserDisabled() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addTimerTask(TimerTask timerTask, long milliSeconds) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void purgeTimerTasks() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public LruCache<Key, FieldMetadata> getFieldMetadataCache() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public CachedQuery createQuery(String sql, boolean escapeProcessing, boolean isParameterized, String... columnNames)
+        throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setFlushCacheOnDeallocate(boolean flushCacheOnDeallocate) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Statement createStatement() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PreparedStatement prepareStatement(String sql) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public CallableStatement prepareCall(String sql) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String nativeSQL(String sql) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setAutoCommit(boolean autoCommit) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean getAutoCommit() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void commit() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void rollback() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void close() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isClosed() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public DatabaseMetaData getMetaData() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setReadOnly(boolean readOnly) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isReadOnly() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setCatalog(String catalog) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getCatalog() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setTransactionIsolation(int level) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getTransactionIsolation() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public SQLWarning getWarnings() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void clearWarnings() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency)
+        throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Map<String, Class<?>> getTypeMap() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setTypeMap(Map<String, Class<?>> map) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setHoldability(int holdability) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getHoldability() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Savepoint setSavepoint() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Savepoint setSavepoint(String name) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void rollback(Savepoint savepoint) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void releaseSavepoint(Savepoint savepoint) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability)
+        throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency,
+        int resultSetHoldability) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency,
+        int resultSetHoldability) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Clob createClob() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Blob createBlob() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public NClob createNClob() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public SQLXML createSQLXML() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isValid(int timeout) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setClientInfo(String name, String value) throws SQLClientInfoException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setClientInfo(Properties properties) throws SQLClientInfoException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getClientInfo(String name) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Properties getClientInfo() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public java.sql.Array createArrayOf(String typeName, Object[] elements) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Struct createStruct(String typeName, Object[] attributes) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setSchema(String schema) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String getSchema() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void abort(Executor executor) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getNetworkTimeout() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public <T> T unwrap(Class<T> iface) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isWrapperFor(Class<?> iface) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public java.sql.Array createArrayOf(String typeName, Object elements) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PGNotification[] getNotifications() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PGNotification[] getNotifications(int timeoutMillis) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public CopyManager getCopyAPI() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public LargeObjectManager getLargeObjectAPI() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Fastpath getFastpathAPI() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addDataType(String type, String className) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void addDataType(String type, Class<? extends PGobject> klass) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setPrepareThreshold(int threshold) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getPrepareThreshold() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setDefaultFetchSize(int fetchSize) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getDefaultFetchSize() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getBackendPID() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String escapeIdentifier(String identifier) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public String escapeLiteral(String literal) throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PreferQueryMode getPreferQueryMode() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public AutoSave getAutosave() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void setAutosave(AutoSave autoSave) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public PGReplicationConnection getReplicationAPI() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public Map<String, String> getParameterStatuses() {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public String getParameterStatus(String parameterName) {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public PGXmlFactoryFactory getXmlFactoryFactory() throws SQLException {
+      throw new UnsupportedOperationException();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean hintReadOnly() {
+      return false;
+    }
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/ArraysTest.java b/src/test/java/org/postgresql/jdbc/ArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb93a3f4a4f534d1bbe815b2d7ba556ad617b6b9
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/ArraysTest.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import static org.junit.Assert.assertFalse;
+
+import org.postgresql.core.Oid;
+import org.postgresql.util.PSQLException;
+
+import org.junit.Test;
+
+import java.math.BigDecimal;
+import java.sql.SQLFeatureNotSupportedException;
+
+public class ArraysTest {
+
+  @Test(expected = PSQLException.class)
+  public void testNonArrayNotSupported() throws Exception {
+    ArrayEncoding.getArrayEncoder("asdflkj");
+  }
+
+  @Test(expected = PSQLException.class)
+  public void testNoByteArray() throws Exception {
+    ArrayEncoding.getArrayEncoder(new byte[] {});
+  }
+
+  @Test(expected = SQLFeatureNotSupportedException.class)
+  public void testBinaryNotSupported() throws Exception {
+    final ArrayEncoding.ArrayEncoder<BigDecimal[]> support = ArrayEncoding.getArrayEncoder(new BigDecimal[] {});
+
+    assertFalse(support.supportBinaryRepresentation(Oid.FLOAT8_ARRAY));
+
+    support.toBinaryRepresentation(null, new BigDecimal[] { BigDecimal.valueOf(3) }, Oid.FLOAT8_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/BigDecimalObjectArraysTest.java b/src/test/java/org/postgresql/jdbc/BigDecimalObjectArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..901da1062696d553c777285190913b454dcc326a
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/BigDecimalObjectArraysTest.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import static java.math.BigDecimal.valueOf;
+
+import org.postgresql.core.Oid;
+
+import java.math.BigDecimal;
+
+public class BigDecimalObjectArraysTest extends AbstractArraysTest<BigDecimal[]> {
+
+  private static final BigDecimal[][][] doubles = new BigDecimal[][][] {
+      { { valueOf(1.3), valueOf(2.4), valueOf(3.1), valueOf(4.2) },
+          { valueOf(5D), valueOf(6D), valueOf(7D), valueOf(8D) },
+          { valueOf(9D), valueOf(10D), valueOf(11D), valueOf(12D) } },
+      { { valueOf(13D), valueOf(14D), valueOf(15D), valueOf(16D) }, { valueOf(17D), valueOf(18D), valueOf(19D), null },
+          { valueOf(21D), valueOf(22D), valueOf(23D), valueOf(24D) } } };
+
+  public BigDecimalObjectArraysTest() {
+    super(doubles, false, Oid.NUMERIC_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/BooleanArraysTest.java b/src/test/java/org/postgresql/jdbc/BooleanArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c3717b5f4ae4340f6e94a3f4e473ff3ec6650afe
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/BooleanArraysTest.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class BooleanArraysTest extends AbstractArraysTest<boolean[]> {
+  private static final boolean[][][] booleans = new boolean[][][] {
+      { { true, false, false, true }, { false, false, true, true }, { true, true, false, false } },
+      { { false, true, true, false }, { true, false, true, false }, { false, true, false, true } } };
+
+  public BooleanArraysTest() {
+    super(booleans, true, Oid.BOOL_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/BooleanObjectArraysTest.java b/src/test/java/org/postgresql/jdbc/BooleanObjectArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..6e33d1cd62098eff76a0c7c48353e422bbf7b9f0
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/BooleanObjectArraysTest.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class BooleanObjectArraysTest extends AbstractArraysTest<Boolean[]> {
+  private static final Boolean[][][] booleans = new Boolean[][][] {
+      { { true, false, null, true }, { false, false, true, true }, { true, true, false, false } },
+      { { false, true, true, false }, { true, false, true, null }, { false, true, false, true } } };
+
+  public BooleanObjectArraysTest() {
+    super(booleans, true, Oid.BOOL_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/ByteaArraysTest.java b/src/test/java/org/postgresql/jdbc/ByteaArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..3a8141fa394442dd7a795671d0b58aeb2ac4b8e7
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/ByteaArraysTest.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import static org.junit.Assert.assertEquals;
+
+import org.postgresql.core.Oid;
+
+import org.junit.Assert;
+
+import java.lang.reflect.Array;
+
+public class ByteaArraysTest extends AbstractArraysTest<byte[][]> {
+
+  private static final byte[][][][] longs = new byte[][][][] {
+      { { { 0x1, 0x2, (byte) 0xFF, 0x4 }, { 0x5, 0x6, 0x7, (byte) 0xFF }, null, { 0x9, 0x10, 0x11, 0x12 } },
+          { null, { 0x13, 0x14, 0x15, 0x16 }, { 0x17, 0x18, (byte) 0xFF, 0x20 }, { 0x1, 0x2, (byte) 0xFF, 0x4 } },
+          { { 0x1, 0x2, (byte) 0xFF, 0x4 }, { 0x1, 0x2, (byte) 0xFF, 0x4 }, { 0x1, 0x2, (byte) 0xFF, 0x4 },
+              { 0x1, 0x2, (byte) 0xFF, 0x4 } } },
+      { { { 0x1, 0x2, (byte) 0xFF, 0x4 }, { 0x1, 0x2, (byte) 0xFF, 0x4 }, { 0x1, 0x2, (byte) 0xFF, 0x4 },
+          { 0x1, 0x2, (byte) 0xFE, 0x4 } },
+          { { 0x1, 0x2, (byte) 0xCD, 0x4 }, { 0x1, 0x2, (byte) 0xFF, 0x4 }, { 0x1, 0x2, (byte) 0xFF, 0x4 },
+              { 0x1, 0x2, (byte) 0xFF, 0x4 } },
+          { { 0x1, 0x2, (byte) 0xFF, 0x4 }, { 0x1, 0x2, (byte) 0xFE, 0x10 }, { 0x1, 0x2, (byte) 0xFF, 0x4 },
+              { 0x1, 0x2, (byte) 0xFF, 0x4 } } } };
+
+  public ByteaArraysTest() {
+    super(longs, true, Oid.BYTEA_ARRAY);
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected void assertArraysEquals(String message, byte[][] expected, Object actual) {
+    final int expectedLength = Array.getLength(expected);
+    assertEquals(message + " size", expectedLength, Array.getLength(actual));
+    for (int i = 0; i < expectedLength; ++i) {
+      Assert.assertArrayEquals(message + " value at " + i, expected[i], (byte[]) Array.get(actual, i));
+    }
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/DoubleArraysTest.java b/src/test/java/org/postgresql/jdbc/DoubleArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..1f21f547d674ed27a68d13fe3ffd905446b8b721
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/DoubleArraysTest.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class DoubleArraysTest extends AbstractArraysTest<double[]> {
+
+  private static final double[][][] doubles = new double[][][] {
+      { { 1.2, 2.3, 3.7, 4.9 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
+      { { 13, 14, 15, 16 }, { 17, 18, 19, 20 }, { 21, 22, 23, 24 } } };
+
+  public DoubleArraysTest() {
+    super(doubles, true, Oid.FLOAT8_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/DoubleObjectArraysTest.java b/src/test/java/org/postgresql/jdbc/DoubleObjectArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..90bc82036fa2892908adef6b98280d9b33be8563
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/DoubleObjectArraysTest.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class DoubleObjectArraysTest extends AbstractArraysTest<Double[]> {
+
+  private static final Double[][][] doubles = new Double[][][] {
+      { { 1.3, 2.4, 3.1, 4.2 }, { 5D, 6D, 7D, 8D }, { 9D, 10D, 11D, 12D } },
+      { { 13D, 14D, 15D, 16D }, { 17D, 18D, 19D, null }, { 21D, 22D, 23D, 24D } } };
+
+  public DoubleObjectArraysTest() {
+    super(doubles, true, Oid.FLOAT8_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/FloatArraysTest.java b/src/test/java/org/postgresql/jdbc/FloatArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ef9ed26ad74e921cab69d5353f05ceb1cfcf72de
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/FloatArraysTest.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class FloatArraysTest extends AbstractArraysTest<float[]> {
+
+  private static final float[][][] floats = new float[][][] {
+      { { 1.2f, 2.3f, 3.7f, 4.9f }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
+      { { 13, 14, 15, 16 }, { 17, 18, 19, 20 }, { 21, 22, 23, 24 } } };
+
+  public FloatArraysTest() {
+    super(floats, true, Oid.FLOAT4_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/FloatObjectArraysTest.java b/src/test/java/org/postgresql/jdbc/FloatObjectArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..c9a4a5f9892467801c7416796000339064589292
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/FloatObjectArraysTest.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class FloatObjectArraysTest extends AbstractArraysTest<Float[]> {
+
+  private static final Float[][][] floats = new Float[][][] {
+      { { 1.3f, 2.4f, 3.1f, 4.2f }, { 5f, 6f, 7f, 8f }, { 9f, 10f, 11f, 12f } },
+      { { 13f, 14f, 15f, 16f }, { 17f, 18f, 19f, null }, { 21f, 22f, 23f, 24f } } };
+
+  public FloatObjectArraysTest() {
+    super(floats, true, Oid.FLOAT4_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/IntArraysTest.java b/src/test/java/org/postgresql/jdbc/IntArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..01ce25a32a4325fa65e5e3ac1b0f3a536ef1ea98
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/IntArraysTest.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class IntArraysTest extends AbstractArraysTest<int[]> {
+
+  private static final int[][][] ints = new int[][][] { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
+      { { 13, 14, 15, 16 }, { 17, 18, 19, 20 }, { 21, 22, 23, 24 } } };
+
+  public IntArraysTest() {
+    super(ints, true, Oid.INT4_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/IntegerObjectArraysTest.java b/src/test/java/org/postgresql/jdbc/IntegerObjectArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..026659592c715605a3859a079b69595ca78c61b2
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/IntegerObjectArraysTest.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class IntegerObjectArraysTest extends AbstractArraysTest<Integer[]> {
+
+  private static final Integer[][][] ints = new Integer[][][] {
+      { { 1, 2, 3, 4 }, { 5, null, 7, 8 }, { 9, 10, 11, 12 } },
+      { { 13, 14, 15, 16 }, { 17, 18, 19, 20 }, { 21, 22, 23, 24 } } };
+
+  public IntegerObjectArraysTest() {
+    super(ints, true, Oid.INT4_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/LongArraysTest.java b/src/test/java/org/postgresql/jdbc/LongArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..db2c6c4428c43d7a9aded5193481ceda3af42cf1
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/LongArraysTest.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class LongArraysTest extends AbstractArraysTest<long[]> {
+
+  private static final long[][][] longs = new long[][][] { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
+      { { 13, 14, 15, 16 }, { 17, 18, 19, 20 }, { 21, 22, 23, 24 } } };
+
+  public LongArraysTest() {
+    super(longs, true, Oid.INT8_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/LongObjectArraysTest.java b/src/test/java/org/postgresql/jdbc/LongObjectArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..5625fabb75a1823ce3cfc8cb5e44871ca614d1e9
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/LongObjectArraysTest.java
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class LongObjectArraysTest extends AbstractArraysTest<Long[]> {
+
+  private static final Long[][][] longs = new Long[][][] {
+      { { 1L, 2L, null, 4L }, { 5L, 6L, 7L, 8L }, { 9L, 10L, 11L, 12L } },
+      { { 13L, 14L, 15L, 16L }, { 17L, 18L, 19L, 20L }, { 21L, 22L, 23L, 24L } } };
+
+  public LongObjectArraysTest() {
+    super(longs, true, Oid.INT8_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/PgSQLXMLTest.java b/src/test/java/org/postgresql/jdbc/PgSQLXMLTest.java
index 461ad037d3b888a4f1f6554ef8c30ccc7baf582c..9bf848e2c3a2b33688e6d712eb0406c7aea790ca 100644
--- a/src/test/java/org/postgresql/jdbc/PgSQLXMLTest.java
+++ b/src/test/java/org/postgresql/jdbc/PgSQLXMLTest.java
@@ -7,8 +7,8 @@ package org.postgresql.jdbc;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThrows;
 import static org.junit.Assert.assertTrue;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import org.postgresql.PGProperty;
 import org.postgresql.core.BaseConnection;
diff --git a/src/test/java/org/postgresql/jdbc/PrimitiveArraySupportTest.java b/src/test/java/org/postgresql/jdbc/PrimitiveArraySupportTest.java
deleted file mode 100644
index 83b028bbcea5cad7f4d5ed98308ca6152af2f290..0000000000000000000000000000000000000000
--- a/src/test/java/org/postgresql/jdbc/PrimitiveArraySupportTest.java
+++ /dev/null
@@ -1,253 +0,0 @@
-/*
- * Copyright (c) 2003, PostgreSQL Global Development Group
- * See the LICENSE file in the project root for more information.
- */
-
-package org.postgresql.jdbc;
-
-import static org.hamcrest.CoreMatchers.instanceOf;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertThat;
-import static org.junit.Assert.fail;
-
-import org.postgresql.core.Oid;
-
-import org.junit.Test;
-
-import java.sql.SQLFeatureNotSupportedException;
-
-public class PrimitiveArraySupportTest {
-
-  public PrimitiveArraySupport<long[]> longArrays = PrimitiveArraySupport.getArraySupport(new long[] {});
-  public PrimitiveArraySupport<int[]> intArrays = PrimitiveArraySupport.getArraySupport(new int[] {});
-  public PrimitiveArraySupport<short[]> shortArrays = PrimitiveArraySupport.getArraySupport(new short[] {});
-  public PrimitiveArraySupport<double[]> doubleArrays = PrimitiveArraySupport.getArraySupport(new double[] {});
-  public PrimitiveArraySupport<float[]> floatArrays = PrimitiveArraySupport.getArraySupport(new float[] {});
-  public PrimitiveArraySupport<boolean[]> booleanArrays = PrimitiveArraySupport.getArraySupport(new boolean[] {});
-
-  @Test
-  public void testLongBinary() throws Exception {
-    final long[] longs = new long[84];
-    for (int i = 0; i < 84; ++i) {
-      longs[i] = i - 3;
-    }
-
-    final PgArray pgArray = new PgArray(null, Oid.INT8_ARRAY, longArrays.toBinaryRepresentation(null, longs));
-
-    Object arrayObj = pgArray.getArray();
-
-    assertThat(arrayObj, instanceOf(Long[].class));
-
-    final Long[] actual = (Long[]) arrayObj;
-
-    assertEquals(longs.length, actual.length);
-
-    for (int i = 0; i < longs.length; ++i) {
-      assertEquals(Long.valueOf(longs[i]), actual[i]);
-    }
-  }
-
-  @Test
-  public void testLongToString() throws Exception {
-    final long[] longs = new long[] { 12367890987L, 987664198234L, -2982470923874L };
-
-    final String arrayString = longArrays.toArrayString(',', longs);
-
-    assertEquals("{12367890987,987664198234,-2982470923874}", arrayString);
-
-    final String altArrayString = longArrays.toArrayString(';', longs);
-
-    assertEquals("{12367890987;987664198234;-2982470923874}", altArrayString);
-  }
-
-  @Test
-  public void testIntBinary() throws Exception {
-    final int[] ints = new int[13];
-    for (int i = 0; i < 13; ++i) {
-      ints[i] = i - 3;
-    }
-
-    final PgArray pgArray = new PgArray(null, Oid.INT4_ARRAY, intArrays.toBinaryRepresentation(null, ints));
-
-    Object arrayObj = pgArray.getArray();
-
-    assertThat(arrayObj, instanceOf(Integer[].class));
-
-    final Integer[] actual = (Integer[]) arrayObj;
-
-    assertEquals(ints.length, actual.length);
-
-    for (int i = 0; i < ints.length; ++i) {
-      assertEquals(Integer.valueOf(ints[i]), actual[i]);
-    }
-  }
-
-  @Test
-  public void testIntToString() throws Exception {
-    final int[] ints = new int[] { 12367890, 987664198, -298247092 };
-
-    final String arrayString = intArrays.toArrayString(',', ints);
-
-    assertEquals("{12367890,987664198,-298247092}", arrayString);
-
-    final String altArrayString = intArrays.toArrayString(';', ints);
-
-    assertEquals("{12367890;987664198;-298247092}", altArrayString);
-
-  }
-
-  @Test
-  public void testShortToBinary() throws Exception {
-    final short[] shorts = new short[13];
-    for (int i = 0; i < 13; ++i) {
-      shorts[i] = (short) (i - 3);
-    }
-
-    final PgArray pgArray = new PgArray(null, Oid.INT4_ARRAY, shortArrays.toBinaryRepresentation(null, shorts));
-
-    Object arrayObj = pgArray.getArray();
-
-    assertThat(arrayObj, instanceOf(Short[].class));
-
-    final Short[] actual = (Short[]) arrayObj;
-
-    assertEquals(shorts.length, actual.length);
-
-    for (int i = 0; i < shorts.length; ++i) {
-      assertEquals(Short.valueOf(shorts[i]), actual[i]);
-    }
-  }
-
-  @Test
-  public void testShortToString() throws Exception {
-    final short[] shorts = new short[] { 123, 34, -57 };
-
-    final String arrayString = shortArrays.toArrayString(',', shorts);
-
-    assertEquals("{123,34,-57}", arrayString);
-
-    final String altArrayString = shortArrays.toArrayString(';', shorts);
-
-    assertEquals("{123;34;-57}", altArrayString);
-
-  }
-
-  @Test
-  public void testDoubleBinary() throws Exception {
-    final double[] doubles = new double[13];
-    for (int i = 0; i < 13; ++i) {
-      doubles[i] = i - 3.1;
-    }
-
-    final PgArray pgArray = new PgArray(null, Oid.FLOAT8_ARRAY, doubleArrays.toBinaryRepresentation(null, doubles));
-
-    Object arrayObj = pgArray.getArray();
-
-    assertThat(arrayObj, instanceOf(Double[].class));
-
-    final Double[] actual = (Double[]) arrayObj;
-
-    assertEquals(doubles.length, actual.length);
-
-    for (int i = 0; i < doubles.length; ++i) {
-      assertEquals(Double.valueOf(doubles[i]), actual[i]);
-    }
-  }
-
-  @Test
-  public void testdoubleToString() throws Exception {
-    final double[] doubles = new double[] { 122353.345, 923487.235987, -23.239486 };
-
-    final String arrayString = doubleArrays.toArrayString(',', doubles);
-
-    assertEquals("{\"122353.345\",\"923487.235987\",\"-23.239486\"}", arrayString);
-
-    final String altArrayString = doubleArrays.toArrayString(';', doubles);
-
-    assertEquals("{\"122353.345\";\"923487.235987\";\"-23.239486\"}", altArrayString);
-
-  }
-
-  @Test
-  public void testFloatBinary() throws Exception {
-    final float[] floats = new float[13];
-    for (int i = 0; i < 13; ++i) {
-      floats[i] = (float) (i - 3.1);
-    }
-
-    final PgArray pgArray = new PgArray(null, Oid.FLOAT4_ARRAY, floatArrays.toBinaryRepresentation(null, floats));
-
-    Object arrayObj = pgArray.getArray();
-
-    assertThat(arrayObj, instanceOf(Float[].class));
-
-    final Float[] actual = (Float[]) arrayObj;
-
-    assertEquals(floats.length, actual.length);
-
-    for (int i = 0; i < floats.length; ++i) {
-      assertEquals(Float.valueOf(floats[i]), actual[i]);
-    }
-  }
-
-  @Test
-  public void testfloatToString() throws Exception {
-    final float[] floats = new float[] { 122353.34f, 923487.25f, -23.2394f };
-
-    final String arrayString = floatArrays.toArrayString(',', floats);
-
-    assertEquals("{\"122353.34\",\"923487.25\",\"-23.2394\"}", arrayString);
-
-    final String altArrayString = floatArrays.toArrayString(';', floats);
-
-    assertEquals("{\"122353.34\";\"923487.25\";\"-23.2394\"}", altArrayString);
-
-  }
-
-  @Test
-  public void testBooleanBinary() throws Exception {
-    final boolean[] bools = new boolean[] { true, true, false };
-
-    final PgArray pgArray = new PgArray(null, Oid.BIT, booleanArrays.toBinaryRepresentation(null, bools));
-
-    Object arrayObj = pgArray.getArray();
-
-    assertThat(arrayObj, instanceOf(Boolean[].class));
-
-    final Boolean[] actual = (Boolean[]) arrayObj;
-
-    assertEquals(bools.length, actual.length);
-
-    for (int i = 0; i < bools.length; ++i) {
-      assertEquals(Boolean.valueOf(bools[i]), actual[i]);
-    }
-  }
-
-  @Test
-  public void testBooleanToString() throws Exception {
-    final boolean[] bools = new boolean[] { true, true, false };
-
-    final String arrayString = booleanArrays.toArrayString(',', bools);
-
-    assertEquals("{1,1,0}", arrayString);
-
-    final String altArrayString = booleanArrays.toArrayString(';', bools);
-
-    assertEquals("{1;1;0}", altArrayString);
-  }
-
-  @Test
-  public void testStringNotSupportBinary() {
-    PrimitiveArraySupport<String[]> stringArrays = PrimitiveArraySupport.getArraySupport(new String[] {});
-    assertNotNull(stringArrays);
-    assertFalse(stringArrays.supportBinaryRepresentation());
-    try {
-      stringArrays.toBinaryRepresentation(null, new String[] { "1.2" });
-      fail("no sql exception thrown");
-    } catch (SQLFeatureNotSupportedException e) {
-
-    }
-  }
-}
diff --git a/src/test/java/org/postgresql/jdbc/ShortArraysTest.java b/src/test/java/org/postgresql/jdbc/ShortArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..ed2779297fa73594c19531446b1df2c272a3a9b0
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/ShortArraysTest.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class ShortArraysTest extends AbstractArraysTest<short[]> {
+
+  private static final short[][][] shorts = new short[][][] { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
+      { { 13, 14, 15, 16 }, { 17, 18, 19, 20 }, { 21, 22, 23, 24 } } };
+
+  public ShortArraysTest() {
+    super(shorts, true, Oid.INT2_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/ShortObjectArraysTest.java b/src/test/java/org/postgresql/jdbc/ShortObjectArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..856da904752e62363a87e2aeb65c544d9ad34edc
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/ShortObjectArraysTest.java
@@ -0,0 +1,18 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class ShortObjectArraysTest extends AbstractArraysTest<Short[]> {
+
+  private static final Short[][][] shorts = new Short[][][] { { { 1, 2, 3, 4 }, { 5, 6, 7, 8 }, { 9, 10, 11, 12 } },
+      { { 13, 14, 15, 16 }, { 17, 18, null, 20 }, { 21, 22, 23, 24 } } };
+
+  public ShortObjectArraysTest() {
+    super(shorts, true, Oid.INT2_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/jdbc/StringArraysTest.java b/src/test/java/org/postgresql/jdbc/StringArraysTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..d9e131ac73726e4d667f4f2aebe146fac23f11c5
--- /dev/null
+++ b/src/test/java/org/postgresql/jdbc/StringArraysTest.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.jdbc;
+
+import org.postgresql.core.Oid;
+
+public class StringArraysTest extends AbstractArraysTest<String[]> {
+
+  private static final String[][][] strings = new String[][][] {
+      { { "some", "String", "haVE some \u03C0", "another" }, { null, "6L", "7L", "8L" }, //unicode escape for pi character
+          { "asdf", " asdf ", "11L", null } },
+      { { "13L", null, "asasde4wtq", "16L" }, { "17L", "", "19L", "20L" }, { "21L", "22L", "23L", "24L" } } };
+
+  public StringArraysTest() {
+    super(strings, true, Oid.VARCHAR_ARRAY);
+  }
+}
diff --git a/src/test/java/org/postgresql/test/TestUtil.java b/src/test/java/org/postgresql/test/TestUtil.java
index 0ce472c752209a471c0e3425600f05933e465395..f698e5b5d992b08f9afa70e2ee68d042d14db3d5 100644
--- a/src/test/java/org/postgresql/test/TestUtil.java
+++ b/src/test/java/org/postgresql/test/TestUtil.java
@@ -10,8 +10,11 @@ import org.postgresql.core.BaseConnection;
 import org.postgresql.core.ServerVersion;
 import org.postgresql.core.TransactionState;
 import org.postgresql.core.Version;
+import org.postgresql.jdbc.GSSEncMode;
 import org.postgresql.jdbc.PgConnection;
+import org.postgresql.util.PSQLException;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
 import org.junit.Assert;
 
 import java.io.File;
@@ -167,6 +170,13 @@ public class TestUtil {
     return System.getProperty(PGProperty.SSL_PASSWORD.getName());
   }
 
+  /*
+   *  Return the GSSEncMode for the tests
+   */
+  public static GSSEncMode getGSSEncMode() throws PSQLException {
+    return GSSEncMode.of(System.getProperties());
+  }
+
   /*
    * Returns the user for SSPI authentication tests
    */
@@ -296,6 +306,8 @@ public class TestUtil {
   public static Connection openPrivilegedDB() throws SQLException {
     initDriver();
     Properties properties = new Properties();
+
+    PGProperty.GSS_ENC_MODE.set(properties,getGSSEncMode().value);
     properties.setProperty("user", getPrivilegedUser());
     properties.setProperty("password", getPrivilegedPassword());
     return DriverManager.getConnection(getURL(), properties);
@@ -351,13 +363,16 @@ public class TestUtil {
     String hostport = props.getProperty(SERVER_HOST_PORT_PROP, getServer() + ":" + getPort());
     String database = props.getProperty(DATABASE_PROP, getDatabase());
 
+    // Set GSSEncMode for tests
+    PGProperty.GSS_ENC_MODE.set(props,getGSSEncMode().value);
+
     return DriverManager.getConnection(getURL(hostport, database), props);
   }
 
   /*
    * Helper - closes an open connection.
    */
-  public static void closeDB(Connection con) throws SQLException {
+  public static void closeDB(/* @Nullable */ Connection con) throws SQLException {
     if (con != null) {
       con.close();
     }
@@ -813,7 +828,7 @@ public class TestUtil {
   /**
    * Close a Connection and ignore any errors during closing.
    */
-  public static void closeQuietly(Connection conn) {
+  public static void closeQuietly(/* @Nullable */ Connection conn) {
     if (conn != null) {
       try {
         conn.close();
@@ -825,7 +840,7 @@ public class TestUtil {
   /**
    * Close a Statement and ignore any errors during closing.
    */
-  public static void closeQuietly(Statement stmt) {
+  public static void closeQuietly(/* @Nullable */ Statement stmt) {
     if (stmt != null) {
       try {
         stmt.close();
@@ -837,7 +852,7 @@ public class TestUtil {
   /**
    * Close a ResultSet and ignore any errors during closing.
    */
-  public static void closeQuietly(ResultSet rs) {
+  public static void closeQuietly(/* @Nullable */ ResultSet rs) {
     if (rs != null) {
       try {
         rs.close();
diff --git a/src/test/java/org/postgresql/test/jdbc2/ConnectionTest.java b/src/test/java/org/postgresql/test/jdbc2/ConnectionTest.java
index 4f58c9fecbbfebdf579f671841b1676e9caf13df..79540857d2eb71ab137553e71b04e8d23a3ae20e 100644
--- a/src/test/java/org/postgresql/test/jdbc2/ConnectionTest.java
+++ b/src/test/java/org/postgresql/test/jdbc2/ConnectionTest.java
@@ -13,6 +13,8 @@ import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
 
 import org.postgresql.PGProperty;
+import org.postgresql.core.PGStream;
+import org.postgresql.core.QueryExecutor;
 import org.postgresql.jdbc.PgConnection;
 import org.postgresql.test.TestUtil;
 
@@ -20,6 +22,7 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 
+import java.lang.reflect.Field;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
@@ -520,6 +523,27 @@ public class ConnectionTest {
     con.close();
   }
 
+  @Test
+  public void testPGStreamSettings() throws Exception {
+    con = TestUtil.openDB();
+    QueryExecutor queryExecutor = ((PgConnection)con).getQueryExecutor();
+
+    Field f = queryExecutor.getClass().getSuperclass().getDeclaredField("pgStream");
+    f.setAccessible(true);
+    PGStream pgStream = (PGStream)f.get(queryExecutor);
+    pgStream.setNetworkTimeout(1000);
+    pgStream.getSocket().setKeepAlive(true);
+    pgStream.getSocket().setSendBufferSize(8192);
+    pgStream.getSocket().setReceiveBufferSize(2048);
+    PGStream newStream = new PGStream(pgStream, 10);
+    assertEquals(1000, newStream.getSocket().getSoTimeout());
+    assertEquals(2048, newStream.getSocket().getReceiveBufferSize());
+    assertEquals(8192, newStream.getSocket().getSendBufferSize());
+    assertTrue(newStream.getSocket().getKeepAlive());
+
+    TestUtil.closeDB(con);
+  }
+
   private static void assertStringContains(String orig, String toContain) {
     if (!orig.contains(toContain)) {
       fail("expected [" + orig + ']' + "to contain [" + toContain + "].");
diff --git a/src/test/java/org/postgresql/test/jdbc2/CopyLargeFileTest.java b/src/test/java/org/postgresql/test/jdbc2/CopyLargeFileTest.java
index bd07d6d68a1ffba9f2f1da2ba64a676e15c32317..60089eb74f54de7fd5fd8c55fc736d4899f51afc 100644
--- a/src/test/java/org/postgresql/test/jdbc2/CopyLargeFileTest.java
+++ b/src/test/java/org/postgresql/test/jdbc2/CopyLargeFileTest.java
@@ -24,6 +24,7 @@ import java.sql.CallableStatement;
 import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.SQLException;
+import java.util.Random;
 
 /**
  * @author amozhenin on 30.09.2015.
@@ -80,26 +81,51 @@ public class CopyLargeFileTest {
   }
 
   @Test
-  public void testFeedTableSeveralTimesTest() throws Exception {
+  public void testFeedTableSeveralTimesTest() throws Throwable {
     for (int i = 1; i <= FEED_COUNT; i++) {
       feedTableAndCheckTableFeedIsOk(con);
       cleanupTable(con);
     }
   }
 
-  private void feedTableAndCheckTableFeedIsOk(Connection conn) throws Exception {
+  private void feedTableAndCheckTableFeedIsOk(Connection conn) throws Throwable {
+    Long seed = Long.getLong("StrangeInputStream.seed");
+    if (seed == null) {
+      seed = new Random().nextLong();
+    }
     InputStream in = null;
     try {
-      in = new StrangeInputStream(new FileInputStream("target/buffer.txt"));
+      in = new StrangeInputStream(new FileInputStream("target/buffer.txt"), seed);
       long size = copyAPI.copyIn(
           "COPY pgjdbc_issue366_test_data(data_text_id, glossary_text_id, value) FROM STDIN", in);
       assertEquals(BufferGenerator.ROW_COUNT, size);
+    } catch (Throwable t) {
+      String message = "Using seed = " + seed + " for StrangeInputStream. Set -DStrangeInputStream.seed="
+          + seed + " to reproduce the test";
+      //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
+      t.addSuppressed(new Throwable(message) {
+        @Override
+        public synchronized Throwable fillInStackTrace() {
+          return this;
+        }
+      });
+      //#else
+      if (t.getCause() != null) {
+        System.err.println(message);
+      } else {
+        t.initCause(new Throwable(message) {
+          @Override
+          public synchronized Throwable fillInStackTrace() {
+            return this;
+          }
+        });
+      }
+      //#endif
     } finally {
       if (in != null) {
         in.close();
       }
     }
-
   }
 
   private void cleanupTable(Connection conn) throws Exception {
diff --git a/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java b/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
index b4075d6919d1d45f9f6ba31042937e15bd4a7815..afcb83260855b1f9aea3e8eee6f58f79b92b0e90 100644
--- a/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
+++ b/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
@@ -11,13 +11,17 @@ import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
 import static org.junit.Assert.assertTrue;
 
+import org.postgresql.PGProperty;
 import org.postgresql.core.ServerVersion;
 import org.postgresql.test.TestUtil;
+import org.postgresql.test.jdbc2.BaseTest4.BinaryMode;
 
 import org.junit.After;
 import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
 
 import java.sql.Connection;
 import java.sql.DatabaseMetaData;
@@ -29,21 +33,44 @@ import java.sql.Statement;
 import java.sql.Types;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.List;
+import java.util.Properties;
 import java.util.Set;
 
 /*
  * TestCase to test the internal functionality of org.postgresql.jdbc2.DatabaseMetaData
  *
  */
+@RunWith(Parameterized.class)
 public class DatabaseMetaDataTest {
   private Connection con;
+  private final BinaryMode binaryMode;
+
+  public DatabaseMetaDataTest(BinaryMode binaryMode) {
+    this.binaryMode = binaryMode;
+  }
+
+  @Parameterized.Parameters(name = "binary = {0}")
+  public static Iterable<Object[]> data() {
+    Collection<Object[]> ids = new ArrayList<Object[]>();
+    for (BinaryMode binaryMode : BinaryMode.values()) {
+      ids.add(new Object[]{binaryMode});
+    }
+    return ids;
+  }
 
   @Before
   public void setUp() throws Exception {
-    con = TestUtil.openDB();
+    if (binaryMode == BinaryMode.FORCE) {
+      final Properties props = new Properties();
+      PGProperty.PREPARE_THRESHOLD.set(props, -1);
+      con = TestUtil.openDB(props);
+    } else {
+      con = TestUtil.openDB();
+    }
     TestUtil.createTable(con, "metadatatest",
         "id int4, name text, updated timestamptz, colour text, quest text");
     TestUtil.dropSequence(con, "sercoltest_b_seq");
diff --git a/src/test/java/org/postgresql/test/jdbc2/DriverTest.java b/src/test/java/org/postgresql/test/jdbc2/DriverTest.java
index 37758988a80a5f97a7e651b7840b81dd4eab833e..56e68c4cdcfdc82f0a0d5e3a524db703928f6651 100644
--- a/src/test/java/org/postgresql/test/jdbc2/DriverTest.java
+++ b/src/test/java/org/postgresql/test/jdbc2/DriverTest.java
@@ -15,9 +15,9 @@ import static org.junit.Assert.fail;
 import org.postgresql.Driver;
 import org.postgresql.PGProperty;
 import org.postgresql.test.TestUtil;
+import org.postgresql.util.LogWriterHandler;
 import org.postgresql.util.NullOutputStream;
 import org.postgresql.util.URLCoder;
-import org.postgresql.util.WriterHandler;
 
 import org.junit.Test;
 
@@ -237,7 +237,7 @@ public class DriverTest {
 
       Logger logger = Logger.getLogger("org.postgresql");
       Handler[] handlers = logger.getHandlers();
-      assertTrue(handlers[0] instanceof WriterHandler );
+      assertTrue(handlers[0] instanceof LogWriterHandler );
       con.close();
     } finally {
       DriverManager.setLogWriter(prevLog);
@@ -266,7 +266,7 @@ public class DriverTest {
 
       Logger logger = Logger.getLogger("org.postgresql");
       Handler []handlers = logger.getHandlers();
-      assertTrue( handlers[0] instanceof WriterHandler );
+      assertTrue( handlers[0] instanceof LogWriterHandler );
       con.close();
     } finally {
       DriverManager.setLogStream(null);
diff --git a/src/test/java/org/postgresql/test/jdbc2/NotifyTest.java b/src/test/java/org/postgresql/test/jdbc2/NotifyTest.java
index faaa82bd45d25107993989e331eab9715e14defc..f9fb2a36d62ed771fb6c556736b9909a8cafbe04 100644
--- a/src/test/java/org/postgresql/test/jdbc2/NotifyTest.java
+++ b/src/test/java/org/postgresql/test/jdbc2/NotifyTest.java
@@ -7,7 +7,6 @@ package org.postgresql.test.jdbc2;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
 
 import org.postgresql.PGConnection;
 import org.postgresql.PGNotification;
@@ -22,6 +21,7 @@ import org.junit.Test;
 import java.sql.Connection;
 import java.sql.SQLException;
 import java.sql.Statement;
+import java.util.Arrays;
 
 public class NotifyTest {
   private Connection conn;
@@ -113,7 +113,8 @@ public class NotifyTest {
     PGNotification[] notifications = conn.unwrap(PGConnection.class).getNotifications(500);
     long endMillis = System.currentTimeMillis();
     long runtime = endMillis - startMillis;
-    assertNull("There have been notifications, although none have been expected.",notifications);
+    assertEquals("There have been notifications, although none have been expected.",
+        "[]", Arrays.asList(notifications).toString());
     Assert.assertTrue("We didn't wait long enough! runtime=" + runtime, runtime > 450);
 
     stmt.close();
diff --git a/src/test/java/org/postgresql/test/jdbc2/SocketTimeoutTest.java b/src/test/java/org/postgresql/test/jdbc2/SocketTimeoutTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..f02600e54a22d01600afbde61961a55bde22effb
--- /dev/null
+++ b/src/test/java/org/postgresql/test/jdbc2/SocketTimeoutTest.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2020, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.jdbc2;
+
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import org.postgresql.PGProperty;
+import org.postgresql.test.TestUtil;
+
+import org.junit.Test;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.Properties;
+
+public class SocketTimeoutTest {
+
+  @Test
+  public void testSocketTimeoutEnforcement() throws Exception {
+    Properties properties = new Properties();
+    PGProperty.SOCKET_TIMEOUT.set(properties, 1);
+
+    Connection conn = TestUtil.openDB(properties);
+    Statement stmt = null;
+    try {
+      stmt = conn.createStatement();
+      stmt.execute("SELECT pg_sleep(2)");
+      fail("Connection with socketTimeout did not throw expected exception");
+    } catch (SQLException e) {
+      assertTrue(conn.isClosed());
+    } finally {
+      TestUtil.closeQuietly(stmt);
+      TestUtil.closeDB(conn);
+    }
+  }
+}
diff --git a/src/test/java/org/postgresql/test/jdbc2/UpdateableResultTest.java b/src/test/java/org/postgresql/test/jdbc2/UpdateableResultTest.java
index eda4dc8dadd18f5db067edb7be0af7ef6438b81f..181ca82513229382b49ce57476f34771add80385 100644
--- a/src/test/java/org/postgresql/test/jdbc2/UpdateableResultTest.java
+++ b/src/test/java/org/postgresql/test/jdbc2/UpdateableResultTest.java
@@ -584,9 +584,13 @@ public class UpdateableResultTest extends BaseTest4 {
     ResultSet rs = st.executeQuery("select id1,val from multicol");
     try {
       rs.moveToInsertRow();
+      fail("Move to insert row succeeded. It should not");
     } catch (SQLException sqle) {
       // Ensure we're reporting that the RS is not updatable.
       assertEquals("24000", sqle.getSQLState());
+    } finally {
+      TestUtil.closeQuietly(rs);
+      TestUtil.closeQuietly(st);
     }
   }
 
@@ -607,7 +611,6 @@ public class UpdateableResultTest extends BaseTest4 {
     assertTrue(rs.next());
     assertEquals("newval", rs.getString("val"));
     rs.close();
-
     st.close();
   }
 
@@ -669,4 +672,16 @@ public class UpdateableResultTest extends BaseTest4 {
     //rs.refreshRow(); //fetches the value stored
     assertTrue(rs.getBoolean("b"));
   }
+
+  @Test
+  public void testOidUpdatable() throws Exception {
+    Statement st = con.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,
+        ResultSet.CONCUR_UPDATABLE);
+    ResultSet rs = st.executeQuery("SELECT oid,* FROM pg_class WHERE relname = 'pg_class'");
+    assertTrue(rs.next());
+    assertTrue(rs.first());
+    rs.updateString("relname", "pg_class");
+    rs.close();
+    st.close();
+  }
 }
diff --git a/src/test/java/org/postgresql/test/jdbc4/ArrayTest.java b/src/test/java/org/postgresql/test/jdbc4/ArrayTest.java
index aa7ebd3b399bf3e3a28668754d38d8bbc48aa5f1..4803d70ed25523b75da27a16387928189f180151 100644
--- a/src/test/java/org/postgresql/test/jdbc4/ArrayTest.java
+++ b/src/test/java/org/postgresql/test/jdbc4/ArrayTest.java
@@ -11,6 +11,7 @@ import org.postgresql.jdbc.PgConnection;
 import org.postgresql.jdbc.PreferQueryMode;
 import org.postgresql.test.TestUtil;
 import org.postgresql.test.jdbc2.BaseTest4;
+import org.postgresql.test.util.RegexMatcher;
 import org.postgresql.util.PGobject;
 import org.postgresql.util.PGtokenizer;
 
@@ -112,6 +113,66 @@ public class ArrayTest extends BaseTest4 {
     Assert.assertEquals(2, out[2].intValue());
   }
 
+  @Test
+  public void testCreateArrayOfBytes() throws SQLException {
+
+    PreparedStatement pstmt = conn.prepareStatement("SELECT ?::bytea[]");
+    final byte[][] in = new byte[][] { { 0x01, (byte) 0xFF, (byte) 0x12 }, {}, { (byte) 0xAC, (byte) 0xE4 }, null };
+    final Array createdArray = conn.createArrayOf("bytea", in);
+
+    byte[][] inCopy = (byte[][]) createdArray.getArray();
+
+    Assert.assertEquals(4, inCopy.length);
+
+    Assert.assertArrayEquals(in[0], inCopy[0]);
+    Assert.assertArrayEquals(in[1], inCopy[1]);
+    Assert.assertArrayEquals(in[2], inCopy[2]);
+    Assert.assertArrayEquals(in[3], inCopy[3]);
+    Assert.assertNull(inCopy[3]);
+
+    pstmt.setArray(1, createdArray);
+
+    ResultSet rs = pstmt.executeQuery();
+    Assert.assertTrue(rs.next());
+    Array arr = rs.getArray(1);
+
+    byte[][] out = (byte[][]) arr.getArray();
+
+    Assert.assertEquals(4, out.length);
+
+    Assert.assertArrayEquals(in[0], out[0]);
+    Assert.assertArrayEquals(in[1], out[1]);
+    Assert.assertArrayEquals(in[2], out[2]);
+    Assert.assertArrayEquals(in[3], out[3]);
+    Assert.assertNull(out[3]);
+  }
+
+  @Test
+  public void testCreateArrayOfBytesFromString() throws SQLException {
+
+    assumeMinimumServerVersion("support for bytea[] as string requires hex string support from 9.0",
+        ServerVersion.v9_0);
+
+    PreparedStatement pstmt = conn.prepareStatement("SELECT ?::bytea[]");
+    final byte[][] in = new byte[][] { { 0x01, (byte) 0xFF, (byte) 0x12 }, {}, { (byte) 0xAC, (byte) 0xE4 }, null };
+
+    pstmt.setString(1, "{\"\\\\x01ff12\",\"\\\\x\",\"\\\\xace4\",NULL}");
+
+    ResultSet rs = pstmt.executeQuery();
+    Assert.assertTrue(rs.next());
+    Array arr = rs.getArray(1);
+
+    byte[][] out = (byte[][]) arr.getArray();
+
+    Assert.assertEquals(4, out.length);
+
+    Assert.assertArrayEquals(in[0], out[0]);
+    Assert.assertArrayEquals(in[1], out[1]);
+    Assert.assertArrayEquals(in[2], out[2]);
+    Assert.assertArrayEquals(in[3], out[3]);
+    Assert.assertNull(out[3]);
+  }
+
   @Test
   public void testCreateArrayOfSmallInt() throws SQLException {
     PreparedStatement pstmt = conn.prepareStatement("SELECT ?::smallint[]");
@@ -332,12 +393,12 @@ public class ArrayTest extends BaseTest4 {
 
   @Test
   public void testSetObjectFromJavaArray() throws SQLException {
-    String[] strArray = new String[]{"a", "b", "c"};
+    String[] strArray = new String[] { "a", "b", "c" };
     Object[] objCopy = Arrays.copyOf(strArray, strArray.length, Object[].class);
 
     PreparedStatement pstmt = conn.prepareStatement("INSERT INTO arrtest(strarr) VALUES (?)");
 
-    //cannot handle generic Object[]
+    // cannot handle generic Object[]
     try {
       pstmt.setObject(1, objCopy, Types.ARRAY);
       pstmt.executeUpdate();
@@ -541,13 +602,16 @@ public class ArrayTest extends BaseTest4 {
         Array doubles = rs.getArray(1);
         String actual = doubles.toString();
         if (actual != null) {
+          // if a binary array is provided, the string representation looks like [0:1][0:1]={{1,2},{3,4}}
+          int idx = actual.indexOf('=');
+          if (idx > 0) {
+            actual = actual.substring(idx + 1);
+          }
           // Remove all double quotes. They do not make a difference here.
           actual = actual.replaceAll("\"", "");
-          // Replace X.0 with just X
-          actual = actual.replaceAll("\\.0+([^0-9])", "$1");
         }
-        Assert.assertEquals("Array.toString should use square braces",
-            "{3.5,-4.5,NULL,77}", actual);
+        //the string format may vary based on how data stored
+        Assert.assertThat(actual, RegexMatcher.matchesPattern("\\{3\\.5,-4\\.5,NULL,77(.0)?\\}"));
       }
 
     } finally {
@@ -589,10 +653,34 @@ public class ArrayTest extends BaseTest4 {
     rs.next();
     Array resArray = rs.getArray(1);
     String stringValue = resArray.toString();
+    // if a binary array is provided, the string representation looks like [0:1][0:1]={{1,2},{3,4}}
+    int idx = stringValue.indexOf('=');
+    if (idx > 0) {
+      stringValue = stringValue.substring(idx + 1);
+    }
     // Both {{"1","2"},{"3","4"}} and {{1,2},{3,4}} are the same array representation
     stringValue = stringValue.replaceAll("\"", "");
     Assert.assertEquals("{{1,2},{3,4}}", stringValue);
     TestUtil.closeQuietly(rs);
     TestUtil.closeQuietly(ps);
   }
+
+  @Test
+  public void insertAndQueryMultiDimArray() throws SQLException {
+    Array arr = con.createArrayOf("int4", new int[][] { { 1, 2 }, { 3, 4 } });
+    PreparedStatement insertPs = con.prepareStatement("INSERT INTO arrtest(intarr2) VALUES (?)");
+    insertPs.setArray(1, arr);
+    insertPs.execute();
+    insertPs.close();
+
+    PreparedStatement selectPs = con.prepareStatement("SELECT intarr2 FROM arrtest");
+    ResultSet rs = selectPs.executeQuery();
+    rs.next();
+
+    Array array = rs.getArray(1);
+    Integer[][] secondRowValues = (Integer[][]) array.getArray(2, 1);
+
+    Assert.assertEquals(3, secondRowValues[0][0].intValue());
+    Assert.assertEquals(4, secondRowValues[0][1].intValue());
+  }
 }
diff --git a/src/test/java/org/postgresql/test/jdbc4/CopyUtfTest.java b/src/test/java/org/postgresql/test/jdbc4/CopyUtfTest.java
deleted file mode 100644
index 37641d38871eec803b7e4473e023aad689c43abf..0000000000000000000000000000000000000000
--- a/src/test/java/org/postgresql/test/jdbc4/CopyUtfTest.java
+++ /dev/null
@@ -1,83 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- *
- */
-
-package org.postgresql.test.jdbc4;
-
-import org.postgresql.PGConnection;
-import org.postgresql.copy.CopyManager;
-import org.postgresql.test.TestUtil;
-import org.postgresql.test.jdbc2.BaseTest4;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.FileInputStream;
-import java.io.InputStream;
-import java.io.OutputStreamWriter;
-import java.nio.charset.StandardCharsets;
-import java.rmi.ServerException;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-
-public class CopyUtfTest extends BaseTest4 {
-  @Override
-  @Before
-  public void setUp() throws Exception {
-    super.setUp();
-    TestUtil.createTable(con, "testtable", "text1 character varying, text2 character varying,text3 character varying");
-  }
-
-  @Override
-  @After
-  public void tearDown() throws SQLException {
-    super.tearDown();
-  }
-
-  @Test
-  public void copyTest() throws Exception {
-    PGConnection pgcon = con.unwrap(PGConnection.class);
-    CopyManager mgr = pgcon.getCopyAPI();
-    try {
-
-    ByteArrayOutputStream baos = new ByteArrayOutputStream();
-    try (OutputStreamWriter wr = new OutputStreamWriter(baos, StandardCharsets.UTF_8)) {
-      // Write 10 rows
-      for (int i = 0; i < 10; i++) {
-        wr.write("INR,字仮名交じり文,3255104BTK1\n");
-      }
-    }
-
-    try (InputStream fis = new FileInputStream("file.csv")) {
-      mgr.copyIn(sql, fis);
-    }
-
-    String sql = "COPY testtable  FROM stdin delimiter ','  NULL AS 'null' ENCODING 'UTF8'";
-    mgr.copyIn(sql, new ByteArrayInputStream(baos.toByteArray()));
-      ResultSet rs = con.createStatement().executeQuery("select count(*) from testtable");
-      rs.next();
-      System.out.println("rs.getInt(1) = " + rs.getInt(1));
-
-
-    } catch (SQLException ex) {
-      throw new ServerException("Error while copying data in Postgres DB:", ex);
-    }
-  }
-}
diff --git a/src/test/java/org/postgresql/test/ssl/SslTest.java b/src/test/java/org/postgresql/test/ssl/SslTest.java
index a1af199c5ca51bcec03dcc672e1898e29ca7fc60..718a64a5c505e51aa4288e272e0455b21aa91dc7 100644
--- a/src/test/java/org/postgresql/test/ssl/SslTest.java
+++ b/src/test/java/org/postgresql/test/ssl/SslTest.java
@@ -6,11 +6,13 @@
 package org.postgresql.test.ssl;
 
 import org.postgresql.PGProperty;
+import org.postgresql.jdbc.GSSEncMode;
 import org.postgresql.jdbc.SslMode;
 import org.postgresql.test.TestUtil;
 import org.postgresql.test.jdbc2.BaseTest4;
 import org.postgresql.util.PSQLState;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
 import org.junit.Assert;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -113,7 +115,10 @@ public class SslTest extends BaseTest4 {
   @Parameterized.Parameter(5)
   public String certdir;
 
-  @Parameterized.Parameters(name = "host={0}, db={1} sslMode={2}, cCert={3}, cRootCert={4}")
+  @Parameterized.Parameter(6)
+  public GSSEncMode gssEncMode;
+
+  @Parameterized.Parameters(name = "host={0}, db={1} sslMode={2}, cCert={3}, cRootCert={4}, gssEncMode={6}")
   public static Iterable<Object[]> data() {
     Properties prop = TestUtil.loadPropertyFiles("ssltest.properties");
     String enableSslTests = prop.getProperty("enable_ssl_tests");
@@ -147,9 +152,11 @@ public class SslTest extends BaseTest4 {
                 // DB would reject SSL connection, so it makes no sense to test cases like verify-full
                 continue;
               }
-              tests.add(
-                  new Object[]{hostname, database, sslMode, clientCertificate, rootCertificate,
-                      certdir});
+              for (GSSEncMode gssEncMode : GSSEncMode.values()) {
+                tests.add(
+                    new Object[]{hostname, database, sslMode, clientCertificate, rootCertificate,
+                        certdir, gssEncMode});
+              }
             }
           }
         }
@@ -159,12 +166,17 @@ public class SslTest extends BaseTest4 {
     return tests;
   }
 
+  private static boolean contains(/* @Nullable */ String value, String substring) {
+    return value != null && value.contains(substring);
+  }
+
   @Override
   protected void updateProperties(Properties props) {
     super.updateProperties(props);
     props.put(TestUtil.SERVER_HOST_PORT_PROP, host.value + ":" + TestUtil.getPort());
     props.put(TestUtil.DATABASE_PROP, db.toString());
     PGProperty.SSL_MODE.set(props, sslmode.value);
+    PGProperty.GSS_ENC_MODE.set(props, gssEncMode.value);
     if (clientCertificate == ClientCertificate.EMPTY) {
       PGProperty.SSL_CERT.set(props, "");
       PGProperty.SSL_KEY.set(props, "");
@@ -211,7 +223,7 @@ public class SslTest extends BaseTest4 {
         PSQLState.INVALID_AUTHORIZATION_SPECIFICATION.getState(), e.getSQLState());
   }
 
-  private void checkErrorCodes(SQLException e) {
+  private void checkErrorCodes(/* @Nullable */ SQLException e) {
     if (e != null && e.getCause() instanceof FileNotFoundException
         && clientRootCertificate != ClientRootCertificate.EMPTY) {
       Assert.fail("FileNotFoundException => it looks like a configuration failure");
@@ -334,7 +346,7 @@ public class SslTest extends BaseTest4 {
     throw firstError;
   }
 
-  private List<AssertionError> addError(List<AssertionError> errors, AssertionError ae) {
+  private List<AssertionError> addError(/* @Nullable */ List<AssertionError> errors, AssertionError ae) {
     if (errors == null) {
       errors = new ArrayList<AssertionError>();
     }
@@ -393,9 +405,10 @@ public class SslTest extends BaseTest4 {
     }
     Assert.assertEquals(caseName + " ==> CONNECTION_FAILURE is expected",
         PSQLState.CONNECTION_FAILURE.getState(), e.getSQLState());
-    if (!e.getMessage().contains("PgjdbcHostnameVerifier")) {
+    String message = e.getMessage();
+    if (message == null || !message.contains("PgjdbcHostnameVerifier")) {
       Assert.fail(caseName + " ==> message should contain"
-          + " 'PgjdbcHostnameVerifier'. Actual message is " + e.getMessage());
+          + " 'PgjdbcHostnameVerifier'. Actual message is " + message);
     }
     return true;
   }
@@ -441,7 +454,7 @@ public class SslTest extends BaseTest4 {
       Assert.fail(caseName + " ==> exception should be caused by SocketException(broken pipe)"
           + " or SSLHandshakeException. No exceptions of such kind are present in the getCause chain");
     }
-    if (brokenPipe != null && !brokenPipe.getMessage().contains("Broken pipe")) {
+    if (brokenPipe != null && !contains(brokenPipe.getMessage(), "Broken pipe")) {
       Assert.fail(
           caseName + " ==> server should have terminated the connection (broken pipe expected)"
               + ", actual exception was " + brokenPipe.getMessage());
@@ -449,7 +462,8 @@ public class SslTest extends BaseTest4 {
 
     if (handshakeException != null) {
       final String handshakeMessage = handshakeException.getMessage();
-      if (!handshakeMessage.contains("unknown_ca") && !handshakeMessage.contains("decrypt_error")) {
+      if (!contains(handshakeMessage, "unknown_ca")
+          && !contains(handshakeMessage, "decrypt_error")) {
         Assert.fail(
             caseName
                 + " ==> server should have terminated the connection (expected 'unknown_ca' or 'decrypt_error')"
@@ -459,7 +473,8 @@ public class SslTest extends BaseTest4 {
     return true;
   }
 
-  private static <T extends Throwable> T findCause(Throwable t, Class<T> cause) {
+  private static </* @Nullable */ T extends Throwable> T findCause(/* @Nullable */ Throwable t,
+      Class<T> cause) {
     while (t != null) {
       if (cause.isInstance(t)) {
         return (T) t;
diff --git a/src/test/java/org/postgresql/test/util/RegexMatcher.java b/src/test/java/org/postgresql/test/util/RegexMatcher.java
new file mode 100644
index 0000000000000000000000000000000000000000..382eb4cb2db3d0f3db278d1375b4aa08f0548b92
--- /dev/null
+++ b/src/test/java/org/postgresql/test/util/RegexMatcher.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.test.util;
+
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.TypeSafeMatcher;
+
+import java.util.regex.Pattern;
+
+/**
+ * Provides a matcher for String objects which does a regex comparison.
+ */
+public final class RegexMatcher extends TypeSafeMatcher<String> {
+
+  private final Pattern pattern;
+
+  /**
+   * @param pattern
+   *          The pattern to match items on.
+   */
+  private RegexMatcher(Pattern pattern) {
+    this.pattern = pattern;
+  }
+
+  public static Matcher<String> matchesPattern(String pattern) {
+    return new RegexMatcher(Pattern.compile(pattern));
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  public void describeTo(Description description) {
+    description.appendText("matches regex=" + pattern.toString());
+  }
+
+  /**
+   * {@inheritDoc}
+   */
+  @Override
+  protected boolean matchesSafely(String item) {
+    return pattern.matcher(item).matches();
+  }
+
+}
diff --git a/src/test/java/org/postgresql/test/util/StrangeInputStream.java b/src/test/java/org/postgresql/test/util/StrangeInputStream.java
index c6ffba7dda75263e25abd87afb2151ed5601942a..36dcd0b4749ec0a811d2c259e20086bfd4fd0b6b 100644
--- a/src/test/java/org/postgresql/test/util/StrangeInputStream.java
+++ b/src/test/java/org/postgresql/test/util/StrangeInputStream.java
@@ -16,15 +16,10 @@ import java.util.Random;
  * array. This allows to stress test {@link org.postgresql.copy.CopyManager} or other consumers.
  */
 public class StrangeInputStream extends FilterInputStream {
-  private Random rand; // generator of fun events
+  private final Random rand = new Random(); // generator of fun events
 
-  public StrangeInputStream(InputStream is) throws FileNotFoundException {
+  public StrangeInputStream(InputStream is, long seed) throws FileNotFoundException {
     super(is);
-    rand = new Random();
-    long seed = Long.getLong("StrangeInputStream.seed", System.currentTimeMillis());
-    System.out
-        .println("Using seed = " + seed + " for StrangeInputStream. Set -DStrangeInputStream.seed="
-            + seed + " to reproduce the test");
     rand.setSeed(seed);
   }
 
diff --git a/src/test/java/org/postgresql/test/xa/XADataSourceTest.java b/src/test/java/org/postgresql/test/xa/XADataSourceTest.java
index 2717981cf1d4d4980af4ae2ecdd4b2ae8454bf0f..8a28c3b81d45ca1e7ccedc3a75601572c3758534 100644
--- a/src/test/java/org/postgresql/test/xa/XADataSourceTest.java
+++ b/src/test/java/org/postgresql/test/xa/XADataSourceTest.java
@@ -15,6 +15,7 @@ import org.postgresql.test.TestUtil;
 import org.postgresql.test.jdbc2.optional.BaseDataSourceTest;
 import org.postgresql.xa.PGXADataSource;
 
+// import org.checkerframework.checker.nullness.qual.Nullable;
 import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
@@ -151,7 +152,7 @@ public class XADataSourceTest {
     }
 
     @Override
-    public boolean equals(Object o) {
+    public boolean equals(/* @Nullable */ Object o) {
       if (!(o instanceof Xid)) {
         return false;
       }
diff --git a/src/test/java/org/postgresql/util/ReaderInputStreamTest.java b/src/test/java/org/postgresql/util/ReaderInputStreamTest.java
index d05a625ab54ceee3462f6db219f6fbb58564b7fa..ec335831c62a72fedb4f111655dedc071d991ddb 100644
--- a/src/test/java/org/postgresql/util/ReaderInputStreamTest.java
+++ b/src/test/java/org/postgresql/util/ReaderInputStreamTest.java
@@ -31,6 +31,7 @@ public class ReaderInputStreamTest {
   private static final char TRAILING_SURROGATE = 0xdf0e;
 
   @Test(expected = IllegalArgumentException.class)
+  @SuppressWarnings("nullability")
   public void NullReaderTest() {
     new ReaderInputStream(null);
   }
@@ -123,6 +124,7 @@ public class ReaderInputStreamTest {
   }
 
   @Test(expected = NullPointerException.class)
+  @SuppressWarnings("nullness")
   public void nullArrayReadTest() throws IOException {
     Reader reader = new StringReader("abc");
     InputStream is = new ReaderInputStream(reader);
diff --git a/src/test/java/org/postgresql/util/TestLogHandler.java b/src/test/java/org/postgresql/util/TestLogHandler.java
index ecb41cc537a601f7ecd5f342915c1d20903933c1..5fa77338ac3b1ed8a8719cc58c853bdf54f450ae 100644
--- a/src/test/java/org/postgresql/util/TestLogHandler.java
+++ b/src/test/java/org/postgresql/util/TestLogHandler.java
@@ -30,7 +30,8 @@ public class TestLogHandler extends Handler {
   public List<LogRecord> getRecordsMatching(Pattern messagePattern) {
     ArrayList<LogRecord> matches = new ArrayList<LogRecord>();
     for (LogRecord r: this.records) {
-      if (messagePattern.matcher(r.getMessage()).find()) {
+      String message = r.getMessage();
+      if (message != null && messagePattern.matcher(message).find()) {
         matches.add(r);
       }
     }