Repository: pgjdbc/pgjdbc (java)

Issue:
Fix spelling of CHAR_OCTECT_LENGTH
https://github.com/pgjdbc/pgjdbc/blob/e44e4e8972ec41bf05dddd4ac6dcfbc42a9842cf/pgjdbc/src/main/java/org/postgresql/jdbc/PgDatabaseMetaData.java#L1069

Pull request:
fix missing metadata columns, and misspelled columns
fixes #1320 and #1321



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/CHANGELOG.md b/CHANGELOG.md
index a3f3865a..7d65a822 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -50,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/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/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/ds/common/BaseDataSource.java b/pgjdbc/src/main/java/org/postgresql/ds/common/BaseDataSource.java
index 80c1349a..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,6 +1109,9 @@ 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()) {
       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 f317b234..3d49a67a 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);
     }
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/ChainedCommonCreateSlotBuilder.java b/pgjdbc/src/main/java/org/postgresql/replication/fluent/ChainedCommonCreateSlotBuilder.java
index 4076a4b7..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,13 +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
@@ -36,7 +38,9 @@ public interface ChainedCommonCreateSlotBuilder<T extends ChainedCommonCreateSlo
 
   /**
    * 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 72443c18..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,14 +6,19 @@
 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;
 
   public LogicalCreateSlotBuilder(BaseConnection connection) {
@@ -32,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");
@@ -43,15 +48,30 @@ public class LogicalCreateSlotBuilder
     }
 
     Statement statement = connection.createStatement();
+    ResultSet result = null;
+    ReplicationSlotInfo slotInfo = null;
     try {
       statement.execute(String.format(
-              "CREATE_REPLICATION_SLOT %s %s LOGICAL %s",
-              slotName,
-              temporaryOption ? "TEMPORARY" : "",
-              outputPlugin
+          "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 1ae0ff0f..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,8 +6,12 @@
 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;
 
@@ -25,20 +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 %s PHYSICAL",
-              slotName,
-              temporaryOption ? "TEMPORARY" : ""
+          "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;
   }
 }


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 961075d2..5a5aab24 100644
--- a/pgjdbc/src/test/java/org/postgresql/replication/ReplicationSlotTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/replication/ReplicationSlotTest.java
@@ -6,6 +6,9 @@
 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;
@@ -223,6 +226,47 @@ public class ReplicationSlotTest {
     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 {
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/jdbc2/DatabaseMetaDataTest.java b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
index 77d8d72a..9e936be6 100644
--- a/pgjdbc/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
+++ b/pgjdbc/src/test/java/org/postgresql/test/jdbc2/DatabaseMetaDataTest.java
@@ -177,8 +177,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 +463,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();
   }
 
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());
+  }
 }