Merge branch 'manniwood-add-aclitem'
* manniwood-add-aclitem: Adds note on why tricky test user has to actually exist Adds a tricky user to test Adds NullAclItem
This commit is contained in:
@@ -29,6 +29,8 @@ env:
|
|||||||
- PGVERSION=9.2
|
- PGVERSION=9.2
|
||||||
- PGVERSION=9.1
|
- PGVERSION=9.1
|
||||||
|
|
||||||
|
# The tricky test user, below, has to actually exist so that it can be used in a test
|
||||||
|
# of aclitem formatting. It turns out aclitems cannot contain non-existing users/roles.
|
||||||
before_script:
|
before_script:
|
||||||
- mv conn_config_test.go.travis conn_config_test.go
|
- mv conn_config_test.go.travis conn_config_test.go
|
||||||
- psql -U postgres -c 'create database pgx_test'
|
- psql -U postgres -c 'create database pgx_test'
|
||||||
@@ -36,6 +38,7 @@ before_script:
|
|||||||
- 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'"
|
||||||
|
- psql -U postgres -c "create user \" tricky, ' } \"\" \\ test user \" superuser password 'secret'"
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- go get -u github.com/shopspring/decimal
|
- go get -u github.com/shopspring/decimal
|
||||||
|
|||||||
@@ -66,6 +66,7 @@ To setup the normal test environment, first install these dependencies:
|
|||||||
Then run the following SQL:
|
Then run the following SQL:
|
||||||
|
|
||||||
create user pgx_md5 password 'secret';
|
create user pgx_md5 password 'secret';
|
||||||
|
create user " tricky, ' } "" \ test user " superuser password 'secret';
|
||||||
create database pgx_test;
|
create database pgx_test;
|
||||||
|
|
||||||
Connect to database pgx_test and run:
|
Connect to database pgx_test and run:
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ const (
|
|||||||
Int8ArrayOid = 1016
|
Int8ArrayOid = 1016
|
||||||
Float4ArrayOid = 1021
|
Float4ArrayOid = 1021
|
||||||
Float8ArrayOid = 1022
|
Float8ArrayOid = 1022
|
||||||
|
AclItemOid = 1033
|
||||||
InetArrayOid = 1041
|
InetArrayOid = 1041
|
||||||
VarcharOid = 1043
|
VarcharOid = 1043
|
||||||
DateOid = 1082
|
DateOid = 1082
|
||||||
@@ -89,6 +90,7 @@ func init() {
|
|||||||
"_timestamp": BinaryFormatCode,
|
"_timestamp": BinaryFormatCode,
|
||||||
"_timestamptz": BinaryFormatCode,
|
"_timestamptz": BinaryFormatCode,
|
||||||
"_varchar": BinaryFormatCode,
|
"_varchar": BinaryFormatCode,
|
||||||
|
"aclitem": TextFormatCode, // Pg's src/backend/utils/adt/acl.c has only in/out (text) not send/recv (bin)
|
||||||
"bool": BinaryFormatCode,
|
"bool": BinaryFormatCode,
|
||||||
"bytea": BinaryFormatCode,
|
"bytea": BinaryFormatCode,
|
||||||
"char": BinaryFormatCode,
|
"char": BinaryFormatCode,
|
||||||
@@ -263,6 +265,58 @@ func (n NullString) Encode(w *WriteBuf, oid Oid) error {
|
|||||||
return encodeString(w, oid, n.String)
|
return encodeString(w, oid, n.String)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AclItem is used for PostgreSQL's aclitem data type. A sample aclitem
|
||||||
|
// might look like this:
|
||||||
|
//
|
||||||
|
// postgres=arwdDxt/postgres
|
||||||
|
//
|
||||||
|
// Note, however, that because the user/role name part of an aclitem is
|
||||||
|
// an identifier, it follows all the usual formatting rules for SQL
|
||||||
|
// identifiers: if it contains spaces and other special characters,
|
||||||
|
// it should appear in double-quotes:
|
||||||
|
//
|
||||||
|
// postgres=arwdDxt/"role with spaces"
|
||||||
|
//
|
||||||
|
type AclItem string
|
||||||
|
|
||||||
|
// NullAclItem represents a pgx.AclItem that may be null. NullAclItem implements the
|
||||||
|
// Scanner and Encoder interfaces so it may be used both as an argument to
|
||||||
|
// Query[Row] and a destination for Scan for prepared and unprepared queries.
|
||||||
|
//
|
||||||
|
// If Valid is false then the value is NULL.
|
||||||
|
type NullAclItem struct {
|
||||||
|
AclItem AclItem
|
||||||
|
Valid bool // Valid is true if AclItem is not NULL
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NullAclItem) Scan(vr *ValueReader) error {
|
||||||
|
if vr.Type().DataType != AclItemOid {
|
||||||
|
return SerializationError(fmt.Sprintf("NullAclItem.Scan cannot decode OID %d", vr.Type().DataType))
|
||||||
|
}
|
||||||
|
|
||||||
|
if vr.Len() == -1 {
|
||||||
|
n.AclItem, n.Valid = "", false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
n.Valid = true
|
||||||
|
n.AclItem = AclItem(decodeText(vr))
|
||||||
|
return vr.Err()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Particularly important to return TextFormatCode, seeing as Postgres
|
||||||
|
// only ever sends aclitem as text, not binary.
|
||||||
|
func (n NullAclItem) FormatCode() int16 { return TextFormatCode }
|
||||||
|
|
||||||
|
func (n NullAclItem) Encode(w *WriteBuf, oid Oid) error {
|
||||||
|
if !n.Valid {
|
||||||
|
w.WriteInt32(-1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return encodeString(w, oid, string(n.AclItem))
|
||||||
|
}
|
||||||
|
|
||||||
// Name is a type used for PostgreSQL's special 63-byte
|
// Name is a type used for PostgreSQL's special 63-byte
|
||||||
// name data type, used for identifiers like table names.
|
// name data type, used for identifiers like table names.
|
||||||
// The pg_class.relname column is a good example of where the
|
// The pg_class.relname column is a good example of where the
|
||||||
@@ -964,6 +1018,10 @@ func Encode(wbuf *WriteBuf, oid Oid, arg interface{}) error {
|
|||||||
return encodeUInt(wbuf, oid, arg)
|
return encodeUInt(wbuf, oid, arg)
|
||||||
case Char:
|
case Char:
|
||||||
return encodeChar(wbuf, oid, arg)
|
return encodeChar(wbuf, oid, arg)
|
||||||
|
case AclItem:
|
||||||
|
// The aclitem data type goes over the wire using the same format as string,
|
||||||
|
// so just cast to string and use encodeString
|
||||||
|
return encodeString(wbuf, oid, string(arg))
|
||||||
case Name:
|
case Name:
|
||||||
// The name data type goes over the wire using the same format as string,
|
// The name data type goes over the wire using the same format as string,
|
||||||
// so just cast to string and use encodeString
|
// so just cast to string and use encodeString
|
||||||
@@ -1146,6 +1204,9 @@ func Decode(vr *ValueReader, d interface{}) error {
|
|||||||
*v = uint64(n)
|
*v = uint64(n)
|
||||||
case *Char:
|
case *Char:
|
||||||
*v = decodeChar(vr)
|
*v = decodeChar(vr)
|
||||||
|
case *AclItem:
|
||||||
|
// aclitem goes over the wire just like text
|
||||||
|
*v = AclItem(decodeText(vr))
|
||||||
case *Name:
|
case *Name:
|
||||||
// name goes over the wire just like text
|
// name goes over the wire just like text
|
||||||
*v = Name(decodeText(vr))
|
*v = Name(decodeText(vr))
|
||||||
|
|||||||
@@ -562,6 +562,7 @@ func TestNullX(t *testing.T) {
|
|||||||
i16 pgx.NullInt16
|
i16 pgx.NullInt16
|
||||||
i32 pgx.NullInt32
|
i32 pgx.NullInt32
|
||||||
c pgx.NullChar
|
c pgx.NullChar
|
||||||
|
a pgx.NullAclItem
|
||||||
n pgx.NullName
|
n pgx.NullName
|
||||||
oid pgx.NullOid
|
oid pgx.NullOid
|
||||||
xid pgx.NullXid
|
xid pgx.NullXid
|
||||||
@@ -599,6 +600,10 @@ func TestNullX(t *testing.T) {
|
|||||||
{"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 255, Valid: true}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 255, Valid: true}}},
|
{"select $1::\"char\"", []interface{}{pgx.NullChar{Char: 255, Valid: true}}, []interface{}{&actual.c}, allTypes{c: pgx.NullChar{Char: 255, Valid: true}}},
|
||||||
{"select $1::name", []interface{}{pgx.NullName{Name: "foo", Valid: true}}, []interface{}{&actual.n}, allTypes{n: pgx.NullName{Name: "foo", Valid: true}}},
|
{"select $1::name", []interface{}{pgx.NullName{Name: "foo", Valid: true}}, []interface{}{&actual.n}, allTypes{n: pgx.NullName{Name: "foo", Valid: true}}},
|
||||||
{"select $1::name", []interface{}{pgx.NullName{Name: "foo", Valid: false}}, []interface{}{&actual.n}, allTypes{n: pgx.NullName{Name: "", Valid: false}}},
|
{"select $1::name", []interface{}{pgx.NullName{Name: "foo", Valid: false}}, []interface{}{&actual.n}, allTypes{n: pgx.NullName{Name: "", Valid: false}}},
|
||||||
|
{"select $1::aclitem", []interface{}{pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: true}}, []interface{}{&actual.a}, allTypes{a: pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: true}}},
|
||||||
|
{"select $1::aclitem", []interface{}{pgx.NullAclItem{AclItem: "postgres=arwdDxt/postgres", Valid: false}}, []interface{}{&actual.a}, allTypes{a: pgx.NullAclItem{AclItem: "", Valid: false}}},
|
||||||
|
// A tricky (and valid) aclitem can still be used, especially with Go's useful backticks
|
||||||
|
{"select $1::aclitem", []interface{}{pgx.NullAclItem{AclItem: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}}, []interface{}{&actual.a}, allTypes{a: pgx.NullAclItem{AclItem: `postgres=arwdDxt/" tricky, ' } "" \ test user "`, Valid: true}}},
|
||||||
{"select $1::cid", []interface{}{pgx.NullCid{Cid: 1, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 1, Valid: true}}},
|
{"select $1::cid", []interface{}{pgx.NullCid{Cid: 1, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 1, Valid: true}}},
|
||||||
{"select $1::cid", []interface{}{pgx.NullCid{Cid: 1, Valid: false}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 0, Valid: false}}},
|
{"select $1::cid", []interface{}{pgx.NullCid{Cid: 1, Valid: false}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 0, Valid: false}}},
|
||||||
{"select $1::cid", []interface{}{pgx.NullCid{Cid: 4294967295, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 4294967295, Valid: true}}},
|
{"select $1::cid", []interface{}{pgx.NullCid{Cid: 4294967295, Valid: true}}, []interface{}{&actual.cid}, allTypes{cid: pgx.NullCid{Cid: 4294967295, Valid: true}}},
|
||||||
|
|||||||
Reference in New Issue
Block a user