@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
* Support sslkey, sslcert, and sslrootcert URI params (Sean Chittenden)
|
* Support sslkey, sslcert, and sslrootcert URI params (Sean Chittenden)
|
||||||
* Allow any scheme in ParseURI (for convenience with cockroachdb) (Sean Chittenden)
|
* Allow any scheme in ParseURI (for convenience with cockroachdb) (Sean Chittenden)
|
||||||
|
* Add support for domain types
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
|
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ Then run the following SQL:
|
|||||||
Connect to database pgx_test and run:
|
Connect to database pgx_test and run:
|
||||||
|
|
||||||
create extension hstore;
|
create extension hstore;
|
||||||
|
create domain uint64 as numeric(20,0);
|
||||||
|
|
||||||
Next open conn_config_test.go.example and make a copy without the
|
Next open conn_config_test.go.example and make a copy without the
|
||||||
.example. If your PostgreSQL server is accepting connections on 127.0.0.1,
|
.example. If your PostgreSQL server is accepting connections on 127.0.0.1,
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"os/user"
|
"os/user"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -428,6 +429,10 @@ where (
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err = c.initConnInfoDomains(cinfo); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
return cinfo, nil
|
return cinfo, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -494,6 +499,52 @@ where t.typtype = 'b'
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// initConnInfoDomains introspects for domains and registers a data type for them.
|
||||||
|
func (c *Conn) initConnInfoDomains(cinfo *pgtype.ConnInfo) error {
|
||||||
|
type domain struct {
|
||||||
|
oid pgtype.OID
|
||||||
|
name string
|
||||||
|
baseOID pgtype.OID
|
||||||
|
}
|
||||||
|
|
||||||
|
domains := make([]*domain, 0, 16)
|
||||||
|
|
||||||
|
rows, err := c.Query(`select t.oid, t.typname, t.typbasetype
|
||||||
|
from pg_type t
|
||||||
|
join pg_type base_type on t.typbasetype=base_type.oid
|
||||||
|
where t.typtype = 'd'
|
||||||
|
and base_type.typtype = 'b'`)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for rows.Next() {
|
||||||
|
var d domain
|
||||||
|
if err := rows.Scan(&d.oid, &d.name, &d.baseOID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
domains = append(domains, &d)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rows.Err() != nil {
|
||||||
|
return rows.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, d := range domains {
|
||||||
|
baseDataType, ok := cinfo.DataTypeForOID(d.baseOID)
|
||||||
|
if ok {
|
||||||
|
cinfo.RegisterDataType(pgtype.DataType{
|
||||||
|
Value: reflect.New(reflect.ValueOf(baseDataType.Value).Elem().Type()).Interface().(pgtype.Value),
|
||||||
|
Name: d.name,
|
||||||
|
OID: d.oid,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// crateDBTypesQuery checks if the given err is likely to be the result of
|
// crateDBTypesQuery checks if the given err is likely to be the result of
|
||||||
// CrateDB not implementing the pg_types table correctly. If yes, a CrateDB
|
// CrateDB not implementing the pg_types table correctly. If yes, a CrateDB
|
||||||
// specific query against pg_types is executed and its results are returned. If
|
// specific query against pg_types is executed and its results are returned. If
|
||||||
|
|||||||
@@ -2073,3 +2073,28 @@ func TestConnInitConnInfo(t *testing.T) {
|
|||||||
|
|
||||||
ensureConnValid(t, conn)
|
ensureConnValid(t, conn)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDomainType(t *testing.T) {
|
||||||
|
conn := mustConnect(t, *defaultConnConfig)
|
||||||
|
defer closeConn(t, conn)
|
||||||
|
|
||||||
|
dt, ok := conn.ConnInfo.DataTypeForName("uint64")
|
||||||
|
if !ok {
|
||||||
|
t.Fatal("Expected data type for domain uint64 to be present")
|
||||||
|
}
|
||||||
|
if dt, ok := dt.Value.(*pgtype.Numeric); !ok {
|
||||||
|
t.Fatal("Expected data type value for domain uint64 to be *pgtype.Numeric, but it was %T", dt)
|
||||||
|
}
|
||||||
|
|
||||||
|
var n uint64
|
||||||
|
err := conn.QueryRow("select $1::uint64", uint64(42)).Scan(&n)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if n != 42 {
|
||||||
|
t.Fatalf("Expected n to be 42, but was %v", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureConnValid(t, conn)
|
||||||
|
}
|
||||||
|
|||||||
@@ -479,6 +479,55 @@ where (
|
|||||||
SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}),
|
SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}),
|
||||||
}...)
|
}...)
|
||||||
|
|
||||||
|
steps = append(steps, []Step{
|
||||||
|
ExpectMessage(&pgproto3.Parse{
|
||||||
|
Query: "select t.oid, t.typname, t.typbasetype\nfrom pg_type t\n join pg_type base_type on t.typbasetype=base_type.oid\nwhere t.typtype = 'd'\n and base_type.typtype = 'b'",
|
||||||
|
}),
|
||||||
|
ExpectMessage(&pgproto3.Describe{
|
||||||
|
ObjectType: 'S',
|
||||||
|
}),
|
||||||
|
ExpectMessage(&pgproto3.Sync{}),
|
||||||
|
SendMessage(&pgproto3.ParseComplete{}),
|
||||||
|
SendMessage(&pgproto3.ParameterDescription{}),
|
||||||
|
SendMessage(&pgproto3.RowDescription{
|
||||||
|
Fields: []pgproto3.FieldDescription{
|
||||||
|
{Name: "oid",
|
||||||
|
TableOID: 1247,
|
||||||
|
TableAttributeNumber: 65534,
|
||||||
|
DataTypeOID: 26,
|
||||||
|
DataTypeSize: 4,
|
||||||
|
TypeModifier: 4294967295,
|
||||||
|
Format: 0,
|
||||||
|
},
|
||||||
|
{Name: "typname",
|
||||||
|
TableOID: 1247,
|
||||||
|
TableAttributeNumber: 1,
|
||||||
|
DataTypeOID: 19,
|
||||||
|
DataTypeSize: 64,
|
||||||
|
TypeModifier: 4294967295,
|
||||||
|
Format: 0,
|
||||||
|
},
|
||||||
|
{Name: "typbasetype",
|
||||||
|
TableOID: 1247,
|
||||||
|
TableAttributeNumber: 65534,
|
||||||
|
DataTypeOID: 26,
|
||||||
|
DataTypeSize: 4,
|
||||||
|
TypeModifier: 4294967295,
|
||||||
|
Format: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}),
|
||||||
|
ExpectMessage(&pgproto3.Bind{
|
||||||
|
ResultFormatCodes: []int16{1, 1, 1},
|
||||||
|
}),
|
||||||
|
ExpectMessage(&pgproto3.Execute{}),
|
||||||
|
ExpectMessage(&pgproto3.Sync{}),
|
||||||
|
SendMessage(&pgproto3.BindComplete{}),
|
||||||
|
SendMessage(&pgproto3.CommandComplete{CommandTag: "SELECT 0"}),
|
||||||
|
SendMessage(&pgproto3.ReadyForQuery{TxStatus: 'I'}),
|
||||||
|
}...)
|
||||||
|
|
||||||
return steps
|
return steps
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ then
|
|||||||
# of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles.
|
# of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles.
|
||||||
psql -U postgres -c 'create database pgx_test'
|
psql -U postgres -c 'create database pgx_test'
|
||||||
psql -U postgres pgx_test -c 'create extension hstore'
|
psql -U postgres pgx_test -c 'create extension hstore'
|
||||||
|
psql -U postgres pgx_test -c 'create domain uint64 as numeric(20,0)'
|
||||||
psql -U postgres -c "create user pgx_ssl SUPERUSER PASSWORD 'secret'"
|
psql -U postgres -c "create user pgx_ssl SUPERUSER PASSWORD 'secret'"
|
||||||
psql -U postgres -c "create user pgx_md5 SUPERUSER PASSWORD 'secret'"
|
psql -U postgres -c "create user pgx_md5 SUPERUSER PASSWORD 'secret'"
|
||||||
psql -U postgres -c "create user pgx_pw SUPERUSER PASSWORD 'secret'"
|
psql -U postgres -c "create user pgx_pw SUPERUSER PASSWORD 'secret'"
|
||||||
|
|||||||
Reference in New Issue
Block a user