Permalink
Browse files

fix: revert array naming to pre 1202 behavior (e.g. _int4)

Register Postgres internal name in oid to name map.
Alternative names are registered in name to oid map.

During unknown type lookup use current_schemas(true) which will include pg_catalog schema too.
This is required since unregistered core types will use complex lookup for arrays.

closes #595
  • Loading branch information...
vlsi committed Jul 10, 2016
1 parent 8e8dbab commit 1d8ebfc48466f9105985bda88514ab04602bcae8
@@ -156,10 +156,13 @@ public synchronized void addCoreType(String pgTypeName, Integer oid, Integer sql
String pgArrayTypeName = pgTypeName + "[]";
_pgNameToJavaClass.put(pgArrayTypeName, "java.sql.Array");
_pgNameToSQLType.put(pgArrayTypeName, Types.ARRAY);
_pgNameToOid.put(pgArrayTypeName, arrayOid);
pgArrayTypeName = "_" + pgTypeName;
if (!_pgNameToJavaClass.containsKey(pgArrayTypeName)) {
_pgNameToJavaClass.put(pgArrayTypeName, "java.sql.Array");
_pgNameToSQLType.put(pgArrayTypeName, Types.ARRAY);
_pgNameToOid.put(pgArrayTypeName, arrayOid);
_oidToPgName.put(arrayOid, pgArrayTypeName);
}
}

@@ -261,13 +264,13 @@ private PreparedStatement getOidStatement(String pgTypeName) throws SQLException
boolean hasQuote = pgTypeName.contains("\"");
int dotIndex = pgTypeName.indexOf('.');

if (dotIndex == -1 && !hasQuote) {
if (dotIndex == -1 && !hasQuote && !isArray) {
if (_getOidStatementSimple == null) {
String sql;
if (_conn.haveMinimumServerVersion(ServerVersion.v8_0)) {
// see comments in @getSQLType()
// -- go with older way of unnesting array to be compatible with 8.0
sql = "SELECT pg_type.oid "
sql = "SELECT pg_type.oid, typname "
+ " FROM pg_catalog.pg_type "
+ " LEFT "
+ " JOIN (select ns.oid as nspoid, ns.nspname, r.r "
@@ -280,38 +283,38 @@ private PreparedStatement getOidStatement(String pgTypeName) throws SQLException
+ " WHERE typname = ? "
+ " ORDER BY sp.r, pg_type.oid DESC LIMIT 1;";
} else if (_conn.haveMinimumServerVersion(ServerVersion.v7_3)) {
sql = "SELECT oid FROM pg_catalog.pg_type WHERE typname = ? ORDER BY oid DESC LIMIT 1";
sql = "SELECT oid, typname FROM pg_catalog.pg_type WHERE typname = ? ORDER BY oid DESC LIMIT 1";
} else {
sql = "SELECT oid FROM pg_type WHERE typname = ? ORDER BY oid DESC LIMIT 1";
sql = "SELECT oid, typname FROM pg_type WHERE typname = ? ORDER BY oid DESC LIMIT 1";
}
_getOidStatementSimple = _conn.prepareStatement(sql);
}
// coerce to lower case to handle upper case type names
String lcName = pgTypeName.toLowerCase();
// default arrays are represented with _ as prefix ... this dont even work for public schema
// fully
_getOidStatementSimple.setString(1,
isArray ? "_" + lcName.substring(0, lcName.length() - 2) : lcName);
_getOidStatementSimple.setString(1, lcName);
return _getOidStatementSimple;
}
PreparedStatement oidStatementComplex;
if (isArray) {
if (_getOidStatementComplexArray == null) {
String sql = "SELECT t.typarray "
String sql = "SELECT t.typarray, arr.typname "
+ " FROM pg_catalog.pg_type t"
+ " JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid"
+ " WHERE t.typname = ? AND (n.nspname = ? OR ? IS NULL AND n.nspname = ANY (current_schemas(false)))"
+ " ORDER BY t.oid DESC LIMIT 1;";
+ " JOIN pg_catalog.pg_type arr ON arr.oid = t.typarray"
+ " WHERE t.typname = ? AND (n.nspname = ? OR ? IS NULL AND n.nspname = ANY (current_schemas(true)))"
+ " ORDER BY t.oid DESC LIMIT 1";
_getOidStatementComplexArray = _conn.prepareStatement(sql);
}
oidStatementComplex = _getOidStatementComplexArray;
} else {
if (_getOidStatementComplexNonArray == null) {
String sql = "SELECT t.oid "
String sql = "SELECT t.oid, t.typname "
+ " FROM pg_catalog.pg_type t"
+ " JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid"
+ " WHERE t.typname = ? AND (n.nspname = ? OR ? IS NULL AND n.nspname = ANY (current_schemas(false)))"
+ " ORDER BY t.oid DESC LIMIT 1;";
+ " WHERE t.typname = ? AND (n.nspname = ? OR ? IS NULL AND n.nspname = ANY (current_schemas(true)))"
+ " ORDER BY t.oid DESC LIMIT 1";
_getOidStatementComplexNonArray = _conn.prepareStatement(sql);
}
oidStatementComplex = _getOidStatementComplexNonArray;
@@ -372,7 +375,9 @@ public synchronized int getPGType(String pgTypeName) throws SQLException {
ResultSet rs = oidStatement.getResultSet();
if (rs.next()) {
oid = (int) rs.getLong(1);
_oidToPgName.put(oid, pgTypeName);
String internalName = rs.getString(2);
_oidToPgName.put(oid, internalName);
_pgNameToOid.put(internalName, oid);
}
_pgNameToOid.put(pgTypeName, oid);
rs.close();
@@ -318,7 +318,7 @@ public static void createSchema(Connection con, String schema) throws SQLExcepti

st.executeUpdate(sql);
} finally {
st.close();
closeQuietly(st);
}
}

@@ -372,7 +372,7 @@ public static void createTable(Connection con, String table, String columns, boo
}
st.executeUpdate(sql);
} finally {
st.close();
closeQuietly(st);
}
}

