Permalink
Browse files

fix: Improve type detection with casing issues.

Instead of lowercasing typename which causes problems for types which require quote due to non lowercase casing,
lowercase on failed alias check and during type lookup for types without schema or quote.

Improve the quote casing heuristics by checking for quote char.
Support case type in schemas on path without schema reference.

Current rules for casing

TYPE => "type"
Type => "type"
"TYPE" => "TYPE"
NS.TYPE => "ns"."type"
"NS"."TYPE" => "NS"."TYPE"
NS."TYPE" => "ns"."TYPE"

Not sure about the last one and the first one.
  • Loading branch information...
zapov committed Oct 16, 2015
1 parent 80ac06e commit 0de91a570c27f78f4648c75dfd00c1b2d66aceaf
@@ -12,10 +12,7 @@
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.*;
import org.postgresql.core.BaseConnection;
import org.postgresql.core.BaseStatement;
@@ -260,12 +257,10 @@ public synchronized int getSQLType(String pgTypeName) throws SQLException
private PreparedStatement getOidStatement(String pgTypeName) throws SQLException
{
boolean isArray = pgTypeName.endsWith("[]");
//TODO: not fully correct, but better than before
boolean hasSchema = pgTypeName.contains("\".\"")
|| pgTypeName.contains(".") && (pgTypeName.charAt(0) != '"'
|| pgTypeName.charAt(pgTypeName.length() - 1) != '"');
boolean hasQuote = pgTypeName.contains("\"");
int dotIndex = pgTypeName.indexOf('.');
if (!hasSchema) {
if (dotIndex == -1 && !hasQuote) {
if (_getOidStatementSimple == null) {
String sql;
if (_conn.haveMinimumServerVersion(ServerVersion.v8_0)) {
@@ -290,8 +285,10 @@ private PreparedStatement getOidStatement(String pgTypeName) throws SQLException
}
_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 ? "_" + pgTypeName.substring(0, pgTypeName.length() - 2) : pgTypeName);
_getOidStatementSimple.setString(1, isArray ? "_" + lcName.substring(0, lcName.length() - 2) : lcName);
return _getOidStatementSimple;
}
PreparedStatement oidStatementComplex;
@@ -300,7 +297,7 @@ private PreparedStatement getOidStatement(String pgTypeName) throws SQLException
String sql = "SELECT t.typarray " +
" FROM pg_catalog.pg_type t" +
" JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid" +
" WHERE t.typname = ? AND n.nspname = ?" +
" WHERE t.typname = ? AND (n.nspname = ? OR ? IS NULL AND n.nspname = ANY (current_schemas(false)))" +
" ORDER BY t.oid DESC LIMIT 1;";
_getOidStatementComplexArray = _conn.prepareStatement(sql);
}
@@ -310,7 +307,7 @@ private PreparedStatement getOidStatement(String pgTypeName) throws SQLException
String sql = "SELECT t.oid " +
" FROM pg_catalog.pg_type t" +
" JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid" +
" WHERE t.typname = ? AND n.nspname = ?" +
" WHERE t.typname = ? AND (n.nspname = ? OR ? IS NULL AND n.nspname = ANY (current_schemas(false)))" +
" ORDER BY t.oid DESC LIMIT 1;";
_getOidStatementComplexNonArray = _conn.prepareStatement(sql);
}
@@ -320,36 +317,38 @@ private PreparedStatement getOidStatement(String pgTypeName) throws SQLException
String schema;
String name;
//simple use case
int firstDotIndex = fullName.indexOf('.');
int lastDotIndex = fullName.lastIndexOf('.');
if (firstDotIndex == lastDotIndex) {
String[] parts = fullName.split("\\.");
schema = parts[0];
name = parts[1];
if (dotIndex == -1) {
schema = null;
name = fullName;
} else {
if (fullName.startsWith("\"")) {
if (fullName.endsWith("\"")) {
//TODO: only covers simple scenario...
String[] parts = fullName.split("\"\\.\"");
schema = parts[0];
name = parts[1];
schema = parts.length == 2 ? parts[0] + "\"" : null;
name = parts.length == 2 ? "\"" + parts[1] : parts[0];
} else {
int lastDotIndex = fullName.lastIndexOf('.');
name = fullName.substring(lastDotIndex + 1);
schema = fullName.substring(0, lastDotIndex);
}
} else {
schema = fullName.substring(0, firstDotIndex);
name = fullName.substring(firstDotIndex + 1);
schema = fullName.substring(0, dotIndex);
name = fullName.substring(dotIndex + 1);
}
}
if (schema.startsWith("\"") && schema.endsWith("\"")) {
if (schema != null && schema.startsWith("\"") && schema.endsWith("\"")) {
schema = schema.substring(1, schema.length() - 1);
} //TODO: should lowercase otherwise?
} else if (schema != null) {
schema = schema.toLowerCase();
}
if (name.startsWith("\"") && name.endsWith("\"")) {
name = name.substring(1, name.length() - 1);
} else {
name = name.toLowerCase();
}
oidStatementComplex.setString(1, name);
oidStatementComplex.setString(2, schema);
oidStatementComplex.setString(3, schema);
return oidStatementComplex;
}
@@ -504,7 +503,7 @@ public synchronized int getPGArrayElement (int oid) throws SQLException
if (_conn.haveMinimumServerVersion(ServerVersion.v7_3)) {
sql = "SELECT e.oid, n.nspname = ANY(current_schemas(true)), n.nspname, e.typname FROM pg_catalog.pg_type t JOIN pg_catalog.pg_type e ON t.typelem = e.oid JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = ?";
} else {
sql = "SELECT e.oid, n.nspname = ANY(current_schemas(true)), n.nspname, e.typname FROM pg_type t JOIN pg_type e ON t.typelem = e.oid JOIN pg_catalog.pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = ?";
sql = "SELECT e.oid, n.nspname = ANY(current_schemas(true)), n.nspname, e.typname FROM pg_type t JOIN pg_type e ON t.typelem = e.oid JOIN pg_namespace n ON t.typnamespace = n.oid WHERE t.oid = ?";
}
_getArrayElementOidStatement = _conn.prepareStatement(sql);
}
@@ -565,6 +564,11 @@ public String getTypeForAlias(String alias) {
String type = (String) typeAliases.get(alias);
if (type != null)
return type;
if (alias.indexOf('"') == -1) {
type = (String) typeAliases.get(alias.toLowerCase());
if (type != null)
return type;
}
return alias;
}
@@ -100,8 +100,7 @@ public Array createArrayOf(String typeName, Object[] elements) throws SQLExcepti
{
checkClosed();
// coerce to lower case to handle upper case type names
int oid = getTypeInfo().getPGArrayType(typeName.toLowerCase());
int oid = getTypeInfo().getPGArrayType(typeName);
if (oid == Oid.UNSPECIFIED)
throw new PSQLException(GT.tr("Unable to find server array type for provided name {0}.", typeName), PSQLState.INVALID_NAME);
@@ -90,7 +90,7 @@ public void testCompositeFromTable() throws SQLException {
pstmt.setObject(1, pgo1);
String[] ctArr = new String[1];
ctArr[0] = "(\"{1,2}\",{},\"(1,2.2,)\")";
Array pgarr1 = _conn.createArrayOf("Composites.ComplexCompositeTest", ctArr);
Array pgarr1 = _conn.createArrayOf("\"Composites\".\"ComplexCompositeTest\"", ctArr);
pstmt.setArray(2, pgarr1);
int res = pstmt.executeUpdate();
assertEquals(1, res);
@@ -11,7 +11,9 @@
import java.util.UUID;
import junit.framework.TestCase;
import org.junit.Assert;
import org.postgresql.test.TestUtil;
import org.postgresql.util.PGobject;
import org.postgresql.util.PGtokenizer;
import org.postgresql.geometric.PGbox;
@@ -28,12 +30,15 @@ protected void setUp() throws Exception {
TestUtil.createTable(_conn, "arrtest", "intarr int[], decarr decimal(2,1)[], strarr text[], uuidarr uuid[]");
TestUtil.createTable(_conn, "arrcompprnttest", "id serial, name character(10)");
TestUtil.createTable(_conn, "arrcompchldttest", "id serial, name character(10), description character varying, parent integer");
TestUtil.createTable(_conn, "\"CorrectCasing\"", "id serial");
TestUtil.createTable(_conn, "\"Evil.Table\"", "id serial");
}
protected void tearDown() throws SQLException {
TestUtil.dropTable(_conn, "arrtest");
TestUtil.dropTable(_conn, "arrcompprnttest");
TestUtil.dropTable(_conn, "arrcompchldttest");
TestUtil.dropTable(_conn, "\"CorrectCasing\"");
TestUtil.closeDB(_conn);
}
@@ -311,4 +316,68 @@ public void testGetArrayOfComposites() throws SQLException {
fail("Needs to have 3 tokens");
}
}
public void testCasingComposite() throws SQLException {
PGobject cc = new PGobject();
cc.setType("\"CorrectCasing\"");
cc.setValue("(1)");
Object[] in = new Object[1];
in[0] = cc;
Array arr = _conn.createArrayOf("\"CorrectCasing\"", in);
PreparedStatement pstmt = _conn.prepareStatement("SELECT ?::\"CorrectCasing\"[]");
pstmt.setArray(1, arr);
ResultSet rs = pstmt.executeQuery();
assertTrue(rs.next());
Object[] resArr = (Object[])rs.getArray(1).getArray();
assertTrue(resArr[0] instanceof PGobject);
PGobject resObj = (PGobject) resArr[0];
assertEquals("(1)", resObj.getValue());
}
public void testCasingBuiltinAlias() throws SQLException {
Array arr = _conn.createArrayOf("INT", new Integer[] { 1 , 2, 3});
PreparedStatement pstmt = _conn.prepareStatement("SELECT ?::INT[]");
pstmt.setArray(1, arr);
ResultSet rs = pstmt.executeQuery();
assertTrue(rs.next());
Integer[] resArr = (Integer[])rs.getArray(1).getArray();
Assert.assertArrayEquals(new Integer[]{1, 2, 3}, resArr);
}
public void testCasingBuiltinNonAlias() throws SQLException {
Array arr = _conn.createArrayOf("INT4", new Integer[] { 1 , 2, 3});
PreparedStatement pstmt = _conn.prepareStatement("SELECT ?::INT4[]");
pstmt.setArray(1, arr);
ResultSet rs = pstmt.executeQuery();
assertTrue(rs.next());
Integer[] resArr = (Integer[])rs.getArray(1).getArray();
Assert.assertArrayEquals(new Integer[]{1, 2, 3}, resArr);
}
public void testEvilCasing() throws SQLException {
PGobject cc = new PGobject();
cc.setType("\"Evil.Table\"");
cc.setValue("(1)");
Object[] in = new Object[1];
in[0] = cc;
Array arr = _conn.createArrayOf("\"Evil.Table\"", in);
PreparedStatement pstmt = _conn.prepareStatement("SELECT ?::\"Evil.Table\"[]");
pstmt.setArray(1, arr);
ResultSet rs = pstmt.executeQuery();
assertTrue(rs.next());
Object[] resArr = (Object[])rs.getArray(1).getArray();
assertTrue(resArr[0] instanceof PGobject);
PGobject resObj = (PGobject) resArr[0];
assertEquals("(1)", resObj.getValue());
}
}

0 comments on commit 0de91a5

Please sign in to comment.