Repository: pgjdbc/pgjdbc
Pull request #1284 (2018-08-01 16:42:53)

test: fix wrong secondaryPort2 build property
The `secondaryServerPort2` is misspelled in the `build.properties` file and do not match with the one used in `MultiHostsConnectionTest.java`. Also make `secondaryServer1` and `secondaryPort1` consistent in the naming.

davecramer: I'm confused. Where is this spelled wrong ?
jorsol: Probably it's not clear in the PR, but the property file have `secondaryServerPort2` and the MultiHostsConnectionTest looks for `secondaryPort2`, since I was testing a docker setup of patroni, the port of all instances was 5432, but the test suite fails with port 5434, which is the default.
codecov-io: # [Codecov](https://codecov.io/gh/pgjdbc/pgjdbc/pull/1284?src=pr&el=h1) Report
> Merging [#1284](https://codecov.io/gh/pgjdbc/pgjdbc/pull/1284?src=pr&el=desc) into [master](https://codecov.io/gh/pgjdbc/pgjdbc/commit/b5c19af627c8650410495ad8e3f2ee85e687e3c1?src=pr&el=desc) will **decrease** coverage by `<.01%`.
> The diff coverage is `n/a`.



```diff
@@             Coverage Diff              @@
##             master    #1284      +/-   ##
============================================
- Coverage     68.76%   68.76%   -0.01%     
+ Complexity     3855     3854       -1     
============================================
  Files           174      174              
  Lines         16029    16029              
  Branches       2612     2612              
============================================
- Hits          11023    11022       -1     
  Misses         3775     3775              
- Partials       1231     1232       +1
```

vlsi: LGTM

Patch:
diff --git a/.github/issue_template.md b/.github/issue_template.md
index a892ae3d..52f04c02 100644
--- a/.github/issue_template.md
+++ b/.github/issue_template.md
@@ -8,11 +8,13 @@
 **Describe the issue**
 A clear and concise description of what the issue is.
 
-**Java Version**
+**Driver Version?** 
 
-**OS Version**
+**Java Version?**
 
-**PostgreSQL Version**
+**OS Version?**
+
+**PostgreSQL Version?**
 
 **To Reproduce**
 Steps to reproduce the behaviour:
diff --git a/.travis/travis_create_slaves.sh b/.travis/travis_create_slaves.sh
index 2eba2450..f772a0ad 100755
--- a/.travis/travis_create_slaves.sh
+++ b/.travis/travis_create_slaves.sh
@@ -3,7 +3,7 @@ set -x -e
 
 if [ -z "$PG_VERSION" ]
 then
-    echo "env PG_VERSION not define";
+    echo "env PG_VERSION not defined";
 elif [ "x${PG_VERSION}" = "xHEAD" ]
 then
     PG_CTL="/usr/local/pgsql/bin/pg_ctl"
@@ -19,11 +19,8 @@ sudo mkdir -p ${PG_SLAVE1_DATADIR}
 sudo chmod 700 ${PG_SLAVE1_DATADIR}
 sudo chown -R postgres:postgres ${PG_SLAVE1_DATADIR}
 
-sudo su postgres -c "$PG_BASEBACKUP -Upostgres -D ${PG_SLAVE1_DATADIR} -X stream"
+sudo su postgres -c "$PG_BASEBACKUP -Upostgres -D ${PG_SLAVE1_DATADIR} -X stream -R"
 
-sudo su postgres -c "echo standby_mode = \'on\' >${PG_SLAVE1_DATADIR}/recovery.conf"
-sudo su postgres -c "echo primary_conninfo = \'host=localhost port=5432 user=test password=test\' >>${PG_SLAVE1_DATADIR}/recovery.conf"
-sudo su postgres -c "echo recovery_target_timeline = \'latest\' >>${PG_SLAVE1_DATADIR}/recovery.conf"
 
 if [[ "x${PG_VERSION}" != "xHEAD" ]]
 then
@@ -38,6 +35,7 @@ then
     sudo sed -i -e "/^[ \t]*hba_file.*/d" ${PG_SLAVE1_DATADIR}/postgresql.conf
     sudo sed -i -e "/^[ \t]*ident_file.*/d" ${PG_SLAVE1_DATADIR}/postgresql.conf
     sudo sed -i -e "s/^#\?hot_standby.*/hot_standby = on/g" ${PG_SLAVE1_DATADIR}/postgresql.conf
+
 fi
 
 #Start Slave 1
@@ -50,25 +48,24 @@ sudo mkdir -p ${PG_SLAVE2_DATADIR}
 sudo chmod 700 ${PG_SLAVE2_DATADIR}
 sudo chown -R postgres:postgres ${PG_SLAVE2_DATADIR}
 
-sudo su postgres -c "$PG_BASEBACKUP -Upostgres -D ${PG_SLAVE2_DATADIR} -X stream"
+sudo su postgres -c "$PG_BASEBACKUP -Upostgres -D ${PG_SLAVE2_DATADIR} -X stream -R"
 
-sudo su postgres -c "echo standby_mode = \'on\' >${PG_SLAVE2_DATADIR}/recovery.conf"
-sudo su postgres -c "echo primary_conninfo = \'host=localhost port=5432 user=test password=test\' >>${PG_SLAVE2_DATADIR}/recovery.conf"
-sudo su postgres -c "echo recovery_target_timeline = \'latest\' >>${PG_SLAVE2_DATADIR}/recovery.conf"
 
 if [[ "x${PG_VERSION}" != "xHEAD" ]]
 then
+
     sudo su postgres -c "echo 'local all all trust' > ${PG_SLAVE2_DATADIR}/pg_hba.conf"
     sudo su postgres -c "echo 'host all all 127.0.0.1/32 trust' >> ${PG_SLAVE2_DATADIR}/pg_hba.conf"
     sudo su postgres -c "echo 'host all all ::1/128 trust' >> ${PG_SLAVE2_DATADIR}/pg_hba.conf"
 
     sudo su postgres -c "touch ${PG_SLAVE2_DATADIR}/pg_ident.conf"
-
-    sudo su postgres -c "cp -f ${PG_DATADIR}/postgresql.conf ${PG_SLAVE2_DATADIR}/postgresql.conf"
+        sudo su postgres -c "cp -f ${PG_DATADIR}/postgresql.conf ${PG_SLAVE2_DATADIR}/postgresql.conf"
     sudo sed -i -e "/^[ \t]*data_directory.*/d" ${PG_SLAVE2_DATADIR}/postgresql.conf
     sudo sed -i -e "/^[ \t]*hba_file.*/d" ${PG_SLAVE2_DATADIR}/postgresql.conf
     sudo sed -i -e "/^[ \t]*ident_file.*/d" ${PG_SLAVE2_DATADIR}/postgresql.conf
     sudo sed -i -e "s/^#\?hot_standby.*/hot_standby = on/g" ${PG_SLAVE2_DATADIR}/postgresql.conf
+
+
 fi
 
 #Start Slave 2
diff --git a/CHANGELOG.md b/CHANGELOG.md
index be40476b..7d65a822 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,8 +9,12 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/).
 ### Added
 
 ### Fixed
