mirror of
https://github.com/jackc/pgx.git
synced 2025-05-03 06:00:18 +00:00
It was a mistake to use it in other contexts. This made interop difficult between pacakges that depended on pgtype such as pgx and packages that did not like pgconn and pgproto3. In particular this was awkward for prepared statements. Because pgx depends on pgtype and the tests for pgtype depend on pgx this change will require a couple back and forth commits to get the go.mod dependecies correct.
200 lines
6.1 KiB
Go
200 lines
6.1 KiB
Go
package pgtype_test
|
|
|
|
import (
|
|
"context"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/jackc/pgtype"
|
|
"github.com/jackc/pgtype/testutil"
|
|
"github.com/jackc/pgx/v4"
|
|
)
|
|
|
|
func TestHstoreArrayTranscode(t *testing.T) {
|
|
conn := testutil.MustConnectPgx(t)
|
|
defer testutil.MustCloseContext(t, conn)
|
|
|
|
var hstoreOID uint32
|
|
err := conn.QueryRow(context.Background(), "select t.oid from pg_type t where t.typname='hstore';").Scan(&hstoreOID)
|
|
if err != nil {
|
|
t.Fatalf("did not find hstore OID, %v", err)
|
|
}
|
|
conn.ConnInfo.RegisterDataType(pgtype.DataType{Value: &pgtype.Hstore{}, Name: "hstore", OID: hstoreOID})
|
|
|
|
var hstoreArrayOID uint32
|
|
err = conn.QueryRow(context.Background(), "select t.oid from pg_type t where t.typname='_hstore';").Scan(&hstoreArrayOID)
|
|
if err != nil {
|
|
t.Fatalf("did not find _hstore OID, %v", err)
|
|
}
|
|
conn.ConnInfo.RegisterDataType(pgtype.DataType{Value: &pgtype.HstoreArray{}, Name: "_hstore", OID: hstoreArrayOID})
|
|
|
|
text := func(s string) pgtype.Text {
|
|
return pgtype.Text{String: s, Status: pgtype.Present}
|
|
}
|
|
|
|
values := []pgtype.Hstore{
|
|
{Map: map[string]pgtype.Text{}, Status: pgtype.Present},
|
|
{Map: map[string]pgtype.Text{"foo": text("bar")}, Status: pgtype.Present},
|
|
{Map: map[string]pgtype.Text{"foo": text("bar"), "baz": text("quz")}, Status: pgtype.Present},
|
|
{Map: map[string]pgtype.Text{"NULL": text("bar")}, Status: pgtype.Present},
|
|
{Map: map[string]pgtype.Text{"foo": text("NULL")}, Status: pgtype.Present},
|
|
{Status: pgtype.Null},
|
|
}
|
|
|
|
specialStrings := []string{
|
|
`"`,
|
|
`'`,
|
|
`\`,
|
|
`\\`,
|
|
`=>`,
|
|
` `,
|
|
`\ / / \\ => " ' " '`,
|
|
}
|
|
for _, s := range specialStrings {
|
|
// Special key values
|
|
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s + "foo": text("bar")}, Status: pgtype.Present}) // at beginning
|
|
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s + "bar": text("bar")}, Status: pgtype.Present}) // in middle
|
|
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo" + s: text("bar")}, Status: pgtype.Present}) // at end
|
|
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{s: text("bar")}, Status: pgtype.Present}) // is key
|
|
|
|
// Special value values
|
|
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s + "bar")}, Status: pgtype.Present}) // at beginning
|
|
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s + "bar")}, Status: pgtype.Present}) // in middle
|
|
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text("foo" + s)}, Status: pgtype.Present}) // at end
|
|
values = append(values, pgtype.Hstore{Map: map[string]pgtype.Text{"foo": text(s)}, Status: pgtype.Present}) // is key
|
|
}
|
|
|
|
src := &pgtype.HstoreArray{
|
|
Elements: values,
|
|
Dimensions: []pgtype.ArrayDimension{{Length: int32(len(values)), LowerBound: 1}},
|
|
Status: pgtype.Present,
|
|
}
|
|
|
|
_, err = conn.Prepare(context.Background(), "test", "select $1::hstore[]")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
formats := []struct {
|
|
name string
|
|
formatCode int16
|
|
}{
|
|
{name: "TextFormat", formatCode: pgx.TextFormatCode},
|
|
{name: "BinaryFormat", formatCode: pgx.BinaryFormatCode},
|
|
}
|
|
|
|
for _, fc := range formats {
|
|
queryResultFormats := pgx.QueryResultFormats{fc.formatCode}
|
|
vEncoder := testutil.ForceEncoder(src, fc.formatCode)
|
|
if vEncoder == nil {
|
|
t.Logf("%#v does not implement %v", src, fc.name)
|
|
continue
|
|
}
|
|
|
|
var result pgtype.HstoreArray
|
|
err := conn.QueryRow(context.Background(), "test", queryResultFormats, vEncoder).Scan(&result)
|
|
if err != nil {
|
|
t.Errorf("%v: %v", fc.name, err)
|
|
continue
|
|
}
|
|
|
|
if result.Status != src.Status {
|
|
t.Errorf("%v: expected Status %v, got %v", fc.formatCode, src.Status, result.Status)
|
|
continue
|
|
}
|
|
|
|
if len(result.Elements) != len(src.Elements) {
|
|
t.Errorf("%v: expected %v elements, got %v", fc.formatCode, len(src.Elements), len(result.Elements))
|
|
continue
|
|
}
|
|
|
|
for i := range result.Elements {
|
|
a := src.Elements[i]
|
|
b := result.Elements[i]
|
|
|
|
if a.Status != b.Status {
|
|
t.Errorf("%v element idx %d: expected status %v, got %v", fc.formatCode, i, a.Status, b.Status)
|
|
}
|
|
|
|
if len(a.Map) != len(b.Map) {
|
|
t.Errorf("%v element idx %d: expected %v pairs, got %v", fc.formatCode, i, len(a.Map), len(b.Map))
|
|
}
|
|
|
|
for k := range a.Map {
|
|
if a.Map[k] != b.Map[k] {
|
|
t.Errorf("%v element idx %d: expected key %v to be %v, got %v", fc.formatCode, i, k, a.Map[k], b.Map[k])
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHstoreArraySet(t *testing.T) {
|
|
successfulTests := []struct {
|
|
src []map[string]string
|
|
result pgtype.HstoreArray
|
|
}{
|
|
{
|
|
src: []map[string]string{{"foo": "bar"}},
|
|
result: pgtype.HstoreArray{
|
|
Elements: []pgtype.Hstore{
|
|
{
|
|
Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
|
|
Status: pgtype.Present,
|
|
},
|
|
},
|
|
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
|
Status: pgtype.Present,
|
|
},
|
|
},
|
|
}
|
|
|
|
for i, tt := range successfulTests {
|
|
var dst pgtype.HstoreArray
|
|
err := dst.Set(tt.src)
|
|
if err != nil {
|
|
t.Errorf("%d: %v", i, err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(dst, tt.result) {
|
|
t.Errorf("%d: expected %v to convert to %v, but it was %v", i, tt.src, tt.result, dst)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestHstoreArrayAssignTo(t *testing.T) {
|
|
var m []map[string]string
|
|
|
|
simpleTests := []struct {
|
|
src pgtype.HstoreArray
|
|
dst *[]map[string]string
|
|
expected []map[string]string
|
|
}{
|
|
{
|
|
src: pgtype.HstoreArray{
|
|
Elements: []pgtype.Hstore{
|
|
{
|
|
Map: map[string]pgtype.Text{"foo": {String: "bar", Status: pgtype.Present}},
|
|
Status: pgtype.Present,
|
|
},
|
|
},
|
|
Dimensions: []pgtype.ArrayDimension{{LowerBound: 1, Length: 1}},
|
|
Status: pgtype.Present,
|
|
},
|
|
dst: &m,
|
|
expected: []map[string]string{{"foo": "bar"}}},
|
|
{src: pgtype.HstoreArray{Status: pgtype.Null}, dst: &m, expected: (([]map[string]string)(nil))},
|
|
}
|
|
|
|
for i, tt := range simpleTests {
|
|
err := tt.src.AssignTo(tt.dst)
|
|
if err != nil {
|
|
t.Errorf("%d: %v", i, err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(*tt.dst, tt.expected) {
|
|
t.Errorf("%d: expected %v to assign %v, but result was %v", i, tt.src, tt.expected, *tt.dst)
|
|
}
|
|
}
|
|
}
|