@@ -394,7 +394,7 @@ public static void createTempTable(Connection con, String table, String columns)
// Now create the table
st.executeUpdate("create temp table " + table + " (" + columns + ")");
} finally {
st.close();
closeQuietly(st);
}
}

@@ -416,7 +416,7 @@ public static void createEnumType(Connection con, String name, String values)
// Now create the table
st.executeUpdate("create type " + name + " as enum (" + values + ")");
} finally {
st.close();
closeQuietly(st);
}
}

@@ -438,7 +438,46 @@ public static void createCompositeType(Connection con, String name, String value
// Now create the table
st.executeUpdate("create type " + name + " as (" + values + ")");
} finally {
st.close();
closeQuietly(st);
}
}

/**
* Drops a domain
*
* @param con Connection
* @param name String
*/
public static void dropDomain(Connection con, String name)
throws SQLException {
Statement st = con.createStatement();
try {
st.executeUpdate("drop domain " + name + " cascade");
} catch (SQLException ex) {
if (!con.getAutoCommit()) {
throw ex;
}
} finally {
closeQuietly(st);
}
}

/**
* Helper creates a domain
*
* @param con Connection
* @param name String
* @param values String
*/
public static void createDomain(Connection con, String name, String values)
throws SQLException {
Statement st = con.createStatement();
try {
dropDomain(con, name);
// Now create the table
st.executeUpdate("create domain " + name + " as " + values);
} finally {
closeQuietly(st);
}
}

