Prefix types in namespaces other than pg_catalog or public

It's possible to define a type (e.g., an enum) with the same name in two
different schemas. When initializing data types after connecting, types
defined within schemas other than pg_catalog or public should be
qualified with their schema name to disambiguate them and ensure all
types with the same base name get added to the map of OID to type.

Prior to this commit, the last type scanned would "win", and all others
with the same name would be missing from the ConnInfo type maps, which
would subsequently cause any PREPARE involving columns of those missing
types to return the error "unknown oid".
This commit is contained in:
Kelsey Francis 2017-09-08 15:49:54 -07:00
parent b70fb1c7cf
commit 953e08df99
3 changed files with 44 additions and 2 deletions

View File

@ -384,9 +384,13 @@ func (c *Conn) connect(config ConnConfig, network, address string, tlsConfig *tl
func (c *Conn) initConnInfo() error {
nameOIDs := make(map[string]pgtype.OID, 256)
rows, err := c.Query(`select t.oid, t.typname
rows, err := c.Query(`select t.oid,
case when nsp.nspname in ('pg_catalog', 'public') then t.typname
else nsp.nspname||'.'||t.typname
end
from pg_type t
left join pg_type base_type on t.typelem=base_type.oid
left join pg_namespace nsp on t.typnamespace=nsp.oid
where (
t.typtype in('b', 'p', 'r', 'e')
and (base_type.oid is null or base_type.typtype in('b', 'p', 'r'))

View File

@ -1953,3 +1953,31 @@ end$$;`)
ensureConnValid(t, conn)
}
func TestConnInitConnInfo(t *testing.T) {
conn := mustConnect(t, *defaultConnConfig)
defer closeConn(t, conn)
// spot check that the standard postgres type names aren't qualified
nameOIDs := map[string]pgtype.OID{
"_int8": pgtype.Int8ArrayOID,
"int8": pgtype.Int8OID,
"json": pgtype.JSONOID,
"text": pgtype.TextOID,
}
for name, oid := range nameOIDs {
dtByName, ok := conn.ConnInfo.DataTypeForName(name)
if !ok {
t.Fatalf("Expected type named %v to be present", name)
}
dtByOID, ok := conn.ConnInfo.DataTypeForOID(oid)
if !ok {
t.Fatalf("Expected type OID %v to be present", oid)
}
if dtByName != dtByOID {
t.Fatalf("Expected type named %v to be the same as type OID %v", name, oid)
}
}
ensureConnValid(t, conn)
}

View File

@ -204,7 +204,17 @@ func AcceptUnauthenticatedConnRequestSteps() []Step {
func PgxInitSteps() []Step {
steps := []Step{
ExpectMessage(&pgproto3.Parse{
Query: "select t.oid, t.typname\nfrom pg_type t\nleft join pg_type base_type on t.typelem=base_type.oid\nwhere (\n\t t.typtype in('b', 'p', 'r', 'e')\n\t and (base_type.oid is null or base_type.typtype in('b', 'p', 'r'))\n\t)",
Query: `select t.oid,
case when nsp.nspname in ('pg_catalog', 'public') then t.typname
else nsp.nspname||'.'||t.typname
end
from pg_type t
left join pg_type base_type on t.typelem=base_type.oid
left join pg_namespace nsp on t.typnamespace=nsp.oid
where (
t.typtype in('b', 'p', 'r', 'e')
and (base_type.oid is null or base_type.typtype in('b', 'p', 'r'))
)`,
}),
ExpectMessage(&pgproto3.Describe{
ObjectType: 'S',