+- Fixed async copy performance regression [PR 1314](https://github.com/pgjdbc/pgjdbc/pull/1314)
 
 ## [42.2.5] (2018-08-27)
+### Known issues
+- 1ms per async copy call [issue 1312](https://github.com/pgjdbc/pgjdbc/issues/1312)
+
 ### Changed
 - `ssl=true` implies `sslmode=verify-full`, that is it requires valid server certificate [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e)
 
@@ -46,7 +50,7 @@ thrown to caller to be dealt with so no need to log at this verbosity by pgjdbc
 
 ### Fixed
 - getString for PGObject-based types returned "null" string instead of null [PR 1154](https://github.com/pgjdbc/pgjdbc/pull/1154)
-- Field metadata cache can be disabled via databaseMetadataCacheFields=0 [PR 1052](https://github.com/pgjdbc/pgjdbc/pull/1152)
+- Field metadata cache can be disabled via databaseMetadataCacheFields=0 [PR 1052](https://github.com/pgjdbc/pgjdbc/pull/1052)
 - Properly encode special symbols in passwords in BaseDataSource [PR 1201](https://github.com/pgjdbc/pgjdbc/pull/1201)
 - Adjust date, hour, minute, second when rounding nanosecond part of a timestamp [PR 1212](https://github.com/pgjdbc/pgjdbc/pull/1212)
 - perf: reduce memory allocations in query cache [PR 1227](https://github.com/pgjdbc/pgjdbc/pull/1227)
diff --git a/build.properties b/build.properties
index 548d2aaf..a62afc7f 100644
--- a/build.properties
+++ b/build.properties
@@ -5,10 +5,10 @@
 
 server=localhost
 port=5432
-secondaryServer=localhost
-secondaryPort=5433
+secondaryServer1=localhost
+secondaryPort1=5433
 secondaryServer2=localhost
-secondaryServerPort2=5434
+secondaryPort2=5434
 database=test
 username=test
 password=test
diff --git a/docs/_posts/2018-07-12-42.2.3-release.md b/docs/_posts/2018-07-12-42.2.3-release.md
index 91431d1a..3701f67f 100644
--- a/docs/_posts/2018-07-12-42.2.3-release.md
+++ b/docs/_posts/2018-07-12-42.2.3-release.md
@@ -18,7 +18,7 @@ version: 42.2.3
 
 ### Fixed
 - getString for PGObject-based types returned "null" string instead of null [PR 1154](https://github.com/pgjdbc/pgjdbc/pull/1154)
-- Field metadata cache can be disabled via databaseMetadataCacheFields=0 [PR 1052](https://github.com/pgjdbc/pgjdbc/pull/1152)
+- Field metadata cache can be disabled via databaseMetadataCacheFields=0 [PR 1052](https://github.com/pgjdbc/pgjdbc/pull/1052)
 - Properly encode special symbols in passwords in BaseDataSource [PR 1201](https://github.com/pgjdbc/pgjdbc/pull/1201)
 - Adjust date, hour, minute, second when rounding nanosecond part of a timestamp [PR 1212](https://github.com/pgjdbc/pgjdbc/pull/1212)
 - perf: reduce memory allocations in query cache [PR 1227](https://github.com/pgjdbc/pgjdbc/pull/1227)
diff --git a/docs/_posts/2018-08-27-42.2.5-release.md b/docs/_posts/2018-08-27-42.2.5-release.md
index 5467ee10..72773881 100644
--- a/docs/_posts/2018-08-27-42.2.5-release.md
+++ b/docs/_posts/2018-08-27-42.2.5-release.md
@@ -7,6 +7,9 @@ version: 42.2.5
 ---
 **Notable changes**
 
+### Known issues
+- 1ms per async copy call [issue 1312](https://github.com/pgjdbc/pgjdbc/issues/1312)
+
 ### Changed
 - `ssl=true` implies `sslmode=verify-full`, that is it requires valid server certificate [cdeeaca4](https://github.com/pgjdbc/pgjdbc/commit/cdeeaca47dc3bc6f727c79a582c9e4123099526e)
 
diff --git a/pgjdbc/src/main/java/org/postgresql/copy/PGCopyInputStream.java b/pgjdbc/src/main/java/org/postgresql/copy/PGCopyInputStream.java
index 4b4060cc..5bfc100b 100644
--- a/pgjdbc/src/main/java/org/postgresql/copy/PGCopyInputStream.java
+++ b/pgjdbc/src/main/java/org/postgresql/copy/PGCopyInputStream.java
@@ -76,7 +76,7 @@ public class PGCopyInputStream extends InputStream implements CopyOut {
 
   public int read() throws IOException {
     checkClosed();
-    return gotBuf() ? buf[at++] : -1;
+    return gotBuf() ? (buf[at++] & 0xFF)  : -1;
   }
 
   public int read(byte[] buf) throws IOException {
diff --git a/pgjdbc/src/main/java/org/postgresql/core/PGStream.java b/pgjdbc/src/main/java/org/postgresql/core/PGStream.java
index 6f0f3a2b..907b3cf6 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/PGStream.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/PGStream.java
@@ -44,6 +44,11 @@ public class PGStream implements Closeable, Flushable {
   private OutputStream pg_output;
   private byte[] streamBuffer;
 
+  private long nextStreamAvailableCheckTime;
+  // This is a workaround for SSL sockets: sslInputStream.available() might return 0
+  // so we perform "1ms reads" once in a while
+  private int minStreamAvailableCheckDelay = 1000;
+
   private Encoding encoding;
   private Writer encodingWriter;
 
@@ -114,6 +119,12 @@ public class PGStream implements Closeable, Flushable {
       return true;
     }
     // In certain cases, available returns 0, yet there are bytes
+    long now = System.currentTimeMillis();
+    if (now < nextStreamAvailableCheckTime && minStreamAvailableCheckDelay != 0) {
+      // Do not use ".peek" too often
+      return false;
+    }
+    nextStreamAvailableCheckTime = now + minStreamAvailableCheckDelay;
     int soTimeout = getNetworkTimeout();
     setNetworkTimeout(1);
     try {
@@ -125,6 +136,10 @@ public class PGStream implements Closeable, Flushable {
     }
   }
 
+  public void setMinStreamAvailableCheckDelay(int delay) {
+    this.minStreamAvailableCheckDelay = delay;
+  }
+
   /**
    * 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).
diff --git a/pgjdbc/src/main/java/org/postgresql/core/ServerVersion.java b/pgjdbc/src/main/java/org/postgresql/core/ServerVersion.java
index 5eae078d..9348ba13 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/ServerVersion.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/ServerVersion.java
@@ -24,7 +24,9 @@ public enum ServerVersion implements Version {
   v9_4("9.4.0"),
   v9_5("9.5.0"),
   v9_6("9.6.0"),
-  v10("10")
+  v10("10"),
+  v11("11"),
+  v12("12")
   ;
 
   private final int version;
diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/CopyDualImpl.java b/pgjdbc/src/main/java/org/postgresql/core/v3/CopyDualImpl.java
index 8ff5cf88..076321f7 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/v3/CopyDualImpl.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/v3/CopyDualImpl.java
@@ -28,11 +28,7 @@ public class CopyDualImpl extends CopyOperationImpl implements CopyDual {
   }
 
   public byte[] readFromCopy() throws SQLException {
-    if (received.isEmpty()) {
-      queryExecutor.readFromCopy(this, true);
-    }
-
-    return received.poll();
+    return readFromCopy(true);
   }
 
   @Override
diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java b/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java
index 1a1ddeea..20283973 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/v3/SimpleParameterList.java
@@ -188,10 +188,16 @@ class SimpleParameterList implements V3ParameterList {
 
         case Oid.FLOAT4:
           float f = ByteConverter.float4((byte[]) paramValues[index], 0);
+          if (Float.isNaN(f)) {
+            return "'NaN'::real";
+          }
           return Float.toString(f);
 
         case Oid.FLOAT8:
           double d = ByteConverter.float8((byte[]) paramValues[index], 0);
+          if (Double.isNaN(d)) {
+            return "'NaN'::double precision";
+          }
           return Double.toString(d);
 
         case Oid.UUID:
diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java b/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
index 38d1afd8..11b4defb 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3PGReplicationStream.java
@@ -35,9 +35,9 @@ public class V3PGReplicationStream implements PGReplicationStream {
   /**
    * Last receive LSN + payload size.
    */
-  private LogSequenceNumber lastReceiveLSN = LogSequenceNumber.INVALID_LSN;
-  private LogSequenceNumber lastAppliedLSN = LogSequenceNumber.INVALID_LSN;
-  private LogSequenceNumber lastFlushedLSN = LogSequenceNumber.INVALID_LSN;
+  private volatile LogSequenceNumber lastReceiveLSN = LogSequenceNumber.INVALID_LSN;
+  private volatile LogSequenceNumber lastAppliedLSN = LogSequenceNumber.INVALID_LSN;
+  private volatile LogSequenceNumber lastFlushedLSN = LogSequenceNumber.INVALID_LSN;
 
   /**
    * @param copyDual         bidirectional copy protocol
diff --git a/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java b/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java
index 68237473..c06fdf28 100644
--- a/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java
+++ b/pgjdbc/src/main/java/org/postgresql/core/v3/replication/V3ReplicationProtocol.java
@@ -129,6 +129,8 @@ public class V3ReplicationProtocol implements ReplicationProtocol {
       }
 
       pgStream.getSocket().setSoTimeout(minimalTimeOut);
+      // Use blocking 1ms reads for `available()` checks
+      pgStream.setMinStreamAvailableCheckDelay(0);
     } catch (IOException ioe) {
       throw new PSQLException(GT.tr("The connection attempt failed."),
           PSQLState.CONNECTION_UNABLE_TO_CONNECT, ioe);
diff --git a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
index 268d936f..7f78f404 100644
--- a/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
+++ b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
@@ -1109,8 +1109,13 @@ public abstract class BaseDataSource implements CommonDataSource, Referenceable
 
     Properties p = org.postgresql.Driver.parseURL(url, null);
 
+    if (p == null ) {
+      throw new IllegalArgumentException("URL invalid " + url);
+    }
     for (PGProperty property : PGProperty.values()) {
-      setProperty(property, property.get(p));
+      if (!this.properties.containsKey(property.getName())) {
+        setProperty(property, property.get(p));
+      }
     }
   }
 
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
index 4abab216..f8f53d1b 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java
@@ -1066,7 +1066,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     f[13] = new Field("COLUMN_DEF", Oid.VARCHAR);
     f[14] = new Field("SQL_DATA_TYPE", Oid.INT4);
     f[15] = new Field("SQL_DATETIME_SUB", Oid.INT4);
-    f[16] = new Field("CHAR_OCTECT_LENGTH", Oid.INT4);
+    f[16] = new Field("CHAR_OCTET_LENGTH", Oid.INT4);
     f[17] = new Field("ORDINAL_POSITION", Oid.INT4);
     f[18] = new Field("IS_NULLABLE", Oid.VARCHAR);
     f[19] = new Field("SPECIFIC_NAME", Oid.VARCHAR);
@@ -1284,7 +1284,9 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
              + " END "
              + " ELSE NULL "
              + " END "
-             + " AS TABLE_TYPE, d.description AS REMARKS "
+             + " AS TABLE_TYPE, d.description AS REMARKS, "
+             + " '' as TYPE_CAT, '' as TYPE_SCHEM, '' as TYPE_NAME, "
+             + "'' AS SELF_REFERENCING_COL_NAME, '' AS REF_GENERATION "
              + " FROM pg_catalog.pg_namespace n, pg_catalog.pg_class c "
              + " LEFT JOIN pg_catalog.pg_description d ON (c.oid = d.objoid AND d.objsubid = 0) "
              + " LEFT JOIN pg_catalog.pg_class dc ON (d.classoid=dc.oid AND dc.relname='pg_class') "
@@ -1451,7 +1453,7 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
   public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern,
                               String columnNamePattern) throws SQLException {
 
-    int numberOfFields = 23; // JDBC4
+    int numberOfFields = 24; // JDBC4
     List<byte[][]> v = new ArrayList<byte[][]>(); // The new ResultSet tuple stuff
     Field[] f = new Field[numberOfFields]; // The field descriptors for the new ResultSet
 
@@ -1473,11 +1475,12 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     f[15] = new Field("CHAR_OCTET_LENGTH", Oid.VARCHAR);
     f[16] = new Field("ORDINAL_POSITION", Oid.INT4);
     f[17] = new Field("IS_NULLABLE", Oid.VARCHAR);
-    f[18] = new Field("SCOPE_CATLOG", Oid.VARCHAR);
+    f[18] = new Field("SCOPE_CATALOG", Oid.VARCHAR);
     f[19] = new Field("SCOPE_SCHEMA", Oid.VARCHAR);
     f[20] = new Field("SCOPE_TABLE", Oid.VARCHAR);
     f[21] = new Field("SOURCE_DATA_TYPE", Oid.INT2);
     f[22] = new Field("IS_AUTOINCREMENT", Oid.VARCHAR);
+    f[23] = new Field( "IS_GENERATED", Oid.VARCHAR);
 
     String sql;
     // a.attnum isn't decremented when preceding columns are dropped,
@@ -1619,6 +1622,8 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
         autoinc = "YES";
       }
       tuple[22] = connection.encodeString(autoinc);
+      // there is no way to tell if the value was actually autogenerated.
+      tuple[23] = connection.encodeString("");
 
       v.add(tuple);
     }
@@ -2224,7 +2229,9 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
     String sql;
     sql = "SELECT t.typname,t.oid FROM pg_catalog.pg_type t"
           + " JOIN pg_catalog.pg_namespace n ON (t.typnamespace = n.oid) "
-          + " WHERE n.nspname  != 'pg_toast'";
+          + " WHERE n.nspname  != 'pg_toast'"
+          + " AND "
+          + " (t.typrelid = 0 OR (SELECT c.relkind = 'c' FROM pg_catalog.pg_class c WHERE c.oid = t.typrelid))";
 
     Statement stmt = connection.createStatement();
     ResultSet rs = stmt.executeQuery(sql);
@@ -2648,7 +2655,200 @@ public class PgDatabaseMetaData implements DatabaseMetaData {
   public ResultSet getFunctionColumns(String catalog, String schemaPattern,
       String functionNamePattern, String columnNamePattern)
       throws SQLException {
-    return getProcedureColumns(catalog, schemaPattern, functionNamePattern, columnNamePattern);
+    int columns = 17;
+
+    Field[] f = new Field[columns];
+    List<byte[][]> v = new ArrayList<byte[][]>();
+
+    f[0] = new Field("FUNCTION_CAT", Oid.VARCHAR);
+    f[1] = new Field("FUNCTION_SCHEM", Oid.VARCHAR);
+    f[2] = new Field("FUNCTION_NAME", Oid.VARCHAR);
+    f[3] = new Field("COLUMN_NAME", Oid.VARCHAR);
+    f[4] = new Field("COLUMN_TYPE", Oid.INT2);
+    f[5] = new Field("DATA_TYPE", Oid.INT2);
+    f[6] = new Field("TYPE_NAME", Oid.VARCHAR);
+    f[7] = new Field("PRECISION", Oid.INT2);
+    f[8] = new Field("LENGTH", Oid.INT4);
+    f[9] = new Field("SCALE", Oid.INT2);
+    f[10] = new Field("RADIX", Oid.INT2);
+    f[11] = new Field("NULLABLE", Oid.INT2);
+    f[12] = new Field("REMARKS", Oid.VARCHAR);
+    f[13] = new Field("CHAR_OCTET_LENGTH", Oid.INT4);
+    f[14] = new Field("ORDINAL_POSITION", Oid.INT4);
+    f[15] = new Field("IS_NULLABLE", Oid.VARCHAR);
+    f[16] = new Field("SPECIFIC_NAME", Oid.VARCHAR);
+
+    String sql;
+    sql = "SELECT n.nspname,p.proname,p.prorettype,p.proargtypes, t.typtype,t.typrelid, "
+        + " p.proargnames, p.proargmodes, p.proallargtypes, p.oid "
+        + " FROM pg_catalog.pg_proc p, pg_catalog.pg_namespace n, pg_catalog.pg_type t "
+        + " WHERE p.pronamespace=n.oid AND p.prorettype=t.oid ";
+    if (schemaPattern != null && !schemaPattern.isEmpty()) {
+      sql += " AND n.nspname LIKE " + escapeQuotes(schemaPattern);
+    }
+    if (functionNamePattern != null && !functionNamePattern.isEmpty()) {
+      sql += " AND p.proname LIKE " + escapeQuotes(functionNamePattern);
+    }
+    sql += " ORDER BY n.nspname, p.proname, p.oid::text ";
+
+    byte[] isnullableUnknown = new byte[0];
+
+    Statement stmt = connection.createStatement();
+    ResultSet rs = stmt.executeQuery(sql);
+    while (rs.next()) {
+      byte[] schema = rs.getBytes("nspname");
+      byte[] functionName = rs.getBytes("proname");
+      byte[] specificName =
+          connection.encodeString(rs.getString("proname") + "_" + rs.getString("oid"));
+      int returnType = (int) rs.getLong("prorettype");
+      String returnTypeType = rs.getString("typtype");
+      int returnTypeRelid = (int) rs.getLong("typrelid");
+
+      String strArgTypes = rs.getString("proargtypes");
+      StringTokenizer st = new StringTokenizer(strArgTypes);
+      List<Long> argTypes = new ArrayList<Long>();
+      while (st.hasMoreTokens()) {
+        argTypes.add(Long.valueOf(st.nextToken()));
+      }
+
+      String[] argNames = null;
+      Array argNamesArray = rs.getArray("proargnames");
+      if (argNamesArray != null) {
+        argNames = (String[]) argNamesArray.getArray();
+      }
+
+      String[] argModes = null;
+      Array argModesArray = rs.getArray("proargmodes");
+      if (argModesArray != null) {
+        argModes = (String[]) argModesArray.getArray();
+      }
+
+      int numArgs = argTypes.size();
+
+      Long[] allArgTypes = null;
+      Array allArgTypesArray = rs.getArray("proallargtypes");
+      if (allArgTypesArray != null) {
+        allArgTypes = (Long[]) allArgTypesArray.getArray();
+        numArgs = allArgTypes.length;
+      }
+
+      // 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][];
+        tuple[0] = null;
+        tuple[1] = schema;
+        tuple[2] = functionName;
+        tuple[3] = connection.encodeString("returnValue");
+        tuple[4] = connection
+            .encodeString(Integer.toString(java.sql.DatabaseMetaData.functionReturn));
+        tuple[5] = connection
+            .encodeString(Integer.toString(connection.getTypeInfo().getSQLType(returnType)));
+        tuple[6] = connection.encodeString(connection.getTypeInfo().getPGType(returnType));
+        tuple[7] = null;
+        tuple[8] = null;
+        tuple[9] = null;
+        tuple[10] = null;
+        tuple[11] = connection
+            .encodeString(Integer.toString(java.sql.DatabaseMetaData.functionNullableUnknown));
+        tuple[12] = null;
+        tuple[14] = connection.encodeString(Integer.toString(0));
+        tuple[15] = isnullableUnknown;
+        tuple[16] = specificName;
+
+        v.add(tuple);
+      }
+
+      // Add a row for each argument.
+      for (int i = 0; i < numArgs; i++) {
+        byte[][] tuple = new byte[columns][];
+        tuple[0] = null;
+        tuple[1] = schema;
+        tuple[2] = functionName;
+
+        if (argNames != null) {
+          tuple[3] = connection.encodeString(argNames[i]);
+        } else {
+          tuple[3] = connection.encodeString("$" + (i + 1));
+        }
+
+        int columnMode = DatabaseMetaData.functionColumnIn;
+        if (argModes != null && argModes[i] != null) {
+          if (argModes[i].equals("o")) {
+            columnMode = DatabaseMetaData.functionColumnOut;
+          } else if (argModes[i].equals("b")) {
+            columnMode = DatabaseMetaData.functionColumnInOut;
+          } else if (argModes[i].equals("t")) {
+            columnMode = DatabaseMetaData.functionReturn;
+          }
+        }
+
+        tuple[4] = connection.encodeString(Integer.toString(columnMode));
+
+        int argOid;
+        if (allArgTypes != null) {
+          argOid = allArgTypes[i].intValue();
+        } else {
+          argOid = argTypes.get(i).intValue();
+        }
+
+        tuple[5] =
+            connection.encodeString(Integer.toString(connection.getTypeInfo().getSQLType(argOid)));
+        tuple[6] = connection.encodeString(connection.getTypeInfo().getPGType(argOid));
+        tuple[7] = null;
+        tuple[8] = null;
+        tuple[9] = null;
+        tuple[10] = null;
+        tuple[11] =
+            connection.encodeString(Integer.toString(DatabaseMetaData.functionNullableUnknown));
+        tuple[12] = null;
+        tuple[14] = connection.encodeString(Integer.toString(i + 1));
+        tuple[15] = isnullableUnknown;
+        tuple[16] = specificName;
+
+        v.add(tuple);
+      }
+
+      // if we are returning a multi-column result.
+      if (returnTypeType.equals("c") || (returnTypeType.equals("p") && 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 ";
+        Statement columnstmt = connection.createStatement();
+        ResultSet columnrs = columnstmt.executeQuery(columnsql);
+        while (columnrs.next()) {
+          int columnTypeOid = (int) columnrs.getLong("atttypid");
+          byte[][] tuple = new byte[columns][];
+          tuple[0] = null;
+          tuple[1] = schema;
+          tuple[2] = functionName;
+          tuple[3] = columnrs.getBytes("attname");
+          tuple[4] = connection
+              .encodeString(Integer.toString(java.sql.DatabaseMetaData.functionColumnResult));
+          tuple[5] = connection
+              .encodeString(Integer.toString(connection.getTypeInfo().getSQLType(columnTypeOid)));
+          tuple[6] = connection.encodeString(connection.getTypeInfo().getPGType(columnTypeOid));
+          tuple[7] = null;
+          tuple[8] = null;
+          tuple[9] = null;
+          tuple[10] = null;
+          tuple[11] = connection
+              .encodeString(Integer.toString(java.sql.DatabaseMetaData.functionNullableUnknown));
+          tuple[12] = null;
+          tuple[14] = connection.encodeString(Integer.toString(0));
+          tuple[15] = isnullableUnknown;
+          tuple[16] = specificName;
+
+          v.add(tuple);
+        }
+        columnrs.close();
+        columnstmt.close();
+      }
+    }
+    rs.close();
+    stmt.close();
+
+    return ((BaseStatement) createMetaDataStatement()).createDriverResultSet(f, v);
   }
 
   public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern,
diff --git a/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java b/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
index d4a6c907..fd9964f9 100644
--- a/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
+++ b/pgjdbc/src/main/java/org/postgresql/jdbc/PgResultSet.java
@@ -185,8 +185,8 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
         return getLong(columnIndex);
       case Types.NUMERIC:
       case Types.DECIMAL:
-        return getBigDecimal(columnIndex,
-            (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff));
+        return getNumeric(columnIndex,
+            (field.getMod() == -1) ? -1 : ((field.getMod() - 4) & 0xffff), true);
       case Types.REAL:
         return getFloat(columnIndex);
       case Types.FLOAT:
@@ -2325,6 +2325,10 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
 
   public 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) {
       return null;
@@ -2352,11 +2356,15 @@ public class PgResultSet implements ResultSet, org.postgresql.PGRefCursorResultS
         BigDecimal res = getFastBigDecimal(columnIndex);
         res = scaleBigDecimal(res, scale);
         return res;
-      } catch (NumberFormatException ex) {
+      } catch (NumberFormatException ignore) {
       }
     }
 
-    return toBigDecimal(getFixedString(columnIndex), scale);
+    String stringValue = getFixedString(columnIndex);
+    if (allowNaN && "NaN".equalsIgnoreCase(stringValue)) {
+      return Double.NaN;
+    }
+    return toBigDecimal(stringValue, scale);
   }
 
   /**
diff --git a/pgjdbc/src/main/java/org/postgresql/replication/PGReplicationStream.java b/pgjdbc/src/main/java/org/postgresql/replication/PGReplicationStream.java
index 8714c1af..cdb186ae 100644
--- a/pgjdbc/src/main/java/org/postgresql/replication/PGReplicationStream.java
+++ b/pgjdbc/src/main/java/org/postgresql/replication/PGReplicationStream.java
@@ -12,10 +12,11 @@ import java.nio.ByteBuffer;
 import java.sql.SQLException;
 
 /**
- * Not tread safe replication stream. After complete streaming should be close, for free resource on
- * backend. Periodical status update work only when use {@link PGReplicationStream#read()} method.
- * It means that process wal record should be fast as possible, because during process wal record
- * lead to disconnect by timeout from server.
+ * Not tread safe replication stream (though certain methods can be safely called by different
+ * threads). After complete streaming should be close, for free resource on backend. Periodical
+ * status update work only when use {@link PGReplicationStream#read()} method. It means that
+ * process wal record should be fast as possible, because during process wal record lead to
+ * disconnect by timeout from server.
  */
 public interface PGReplicationStream
     //#if mvn.project.property.postgresql.jdbc.spec >= "JDBC4.1"
@@ -57,7 +58,11 @@ public interface PGReplicationStream
   ByteBuffer readPending() throws SQLException;
 
   /**
-   * Parameter updates by execute {@link PGReplicationStream#read()} method.
+   * <p>Parameter updates by execute {@link PGReplicationStream#read()} method.</p>
+   *
+   * <p>It is safe to call this method in a thread different than the main thread. However, usually this
+   * method is called in the main thread after a successful {@link PGReplicationStream#read()} or
+   * {@link PGReplicationStream#readPending()}, to get the LSN corresponding to the received record.</p>
    *
    * @return not null LSN position that was receive last time via {@link PGReplicationStream#read()}
    *     method
@@ -65,24 +70,31 @@ public interface PGReplicationStream
   LogSequenceNumber getLastReceiveLSN();
 
   /**
-   * Last flushed lsn send in update message to backend. Parameter updates only via {@link
-   * PGReplicationStream#setFlushedLSN(LogSequenceNumber)}
+   * <p>Last flushed lsn send in update message to backend. Parameter updates only via {@link
+   * PGReplicationStream#setFlushedLSN(LogSequenceNumber)}</p>
+   *
+   * <p>It is safe to call this method in a thread different than the main thread.</p>
    *
    * @return not null location of the last WAL flushed to disk in the standby.
    */
   LogSequenceNumber getLastFlushedLSN();
 
   /**
-   * Last applied lsn send in update message to backed. Parameter updates only via {@link
-   * PGReplicationStream#setAppliedLSN(LogSequenceNumber)}
+   * <p>Last applied lsn send in update message to backed. Parameter updates only via {@link
+   * PGReplicationStream#setAppliedLSN(LogSequenceNumber)}</p>
+   *
+   * <p>It is safe to call this method in a thread different than the main thread.</p>
    *
    * @return not null location of the last WAL applied in the standby.
    */
   LogSequenceNumber getLastAppliedLSN();
 
   /**
-   * Set flushed LSN. It parameter will be send to backend on next update status iteration. Flushed
-   * LSN position help backend define which wal can be recycle.
+   * <p>Set flushed LSN. It parameter will be send to backend on next update status iteration. Flushed
+   * LSN position help backend define which wal can be recycle.</p>
+   *
+   * <p>It is safe to call this method in a thread different than the main thread. The updated value
+   * will be sent to the backend in the next status update run.</p>
    *
    * @param flushed not null location of the last WAL flushed to disk in the standby.
    * @see PGReplicationStream#forceUpdateStatus()
@@ -90,8 +102,11 @@ public interface PGReplicationStream
   void setFlushedLSN(LogSequenceNumber flushed);
 
   /**
-   * Parameter used only physical replication and define which lsn already was apply on standby.
-   * Feedback will send to backend on next update status iteration.
+   * <p>Parameter used only physical replication and define which lsn already was apply on standby.
+   * Feedback will send to backend on next update status iteration.</p>
+   *
+   * <p>It is safe to call this method in a thread different than the main thread. The updated value
+   * will be sent to the backend in the next status update run.</p>
    *
    * @param applied not null location of the last WAL applied in the standby.
    * @see PGReplicationStream#forceUpdateStatus()
diff --git a/pgjdbc/src/main/java/org/postgresql/replication/ReplicationSlotInfo.java b/pgjdbc/src/main/java/org/postgresql/replication/ReplicationSlotInfo.java
new file mode 100644
index 00000000..5a8bde42
--- /dev/null
+++ b/pgjdbc/src/main/java/org/postgresql/replication/ReplicationSlotInfo.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2018, PostgreSQL Global Development Group
+ * See the LICENSE file in the project root for more information.
+ */
+
+package org.postgresql.replication;
+
+/**
+ * Information returned on replication slot creation.
+ *
+ * <p>Returned keys of CREATE_REPLICATION_SLOT:
+ * <ol>
+ * <li><b>slot_name</b> String {@code =>} the slot name
+ * <li><b>consistent_point</b> String {@code =>} LSN at which we became consistent
+ * <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>
+ */
+public final class ReplicationSlotInfo {
+
+  private final String slotName;
+  private final ReplicationType replicationType;
+  private final LogSequenceNumber consistentPoint;
+  private final String snapshotName;
+  private final String outputPlugin;
+
+  public ReplicationSlotInfo(String slotName, ReplicationType replicationType,
+      LogSequenceNumber consistentPoint, String snapshotName, String outputPlugin) {
+    this.slotName = slotName;
+    this.replicationType = replicationType;
+    this.consistentPoint = consistentPoint;
+    this.snapshotName = snapshotName;
+    this.outputPlugin = outputPlugin;
+  }
+
+  /**
+   * Replication slot name.
+   *
+   * @return the slot name
+   */
+  public String getSlotName() {
+    return slotName;
+  }
+
+  /**
+   * Replication type of the slot created, might be PHYSICAL or LOGICAL.
+   *
+   * @return ReplicationType, PHYSICAL or LOGICAL
+   */
+  public ReplicationType getReplicationType() {
+    return replicationType;
+  }
+
+  /**
+   * LSN at which we became consistent.
+   *
+   * @return LogSequenceNumber with the consistent_point
+   */
+  public LogSequenceNumber getConsistentPoint() {
+    return consistentPoint;
+  }
+
+  /**
+   * Exported snapshot name at the point of replication slot creation.
+   *
+   * <p>As long as the exporting transaction remains open, other transactions can import its snapshot,
+   * and thereby be guaranteed that they see exactly the same view of the database that the first
+   * transaction sees.
+   *
+   * @return exported snapshot_name (may be <code>null</code>)
+   */
+  public String getSnapshotName() {
+    return snapshotName;
+  }
+
+  /**
+   * Output Plugin used on slot creation.
+   *
+   * @return output_plugin (may be <code>null</code>)
+   */
+  public String getOutputPlugin() {
+    return outputPlugin;
+  }
+
+}
diff --git a/pgjdbc/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java b/pgjdbc/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
index 54f946b4..c55f65b8 100644
--- a/pgjdbc/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
+++ b/pgjdbc/src/main/java/org/postgresql/replication/fluent/AbstractCreateSlotBuilder.java
@@ -5,10 +5,22 @@
 
 package org.postgresql.replication.fluent;
 
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.ServerVersion;
+import org.postgresql.util.GT;
+
+import java.sql.SQLFeatureNotSupportedException;
+
 public abstract class AbstractCreateSlotBuilder<T extends ChainedCommonCreateSlotBuilder<T>>
     implements ChainedCommonCreateSlotBuilder<T> {
 
   protected String slotName;
+  protected boolean temporaryOption = false;
+  protected BaseConnection connection;
+
+  protected AbstractCreateSlotBuilder(BaseConnection connection) {
+    this.connection = connection;
+  }
 
   protected abstract T self();
 
@@ -17,4 +29,17 @@ public abstract class AbstractCreateSlotBuilder<T extends ChainedCommonCreateSlo
     this.slotName = slotName;
     return self();
   }
+
+  @Override
+  public T withTemporaryOption() throws SQLFeatureNotSupportedException {
+
+    if (!connection.haveMinimumServerVersion(ServerVersion.v10)) {
+      throw new SQLFeatureNotSupportedException(
+          GT.tr("Server does not support temporary replication slots")
+      );
+    }
+
+    this.temporaryOption = true;
+    return self();
+  }
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/replication/fluent/ChainedCommonCreateSlotBuilder.java b/pgjdbc/src/main/java/org/postgresql/replication/fluent/ChainedCommonCreateSlotBuilder.java
index 78a4565f..4114befa 100644
--- a/pgjdbc/src/main/java/org/postgresql/replication/fluent/ChainedCommonCreateSlotBuilder.java
+++ b/pgjdbc/src/main/java/org/postgresql/replication/fluent/ChainedCommonCreateSlotBuilder.java
@@ -5,12 +5,15 @@
 
 package org.postgresql.replication.fluent;
 
+import org.postgresql.replication.ReplicationSlotInfo;
+
 import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
 
 /**
  * Fluent interface for specify common parameters for create Logical and Physical replication slot.
  */
-public interface ChainedCommonCreateSlotBuilder<T extends ChainedCommonCreateSlotBuilder<T>>  {
+public interface ChainedCommonCreateSlotBuilder<T extends ChainedCommonCreateSlotBuilder<T>> {
 
   /**
    * Replication slots provide an automated way to ensure that the master does not remove WAL
@@ -22,9 +25,22 @@ public interface ChainedCommonCreateSlotBuilder<T extends ChainedCommonCreateSlo
    */
   T withSlotName(String slotName);
 
+  /**
+   * <p>Temporary slots are not saved to disk and are automatically dropped on error or when
+   * the session has finished.</p>
+   *
+   * <p>This feature is only supported by PostgreSQL versions &gt;= 10.</p>
+   *
+   * @return T a slot builder
+   * @throws SQLFeatureNotSupportedException thrown if PostgreSQL version is less than 10.
+   */
+  T withTemporaryOption() throws SQLFeatureNotSupportedException;
+
   /**
    * Create slot with specified parameters in database.
+   *
+   * @return ReplicationSlotInfo with the information of the created slot.
    * @throws SQLException on error
    */
-  void make() throws SQLException;
+  ReplicationSlotInfo make() throws SQLException;
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java b/pgjdbc/src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java
index 0369976c..27004c82 100644
--- a/pgjdbc/src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java
+++ b/pgjdbc/src/main/java/org/postgresql/replication/fluent/logical/LogicalCreateSlotBuilder.java
@@ -6,19 +6,23 @@
 package org.postgresql.replication.fluent.logical;
 
 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 java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 
 public class LogicalCreateSlotBuilder
     extends AbstractCreateSlotBuilder<ChainedLogicalCreateSlotBuilder>
     implements ChainedLogicalCreateSlotBuilder {
+
   private String outputPlugin;
-  private BaseConnection connection;
 
   public LogicalCreateSlotBuilder(BaseConnection connection) {
-    this.connection = connection;
+    super(connection);
   }
 
   @Override
@@ -33,7 +37,7 @@ public class LogicalCreateSlotBuilder
   }
 
   @Override
-  public void make() throws SQLException {
+  public ReplicationSlotInfo make() throws SQLException {
     if (outputPlugin == null || outputPlugin.isEmpty()) {
       throw new IllegalArgumentException(
           "OutputPlugin required parameter for logical replication slot");
@@ -44,10 +48,30 @@ public class LogicalCreateSlotBuilder
     }
 
     Statement statement = connection.createStatement();
+    ResultSet result = null;
+    ReplicationSlotInfo slotInfo = null;
     try {
-      statement.execute(String.format("CREATE_REPLICATION_SLOT %s LOGICAL %s", slotName, outputPlugin));
+      statement.execute(String.format(
+          "CREATE_REPLICATION_SLOT %s %s LOGICAL %s",
+          slotName,
+          temporaryOption ? "TEMPORARY" : "",
+          outputPlugin
+      ));
+      result = statement.getResultSet();
+      if (result != null && result.next()) {
+        slotInfo = new ReplicationSlotInfo(
+            result.getString("slot_name"),
+            ReplicationType.LOGICAL,
+            LogSequenceNumber.valueOf(result.getString("consistent_point")),
+            result.getString("snapshot_name"),
+            result.getString("output_plugin"));
+      }
     } finally {
+      if (result != null) {
+        result.close();
+      }
       statement.close();
     }
+    return slotInfo;
   }
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java b/pgjdbc/src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java
index 9d99fa4e..45df2a16 100644
--- a/pgjdbc/src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java
+++ b/pgjdbc/src/main/java/org/postgresql/replication/fluent/physical/PhysicalCreateSlotBuilder.java
@@ -6,18 +6,21 @@
 package org.postgresql.replication.fluent.physical;
 
 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 java.sql.ResultSet;
 import java.sql.SQLException;
 import java.sql.Statement;
 
 public class PhysicalCreateSlotBuilder
     extends AbstractCreateSlotBuilder<ChainedPhysicalCreateSlotBuilder>
     implements ChainedPhysicalCreateSlotBuilder {
-  private BaseConnection connection;
 
   public PhysicalCreateSlotBuilder(BaseConnection connection) {
-    this.connection = connection;
+    super(connection);
   }
 
   @Override
@@ -26,16 +29,35 @@ public class PhysicalCreateSlotBuilder
   }
 
   @Override
-  public void make() throws SQLException {
+  public ReplicationSlotInfo make() throws SQLException {
     if (slotName == null || slotName.isEmpty()) {
       throw new IllegalArgumentException("Replication slotName can't be null");
     }
 
     Statement statement = connection.createStatement();
+    ResultSet result = null;
+    ReplicationSlotInfo slotInfo = null;
     try {
-      statement.execute(String.format("CREATE_REPLICATION_SLOT %s PHYSICAL", slotName));
+      statement.execute(String.format(
+          "CREATE_REPLICATION_SLOT %s %s PHYSICAL",
+          slotName,
+          temporaryOption ? "TEMPORARY" : ""
+      ));
+      result = statement.getResultSet();
+      if (result != null && result.next()) {
+        slotInfo = new ReplicationSlotInfo(
+            result.getString("slot_name"),
+            ReplicationType.PHYSICAL,
+            LogSequenceNumber.valueOf(result.getString("consistent_point")),
+            result.getString("snapshot_name"),
+            result.getString("output_plugin"));
+      }
     } finally {
+      if (result != null) {
+        result.close();
+      }
       statement.close();
     }
+    return slotInfo;
   }
 }
diff --git a/pgjdbc/src/main/java/org/postgresql/util/ReaderInputStream.java b/pgjdbc/src/main/java/org/postgresql/util/ReaderInputStream.java
index a313c300..1d6372b2 100644
--- a/pgjdbc/src/main/java/org/postgresql/util/ReaderInputStream.java
+++ b/pgjdbc/src/main/java/org/postgresql/util/ReaderInputStream.java
@@ -113,7 +113,7 @@ public class ReaderInputStream extends InputStream {
     while (res != -1) {
       res = read(oneByte);
       if (res > 0) {
-        return oneByte[0];
+        return (oneByte[0] & 0xFF);
       }
     }
     return -1;


Test patch:
diff --git a/pgjdbc/src/test/java/org/postgresql/replication/ReplicationSlotTest.java b/pgjdbc/src/test/java/org/postgresql/replication/ReplicationSlotTest.java
index 2e9b1d73..5a5aab24 100644
--- a/pgjdbc/src/test/java/org/postgresql/replication/ReplicationSlotTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/replication/ReplicationSlotTest.java
@@ -6,10 +6,17 @@
 package org.postgresql.replication;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
 import static org.junit.Assert.fail;
+import static org.junit.Assume.assumeFalse;
+import static org.junit.Assume.assumeTrue;
 
 import org.postgresql.PGConnection;
 import org.postgresql.PGProperty;
+import org.postgresql.core.BaseConnection;
+import org.postgresql.core.ServerVersion;
 import org.postgresql.test.TestUtil;
 import org.postgresql.test.util.rules.ServerVersionRule;
 import org.postgresql.test.util.rules.annotation.HaveMinimalServerVersion;
@@ -23,6 +30,7 @@ import org.junit.Test;
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.SQLFeatureNotSupportedException;
 import java.sql.Statement;
 import java.util.Properties;
 
@@ -80,7 +88,67 @@ public class ReplicationSlotTest {
 
     boolean result = isPhysicalSlotExists(slotName);
 
-    assertThat(result, CoreMatchers.equalTo(true));
+    assertThat("Slot should exist", result, CoreMatchers.equalTo(true));
+
+    result = isSlotTemporary(slotName);
+
+    assertThat("Slot should not be temporary by default", result, CoreMatchers.equalTo(false));
+  }
+
+  @Test
+  public void testCreateTemporaryPhysicalSlotPg10AndHigher()
+      throws SQLException {
+    assumeTrue(TestUtil.haveMinimumServerVersion(replConnection, ServerVersion.v10));
+
+    BaseConnection baseConnection = (BaseConnection) replConnection;
+
+    String slotName = "pgjdbc_test_create_temporary_physical_replication_slot_pg_10_or_higher";
+
+    try {
+
+      baseConnection
+          .getReplicationAPI()
+          .createReplicationSlot()
+          .physical()
+          .withSlotName(slotName)
+          .withTemporaryOption()
+          .make();
+
+    } catch (SQLFeatureNotSupportedException e) {
+
+      fail("PostgreSQL >= 10 should support temporary replication slots");
+
+    }
+
+    boolean result = isSlotTemporary(slotName);
+
+    assertThat("Slot is not temporary", result, CoreMatchers.equalTo(true));
+  }
+
+  @Test
+  public void testCreateTemporaryPhysicalSlotPgLowerThan10()
+      throws SQLException {
+    assumeFalse(TestUtil.haveMinimumServerVersion(replConnection, ServerVersion.v10));
+
+    BaseConnection baseConnection = (BaseConnection) replConnection;
+
+    String slotName = "pgjdbc_test_create_temporary_physical_replication_slot_pg_lower_than_10";
+
+    try {
+
+      baseConnection
+          .getReplicationAPI()
+          .createReplicationSlot()
+          .physical()
+          .withSlotName(slotName)
+          .withTemporaryOption()
+          .make();
+
+      fail("PostgreSQL < 10 does not support temporary replication slots");
+
+    } catch (SQLFeatureNotSupportedException e) {
+      // success
+    }
   }
 
   @Test
@@ -151,7 +219,110 @@ public class ReplicationSlotTest {
 
     boolean result = isLogicalSlotExists(slotName);
 
-    assertThat(result, CoreMatchers.equalTo(true));
+    assertThat("Slot should exist", result, CoreMatchers.equalTo(true));
+
+    result = isSlotTemporary(slotName);
+
+    assertThat("Slot should not be temporary by default", result, CoreMatchers.equalTo(false));
+  }
+
+  @Test
+  public void testCreateLogicalSlotReturnedInfo() throws Exception {
+    PGConnection pgConnection = (PGConnection) replConnection;
+
+    slotName = "pgjdbc_test_create_logical_replication_slot_info";
+
+    ReplicationSlotInfo info = pgConnection
+        .getReplicationAPI()
+        .createReplicationSlot()
+        .logical()
+        .withSlotName(slotName)
+        .withOutputPlugin("test_decoding")
+        .make();
+
+    assertEquals(slotName, info.getSlotName());
+    assertEquals(ReplicationType.LOGICAL, info.getReplicationType());
+    assertNotNull(info.getConsistentPoint());
+    assertNotNull(info.getSnapshotName());
+    assertEquals("test_decoding", info.getOutputPlugin());
+  }
+
+  @Test
+  public void testCreatePhysicalSlotReturnedInfo() throws Exception {
+    PGConnection pgConnection = (PGConnection) replConnection;
+
+    slotName = "pgjdbc_test_create_physical_replication_slot_info";
+
+    ReplicationSlotInfo info = pgConnection
+        .getReplicationAPI()
+        .createReplicationSlot()
+        .physical()
+        .withSlotName(slotName)
+        .make();
+
+    assertEquals(slotName, info.getSlotName());
+    assertEquals(ReplicationType.PHYSICAL, info.getReplicationType());
+    assertNotNull(info.getConsistentPoint());
+    assertNull(info.getSnapshotName());
+    assertNull(info.getOutputPlugin());
+  }
+
+  @Test
+  public void testCreateTemporaryLogicalSlotPg10AndHigher()
+      throws SQLException {
+    assumeTrue(TestUtil.haveMinimumServerVersion(replConnection, ServerVersion.v10));
+
+    BaseConnection baseConnection = (BaseConnection) replConnection;
+
+    String slotName = "pgjdbc_test_create_temporary_logical_replication_slot_pg_10_or_higher";
+
+    try {
+
+      baseConnection
+          .getReplicationAPI()
+          .createReplicationSlot()
+          .logical()
+          .withSlotName(slotName)
+          .withOutputPlugin("test_decoding")
+          .withTemporaryOption()
+          .make();
+
+    } catch (SQLFeatureNotSupportedException e) {
+
+      fail("PostgreSQL >= 10 should support temporary replication slots");
+
+    }
+
+    boolean result = isSlotTemporary(slotName);
+
+    assertThat("Slot is not temporary", result, CoreMatchers.equalTo(true));
+  }
+
+  @Test
+  public void testCreateTemporaryLogicalSlotPgLowerThan10()
+      throws SQLException {
+    assumeFalse(TestUtil.haveMinimumServerVersion(replConnection, ServerVersion.v10));
+
+    BaseConnection baseConnection = (BaseConnection) replConnection;
+
+    String slotName = "pgjdbc_test_create_temporary_logical_replication_slot_pg_lower_than_10";
+
+    try {
+
+      baseConnection
+          .getReplicationAPI()
+          .createReplicationSlot()
+          .logical()
+          .withSlotName(slotName)
+          .withOutputPlugin("test_decoding")
+          .withTemporaryOption()
+          .make();
+
+      fail("PostgreSQL < 10 does not support temporary replication slots");
+
+    } catch (SQLFeatureNotSupportedException e) {
+      // success
+    }
   }
 
   @Test
@@ -205,6 +376,23 @@ public class ReplicationSlotTest {
     return result;
   }
 
+  private boolean isSlotTemporary(String slotName) throws SQLException {
+    if (!TestUtil.haveMinimumServerVersion(sqlConnection, ServerVersion.v10)) {
+      return false;
+    }
+
+    boolean result;
+
+    Statement st = sqlConnection.createStatement();
+    ResultSet resultSet = st.executeQuery(
+            "select 1 from pg_replication_slots where slot_name = '" + slotName
+                    + "' and temporary = true");
+    result = resultSet.next();
+    resultSet.close();
+    st.close();
+    return result;
+  }
+
   private void dropReplicationSlot() throws Exception {
     if (slotName != null) {
       TestUtil.dropReplicationSlot(sqlConnection, slotName);
diff --git a/pgjdbc/src/test/java/org/postgresql/test/TestUtil.java b/pgjdbc/src/test/java/org/postgresql/test/TestUtil.java
index 789736a6..b300749c 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/TestUtil.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/TestUtil.java
@@ -389,19 +389,11 @@ public class TestUtil {
     }
   }
 
-  /*
-   * Helper - creates a test table for use by a test
-   */
-  public static void createTable(Connection con, String table, String columns) throws SQLException {
-    // by default we don't request oids.
-    createTable(con, table, columns, false);
-  }
 
   /*
    * Helper - creates a test table for use by a test
    */
-  public static void createTable(Connection con, String table, String columns, boolean withOids)
-      throws SQLException {
+  public static void createTable(Connection con, String table, String columns) throws SQLException {
     Statement st = con.createStatement();
     try {
       // Drop the table
@@ -410,10 +402,6 @@ public class TestUtil {
       // Now create the table
       String sql = "CREATE TABLE " + table + " (" + columns + ")";
 
-      if (withOids) {
-        sql += " WITH OIDS";
-      }
-
       st.executeUpdate(sql);
     } finally {
       closeQuietly(st);
diff --git a/pgjdbc/src/test/java/org/postgresql/test/hostchooser/MultiHostsConnectionTest.java b/pgjdbc/src/test/java/org/postgresql/test/hostchooser/MultiHostsConnectionTest.java
index 0b945ca6..8c202946 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/hostchooser/MultiHostsConnectionTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/hostchooser/MultiHostsConnectionTest.java
@@ -46,7 +46,7 @@ public class MultiHostsConnectionTest {
   private static final String user = TestUtil.getUser();
   private static final String password = TestUtil.getPassword();
   private static final String master1 = TestUtil.getServer() + ":" + TestUtil.getPort();
-  private static final String secondary1 = getSecondaryServer() + ":" + getSecondaryPort();
+  private static final String secondary1 = getSecondaryServer1() + ":" + getSecondaryPort1();
   private static final String secondary2 = getSecondaryServer2() + ":" + getSecondaryPort2();
   private static final String fake1 = "127.127.217.217:1";
 
@@ -95,7 +95,7 @@ public class MultiHostsConnectionTest {
 
     Properties props = userAndPassword();
 
-    return DriverManager.getConnection(TestUtil.getURL(getSecondaryServer(), getSecondaryPort()), props);
+    return DriverManager.getConnection(TestUtil.getURL(getSecondaryServer1(), getSecondaryPort1()), props);
   }
 
   private static Properties userAndPassword() {
@@ -106,12 +106,12 @@ public class MultiHostsConnectionTest {
     return props;
   }
 
-  private static String getSecondaryServer() {
-    return System.getProperty("secondaryServer", TestUtil.getServer());
+  private static String getSecondaryServer1() {
+    return System.getProperty("secondaryServer1", TestUtil.getServer());
   }
 
-  private static int getSecondaryPort() {
-    return Integer.parseInt(System.getProperty("secondaryPort", String.valueOf(TestUtil.getPort() + 1)));
+  private static int getSecondaryPort1() {
+    return Integer.parseInt(System.getProperty("secondaryPort1", String.valueOf(TestUtil.getPort() + 1)));
   }
 
   private static Connection openSecondaryDB2() throws Exception {
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
index 77d8d72a..2538aabb 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
@@ -23,6 +23,7 @@ import java.sql.Connection;
 import java.sql.DatabaseMetaData;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
 import java.sql.SQLException;
 import java.sql.Statement;
 import java.sql.Types;
@@ -177,8 +178,16 @@ public class DatabaseMetaDataTest {
     assertEquals("metadatatest", tableName);
     String tableType = rs.getString("TABLE_TYPE");
     assertEquals("TABLE", tableType);
+    assertEquals(rs.findColumn("REMARKS"), 5);
+    assertEquals(rs.findColumn("TYPE_CAT"), 6);
+    assertEquals(rs.findColumn("TYPE_SCHEM"),7);
+    assertEquals(rs.findColumn("TYPE_NAME"), 8);
+    assertEquals(rs.findColumn("SELF_REFERENCING_COL_NAME"), 9);
+    assertEquals(rs.findColumn("REF_GENERATION"), 10);
+
     // There should only be one row returned
     assertTrue("getTables() returned too many rows", rs.next() == false);
+
     rs.close();
 
     rs = dbmd.getColumns("", "", "meta%", "%");
@@ -455,9 +464,21 @@ public class DatabaseMetaDataTest {
   @Test
   public void testColumns() throws SQLException {
     // At the moment just test that no exceptions are thrown KJ
+    String [] metadataColumns = {"TABLE_CAT", "TABLE_SCHEM", "TABLE_NAME", "COLUMN_NAME",
+                                 "DATA_TYPE", "TYPE_NAME", "COLUMN_SIZE", "BUFFER_LENGTH",
+                                 "DECIMAL_DIGITS", "NUM_PREC_RADIX", "NULLABLE", "REMARKS",
+                                 "COLUMN_DEF","SQL_DATA_TYPE","SQL_DATETIME_SUB","CHAR_OCTET_LENGTH",
+                                 "ORDINAL_POSITION", "IS_NULLABLE", "SCOPE_CATALOG", "SCOPE_SCHEMA",
+                                 "SCOPE_TABLE", "SOURCE_DATA_TYPE", "IS_AUTOINCREMENT", "IS_GENERATED"};
+
     DatabaseMetaData dbmd = con.getMetaData();
     assertNotNull(dbmd);
     ResultSet rs = dbmd.getColumns(null, null, "pg_class", null);
+    if ( rs.next() ) {
+      for (int i = 0; i < metadataColumns.length; i++) {
+        assertEquals(i + 1,  rs.findColumn(metadataColumns[i]));
+      }
+    }
     rs.close();
   }
 
@@ -1317,4 +1338,68 @@ public class DatabaseMetaDataTest {
     }
   }
 
+  @Test
+  public void testFunctionColumns() throws SQLException {
+    if (!TestUtil.haveMinimumServerVersion(con, ServerVersion.v8_4)) {
+      return;
+    }
+
+    DatabaseMetaData dbmd = con.getMetaData();
+    ResultSet rs = dbmd.getFunctionColumns(null, null, "f1", null);
+
+    ResultSetMetaData rsmd = rs.getMetaData();
+    assertEquals(17, rsmd.getColumnCount());
+    assertEquals("FUNCTION_CAT", rsmd.getColumnName(1));
+    assertEquals("FUNCTION_SCHEM", rsmd.getColumnName(2));
+    assertEquals("FUNCTION_NAME", rsmd.getColumnName(3));
+    assertEquals("COLUMN_NAME", rsmd.getColumnName(4));
+    assertEquals("COLUMN_TYPE", rsmd.getColumnName(5));
+    assertEquals("DATA_TYPE", rsmd.getColumnName(6));
+    assertEquals("TYPE_NAME", rsmd.getColumnName(7));
+    assertEquals("PRECISION", rsmd.getColumnName(8));
+    assertEquals("LENGTH", rsmd.getColumnName(9));
+    assertEquals("SCALE", rsmd.getColumnName(10));
+    assertEquals("RADIX", rsmd.getColumnName(11));
+    assertEquals("NULLABLE", rsmd.getColumnName(12));
+    assertEquals("REMARKS", rsmd.getColumnName(13));
+    assertEquals("CHAR_OCTET_LENGTH", rsmd.getColumnName(14));
+    assertEquals("ORDINAL_POSITION", rsmd.getColumnName(15));
+    assertEquals("IS_NULLABLE", rsmd.getColumnName(16));
+    assertEquals("SPECIFIC_NAME", rsmd.getColumnName(17));
+
+    assertTrue(rs.next());
+    assertEquals(null, rs.getString(1));
+    assertEquals("public", rs.getString(2));
+    assertEquals("f1", rs.getString(3));
+    assertEquals("returnValue", rs.getString(4));
+    assertEquals(DatabaseMetaData.functionReturn, rs.getInt(5));
+    assertEquals(Types.INTEGER, rs.getInt(6));
+    assertEquals("int4", rs.getString(7));
+    assertEquals(0, rs.getInt(15));
+
+    assertTrue(rs.next());
+    assertEquals(null, rs.getString(1));
+    assertEquals("public", rs.getString(2));
+    assertEquals("f1", rs.getString(3));
+    assertEquals("$1", rs.getString(4));
+    assertEquals(DatabaseMetaData.functionColumnIn, rs.getInt(5));
+    assertEquals(Types.INTEGER, rs.getInt(6));
+    assertEquals("int4", rs.getString(7));
+    assertEquals(1, rs.getInt(15));
+
+    assertTrue(rs.next());
+    assertEquals(null, rs.getString(1));
+    assertEquals("public", rs.getString(2));
+    assertEquals("f1", rs.getString(3));
+    assertEquals("$2", rs.getString(4));
+    assertEquals(DatabaseMetaData.functionColumnIn, rs.getInt(5));
+    assertEquals(Types.VARCHAR, rs.getInt(6));
+    assertEquals("varchar", rs.getString(7));
+    assertEquals(2, rs.getInt(15));
+
+    assertTrue(!rs.next());
+
+    rs.close();
+  }
+
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/PGPropertyTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/PGPropertyTest.java
index f720e687..3558c57e 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/PGPropertyTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/PGPropertyTest.java
@@ -9,12 +9,14 @@ import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
 import static org.junit.Assert.assertTrue;
 
 import org.postgresql.Driver;
 import org.postgresql.PGProperty;
 import org.postgresql.ds.PGSimpleDataSource;
 import org.postgresql.ds.common.BaseDataSource;
+import org.postgresql.jdbc.AutoSave;
 import org.postgresql.test.TestUtil;
 import org.postgresql.util.URLCoder;
 
@@ -150,6 +152,18 @@ public class PGPropertyTest {
     }
   }
 
+  /**
+   * Test to make sure that setURL doesn't overwrite autosave
+   * more should be put in but this scratches the current itch
+   */
+  @Test
+  public void testOverWriteDSProperties() throws Exception {
+    PGSimpleDataSource dataSource = new PGSimpleDataSource();
+    dataSource.setAutosave(AutoSave.CONSERVATIVE);
+    dataSource.setURL("jdbc:postgresql://localhost:5432/postgres");
+    assertSame(dataSource.getAutosave(),AutoSave.CONSERVATIVE);
+  }
+
   /**
    * Test that {@link PGProperty#isPresent(Properties)} returns a correct result in all cases.
    */
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/PreparedStatementTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/PreparedStatementTest.java
index 8c4d9901..ef851df8 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/PreparedStatementTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/PreparedStatementTest.java
@@ -568,6 +568,62 @@ public class PreparedStatementTest extends BaseTest4 {
 
   }
 
+  @Test
+  public void testNaNLiteralsSimpleStatement() throws SQLException {
+    Statement stmt = con.createStatement();
+    ResultSet rs = stmt.executeQuery("select 'NaN'::numeric, 'NaN'::real, 'NaN'::double precision");
+    checkNaNLiterals(stmt, rs);
+  }
+
+  @Test
+  public void testNaNLiteralsPreparedStatement() throws SQLException {
+    PreparedStatement stmt = con.prepareStatement("select 'NaN'::numeric, 'NaN'::real, 'NaN'::double precision");
+    checkNaNLiterals(stmt, stmt.executeQuery());
+  }
+
+  private void checkNaNLiterals(Statement stmt, ResultSet rs) throws SQLException {
+    rs.next();
+    assertTrue("Double.isNaN((Double) rs.getObject", Double.isNaN((Double) rs.getObject(3)));
+    assertTrue("Double.isNaN(rs.getDouble", Double.isNaN(rs.getDouble(3)));
+    assertTrue("Float.isNaN((Float) rs.getObject", Float.isNaN((Float) rs.getObject(2)));
+    assertTrue("Float.isNaN(rs.getFloat", Float.isNaN(rs.getFloat(2)));
+    assertTrue("Double.isNaN((Double) rs.getObject", Double.isNaN((Double) rs.getObject(1)));
+    assertTrue("Double.isNaN(rs.getDouble", Double.isNaN(rs.getDouble(1)));
+    rs.close();
+    stmt.close();
+  }
+
+  @Test
+  public void testNaNSetDoubleFloat() throws SQLException {
+    PreparedStatement ps = con.prepareStatement("select ?, ?");
+    ps.setFloat(1, Float.NaN);
+    ps.setDouble(2, Double.NaN);
+
+    checkNaNParams(ps);
+  }
+
+  @Test
+  public void testNaNSetObject() throws SQLException {
+    PreparedStatement ps = con.prepareStatement("select ?, ?");
+    ps.setObject(1, Float.NaN);
+    ps.setObject(2, Double.NaN);
+
+    checkNaNParams(ps);
+  }
+
+  private void checkNaNParams(PreparedStatement ps) throws SQLException {
+    ResultSet rs = ps.executeQuery();
+    rs.next();
+
+    assertTrue("Float.isNaN((Float) rs.getObject", Float.isNaN((Float) rs.getObject(1)));
+    assertTrue("Float.isNaN(rs.getFloat", Float.isNaN(rs.getFloat(1)));
+    assertTrue("Double.isNaN(rs.getDouble", Double.isNaN(rs.getDouble(2)));
+    assertTrue("Double.isNaN(rs.getDouble", Double.isNaN(rs.getDouble(2)));
+
+    TestUtil.closeQuietly(rs);
+    TestUtil.closeQuietly(ps);
+  }
+
   @Test
   public void testBoolean() throws SQLException {
     testBoolean(0);
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java
index 85a0b04d..dd417c66 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/ResultSetMetaDataTest.java
@@ -70,7 +70,7 @@ public class ResultSetMetaDataTest extends BaseTest4 {
   public void setUp() throws Exception {
     super.setUp();
     conn = con;
-    TestUtil.createTable(conn, "rsmd1", "a int primary key, b text, c decimal(10,2)", true);
+    TestUtil.createTable(conn, "rsmd1", "a int primary key, b text, c decimal(10,2)");
     TestUtil.createTable(conn, "rsmd_cache", "a int primary key");
     TestUtil.createTable(conn, "timetest",
         "tm time(3), tmtz timetz, ts timestamp without time zone, tstz timestamp(6) with time zone");
@@ -110,7 +110,7 @@ public class ResultSetMetaDataTest extends BaseTest4 {
   @Test
   public void testStandardResultSet() throws SQLException {
     Statement stmt = conn.createStatement();
-    ResultSet rs = stmt.executeQuery("SELECT a,b,c,a+c as total,oid,b as d FROM rsmd1");
+    ResultSet rs = stmt.executeQuery("SELECT a,b,c,a+c as total, b as d FROM rsmd1");
     runStandardTests(rs.getMetaData());
     rs.close();
     stmt.close();
@@ -121,7 +121,7 @@ public class ResultSetMetaDataTest extends BaseTest4 {
     assumePreparedStatementMetadataSupported();
 
     PreparedStatement pstmt =
-        conn.prepareStatement("SELECT a,b,c,a+c as total,oid,b as d FROM rsmd1 WHERE b = ?");
+        conn.prepareStatement("SELECT a,b,c,a+c as total, b as d FROM rsmd1 WHERE b = ?");
     runStandardTests(pstmt.getMetaData());
     pstmt.close();
   }
@@ -129,15 +129,14 @@ public class ResultSetMetaDataTest extends BaseTest4 {
   private void runStandardTests(ResultSetMetaData rsmd) throws SQLException {
     PGResultSetMetaData pgrsmd = (PGResultSetMetaData) rsmd;
 
-    assertEquals(6, rsmd.getColumnCount());
+    assertEquals(5, rsmd.getColumnCount());
 
     assertEquals("a", rsmd.getColumnLabel(1));
     assertEquals("total", rsmd.getColumnLabel(4));
 
     assertEquals("a", rsmd.getColumnName(1));
-    assertEquals("oid", rsmd.getColumnName(5));
     assertEquals("", pgrsmd.getBaseColumnName(4));
-    assertEquals("b", pgrsmd.getBaseColumnName(6));
+    assertEquals("b", pgrsmd.getBaseColumnName(5));
 
     assertEquals(Types.INTEGER, rsmd.getColumnType(1));
     assertEquals(Types.VARCHAR, rsmd.getColumnType(2));
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/UpdateableResultTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/UpdateableResultTest.java
index 34631605..2a026f7d 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/UpdateableResultTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/UpdateableResultTest.java
@@ -37,8 +37,7 @@ public class UpdateableResultTest extends BaseTest4 {
   public void setUp() throws Exception {
     super.setUp();
     TestUtil.createTable(con, "updateable",
-        "id int primary key, name text, notselected text, ts timestamp with time zone, intarr int[]",
-        true);
+        "id int primary key, name text, notselected text, ts timestamp with time zone, intarr int[]");
     TestUtil.createTable(con, "second", "id1 int primary key, name1 text");
     TestUtil.createTable(con, "stream", "id int primary key, asi text, chr text, bin bytea");
     TestUtil.createTable(con, "multicol", "id1 int not null, id2 int not null, val text");
@@ -330,7 +329,7 @@ public class UpdateableResultTest extends BaseTest4 {
     } catch (SQLException ex) {
     }
 
-    rs = st.executeQuery("select oid,* from updateable");
+    rs = st.executeQuery("select * from updateable");
     assertTrue(rs.first());
     rs.updateInt("id", 3);
     rs.updateString("name", "dave3");
diff --git a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/SimpleDataSourceTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/SimpleDataSourceTest.java
index 90a7e62f..644d2736 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/SimpleDataSourceTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/optional/SimpleDataSourceTest.java
@@ -5,8 +5,11 @@
 
 package org.postgresql.test.jdbc2.optional;
 
+import org.postgresql.ds.PGSimpleDataSource;
 import org.postgresql.jdbc2.optional.SimpleDataSource;
 
+import org.junit.Test;
+
 /**
  * Performs the basic tests defined in the superclass. Just adds the configuration logic.
  *
@@ -24,4 +27,11 @@ public class SimpleDataSourceTest extends BaseDataSourceTest {
       setupDataSource(bds);
     }
   }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void testTypoPostgresUrl() {
+    PGSimpleDataSource ds = new PGSimpleDataSource();
+    // this should fail because the protocol is wrong.
+    ds.setUrl("jdbc:postgres://localhost:5432/test");
+  }
 }
diff --git a/pgjdbc/src/test/java/org/postgresql/test/util/ServerVersionParseTest.java b/pgjdbc/src/test/java/org/postgresql/test/util/ServerVersionParseTest.java
index 88bbc95c..ffe1a90c 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/util/ServerVersionParseTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/util/ServerVersionParseTest.java
@@ -47,6 +47,7 @@ public class ServerVersionParseTest {
         {"9.6", 90600, null},
         {"10", 100000, null},
         {"11", 110000, null},
+        {"12", 120000, null},
         /* Multidigit */
         {"9.4.10", 90410, null},
         {"9.20.10", 92010, null},
diff --git a/pgjdbc/src/test/java/org/postgresql/test/util/ServerVersionTest.java b/pgjdbc/src/test/java/org/postgresql/test/util/ServerVersionTest.java
index 3e320890..a0a80611 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/util/ServerVersionTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/util/ServerVersionTest.java
@@ -22,4 +22,14 @@ public class ServerVersionTest {
       prev = serverVersion;
     }
   }
+
+  @Test
+  public void testVersions() {
+    Assert.assertEquals(ServerVersion.v12.getVersionNum(), ServerVersion.from("12.0").getVersionNum());
+    Assert.assertEquals(120004, ServerVersion.from("12.4").getVersionNum());
+    Assert.assertEquals(ServerVersion.v11.getVersionNum(), ServerVersion.from("11.0").getVersionNum());
+    Assert.assertEquals(110006, ServerVersion.from("11.6").getVersionNum());
+    Assert.assertEquals(ServerVersion.v10.getVersionNum(), ServerVersion.from("10.0").getVersionNum());
+    Assert.assertTrue(ServerVersion.v9_6.getVersionNum() < ServerVersion.from("9.6.4").getVersionNum());
+  }
 }