@@ -485,7 +524,7 @@ public static void dropTable(Connection con, String table) throws SQLException {
public static void dropType(Connection con, String type) throws SQLException {
Statement stmt = con.createStatement();
try {
String sql = "DROP TYPE " + type;
String sql = "DROP TYPE " + type + " CASCADE";
stmt.executeUpdate(sql);
} catch (SQLException ex) {
if (!con.getAutoCommit()) {
@@ -50,6 +50,10 @@ protected void setUp() throws Exception {
TestUtil.createTable(con, "\"a\\\"", "a int4");
TestUtil.createTable(con, "\"a'\"", "a int4");
TestUtil.createTable(con, "arraytable", "a numeric(5,2)[], b varchar(100)[]");
TestUtil.createTable(con, "intarraytable", "a int4[], b int4[][]");
TestUtil.createCompositeType(con, "custom", "i int");
TestUtil.createCompositeType(con, "_custom", "f float");
TestUtil.createTable(con, "customtable", "c1 custom, c2 _custom, c3 custom[], c4 _custom[]");

Statement stmt = con.createStatement();
// we add the following comments to ensure the joins to the comments
@@ -73,8 +77,8 @@ protected void setUp() throws Exception {
"CREATE OR REPLACE FUNCTION f5() RETURNS TABLE (i int) LANGUAGE sql AS 'SELECT 1'");

if (TestUtil.haveMinimumServerVersion(con, "7.3")) {
stmt.execute("CREATE DOMAIN nndom AS int not null");
stmt.execute("CREATE TABLE domaintable (id nndom)");
TestUtil.createDomain(con, "nndom", "int not null");
TestUtil.createTable(con, "domaintable", "id nndom");
}
stmt.close();
}
@@ -92,6 +96,10 @@ protected void tearDown() throws Exception {
TestUtil.dropTable(con, "\"a\\\"");
TestUtil.dropTable(con, "\"a'\"");
TestUtil.dropTable(con, "arraytable");
TestUtil.dropTable(con, "intarraytable");
TestUtil.dropTable(con, "customtable");
TestUtil.dropType(con, "custom");
TestUtil.dropType(con, "_custom");

stmt.execute("DROP FUNCTION f1(int, varchar)");
if (TestUtil.haveMinimumServerVersion(con, "8.0")) {
@@ -101,13 +109,60 @@ protected void tearDown() throws Exception {
stmt.execute("DROP FUNCTION f3(int, varchar)");
}
if (TestUtil.haveMinimumServerVersion(con, "7.3")) {
stmt.execute("DROP TABLE domaintable");
stmt.execute("DROP DOMAIN nndom");
TestUtil.dropType(con, "domaintable");
TestUtil.dropDomain(con, "nndom");
}

TestUtil.closeDB(con);
}

public void testArrayTypeInfo() throws SQLException {
DatabaseMetaData dbmd = con.getMetaData();
ResultSet rs = dbmd.getColumns(null, null, "intarraytable", "a");
assertTrue(rs.next());
assertEquals("_int4", rs.getString("TYPE_NAME"));
con.createArrayOf("integer", new Integer[] {});
TestUtil.closeQuietly(rs);
rs = dbmd.getColumns(null, null, "intarraytable", "a");
assertTrue(rs.next());
assertEquals("_int4", rs.getString("TYPE_NAME"));
TestUtil.closeQuietly(rs);
}

public void testArrayInt4DoubleDim() throws SQLException {
DatabaseMetaData dbmd = con.getMetaData();
ResultSet rs = dbmd.getColumns(null, null, "intarraytable", "b");
assertTrue(rs.next());
assertEquals("_int4", rs.getString("TYPE_NAME")); // even int4[][] is represented as _int4
con.createArrayOf("int4", new int[][]{{1, 2}, {3, 4}});
rs = dbmd.getColumns(null, null, "intarraytable", "b");
assertTrue(rs.next());
assertEquals("_int4", rs.getString("TYPE_NAME")); // even int4[][] is represented as _int4
}

public void testCustomArrayTypeInfo() throws SQLException {
DatabaseMetaData dbmd = con.getMetaData();
ResultSet res = dbmd.getColumns(null, null, "customtable", null);
assertTrue(res.next());
assertEquals("custom", res.getString("TYPE_NAME"));
assertTrue(res.next());
assertEquals("_custom", res.getString("TYPE_NAME"));
assertTrue(res.next());
assertEquals("__custom", res.getString("TYPE_NAME"));
assertTrue(res.next());
assertEquals("___custom", res.getString("TYPE_NAME"));
con.createArrayOf("custom", new Object[] {});
res = dbmd.getColumns(null, null, "customtable", null);
assertTrue(res.next());
assertEquals("custom", res.getString("TYPE_NAME"));
assertTrue(res.next());
assertEquals("_custom", res.getString("TYPE_NAME"));
assertTrue(res.next());
assertEquals("__custom", res.getString("TYPE_NAME"));
assertTrue(res.next());
assertEquals("___custom", res.getString("TYPE_NAME"));
}

public void testTables() throws Exception {
DatabaseMetaData dbmd = con.getMetaData();
assertNotNull(dbmd);
@@ -53,7 +53,8 @@ public void setUp() throws Exception {
_conn = con;

TestUtil.createTable(_conn, "arrtest",
"intarr int[], decarr decimal(2,1)[], strarr text[], uuidarr uuid[], floatarr float8[]");
"intarr int[], decarr decimal(2,1)[], strarr text[], uuidarr uuid[], floatarr float8[]"
+ ", intarr2 int4[][]");
TestUtil.createTable(_conn, "arrcompprnttest", "id serial, name character(10)");
TestUtil.createTable(_conn, "arrcompchldttest",
"id serial, name character(10), description character varying, parent integer");
@@ -493,4 +494,20 @@ public void createNullArray() throws SQLException {
Array arr = con.createArrayOf("float8", null);
Assert.fail("createArrayOf(float8, null) should fail with NPE");
}

@Test
public void multiDimIntArray() throws SQLException {
Array arr = con.createArrayOf("int4", new int[][]{{1,2}, {3,4}});
PreparedStatement ps = con.prepareStatement("select ?::int4[][]");
ps.setArray(1, arr);
ResultSet rs = ps.executeQuery();
rs.next();
Array resArray = rs.getArray(1);
String stringValue = resArray.toString();
// Both {{"1","2"},{"3","4"}} and {{1,2},{3,4}} are the same array representation
stringValue = stringValue.replaceAll("\"", "");
Assert.assertEquals("{{1,2},{3,4}}", stringValue);
TestUtil.closeQuietly(rs);
TestUtil.closeQuietly(ps);
}
}

0 comments on commit 1d8ebfc

Please sign in to comment.