From f3793914ed3719f7c301e402d5a44e4137910bfc Mon Sep 17 00:00:00 2001 From: Robert Farr Date: Thu, 23 Jan 2025 09:16:33 -0600 Subject: [PATCH] handle arrays of query appender structs --- dialect/pgdialect/array.go | 3 ++- dialect/pgdialect/sqltype.go | 4 +++ internal/dbtest/pg_test.go | 50 ++++++++++++++++++++++++++++-------- 3 files changed, 45 insertions(+), 12 deletions(-) diff --git a/dialect/pgdialect/array.go b/dialect/pgdialect/array.go index f6d10c413..c9e5e33b7 100644 --- a/dialect/pgdialect/array.go +++ b/dialect/pgdialect/array.go @@ -133,7 +133,8 @@ func (d *Dialect) arrayAppender(typ reflect.Type) schema.AppenderFunc { elemType = elemType.Elem() } - if elemType.Kind() == reflect.Struct { + if elemType.Kind() == reflect.Struct && !elemType.Implements(queryAppenderType) { + //Underlying type should be of JSONB, so need for format a JSON array brackets = "[]" } } diff --git a/dialect/pgdialect/sqltype.go b/dialect/pgdialect/sqltype.go index 99075cbc1..121a3d691 100644 --- a/dialect/pgdialect/sqltype.go +++ b/dialect/pgdialect/sqltype.go @@ -86,6 +86,10 @@ func fieldSQLType(field *schema.Field) string { } func sqlType(typ reflect.Type) string { + if typ.Kind() == reflect.Ptr { + typ = typ.Elem() + } + switch typ { case nullStringType: // typ.Kind() == reflect.Struct, test for exact match return sqltype.VarChar diff --git a/internal/dbtest/pg_test.go b/internal/dbtest/pg_test.go index 3fb2d5832..e67b9f5bd 100644 --- a/internal/dbtest/pg_test.go +++ b/internal/dbtest/pg_test.go @@ -17,6 +17,7 @@ import ( "github.com/uptrace/bun" "github.com/uptrace/bun/dialect/pgdialect" "github.com/uptrace/bun/driver/pgdriver" + "github.com/uptrace/bun/schema" ) func TestPostgresArray(t *testing.T) { @@ -25,7 +26,7 @@ func TestPostgresArray(t *testing.T) { Array1 []string `bun:",array"` Array2 *[]string `bun:",array"` Array3 *[]string `bun:",array"` - Array4 []*string `bun:"type:text[]"` + Array4 []*string `bun:",array"` } db := pg(t) @@ -888,16 +889,40 @@ func TestPostgresMultiRange(t *testing.T) { require.NoError(t, err) } +type UserID struct { + ID string +} + +func (u UserID) AppendQuery(fmter schema.Formatter, b []byte) ([]byte, error) { + v := []byte(`"` + u.ID + `"`) + return append(b, v...), nil +} + +var _ schema.QueryAppender = (*UserID)(nil) + +func (r *UserID) Scan(anySrc any) (err error) { + src, ok := anySrc.([]byte) + if !ok { + return fmt.Errorf("pgdialect: Range can't scan %T", anySrc) + } + + r.ID = string(src) + return nil +} + +var _ sql.Scanner = (*UserID)(nil) + func TestPostgresJSONB(t *testing.T) { type Item struct { Name string `json:"name"` } type Model struct { - ID int64 `bun:",pk,autoincrement"` - Item Item `bun:",type:jsonb"` - ItemPtr *Item `bun:",type:jsonb"` - Items []Item `bun:",type:jsonb,array"` - ItemsP []*Item `bun:",type:jsonb,array"` + ID int64 `bun:",pk,autoincrement"` + Item Item `bun:",type:jsonb"` + ItemPtr *Item `bun:",type:jsonb"` + Items []Item `bun:",type:jsonb,array"` + ItemsP []*Item `bun:",type:jsonb,array"` + TextItemA []UserID `bun:"type:text[]"` } db := pg(t) @@ -906,12 +931,15 @@ func TestPostgresJSONB(t *testing.T) { item1 := Item{Name: "one"} item2 := Item{Name: "two"} + uid1 := UserID{ID: "1"} + uid2 := UserID{ID: "2"} model1 := &Model{ - ID: 123, - Item: item1, - ItemPtr: &item2, - Items: []Item{item1, item2}, - ItemsP: []*Item{&item1, &item2}, + ID: 123, + Item: item1, + ItemPtr: &item2, + Items: []Item{item1, item2}, + ItemsP: []*Item{&item1, &item2}, + TextItemA: []UserID{uid1, uid2}, } _, err := db.NewInsert().Model(model1).Exec(ctx) require.NoError(t, err)