Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Q: How to implement pgtype.FlatArray[uuid.UUID] for gorm? #1781

Closed
loeffel-io opened this issue Oct 26, 2023 · 2 comments
Closed

Q: How to implement pgtype.FlatArray[uuid.UUID] for gorm? #1781

loeffel-io opened this issue Oct 26, 2023 · 2 comments

Comments

@loeffel-io
Copy link

It's unclear for me how to implement this in the most simple way, thank you!

UserIds      pgtype.FlatArray[uuid.UUID] `gorm:"type:uuid[];not null"` // relationship: has many (content.mindful.com/User)

unsupported Scan, storing driver.Value type string into type *pgtype.FlatArray[github.com/google/uuid.UUID]

@jackc
Copy link
Owner

jackc commented Oct 27, 2023

Unfortunately, while it is possible to use pgtype arrays in database/sql directly it is not clear how to make it work with an ORM like gorm.

See #1458 and #1662 for previous discussions.

@loeffel-io
Copy link
Author

thanks @jackc for the response!

i now just implemented the gorm interface only but still using the pgx lib through the gorm postgres driver

for ref:

package uuid_v1

import (
	"database/sql/driver"
	"fmt"
	"github.com/google/uuid"
	"strings"
)

type UUIDSlice []uuid.UUID

// Scan implements the sql.Scanner interface for postgres uuid[] deserialization.
// e.g. single: {23e4aa0c-8591-4544-8cc1-8348d9d7f901}
// e.g. multiple: {23e4aa0c-8591-4544-8cc1-8348d9d7f901,aee911e4-3fa8-4866-95ac-e4878d3e06e5}
func (uuidSlice *UUIDSlice) Scan(value interface{}) error {
	var ok bool
	var err error
	var strValue string

	if strValue, ok = value.(string); !ok {
		return fmt.Errorf("failed to scan UUIDSlice: %v", value)
	}

	if strValue == "" {
		return nil
	}

	var strValueSplit = strings.Split(strValue[1:len(strValue)-1], ",")
	for _, uuidStr := range strValueSplit {
		if uuidStr == "" {
			continue
		}

		var tmpUuid uuid.UUID
		if tmpUuid, err = uuid.Parse(uuidStr); err != nil {
			return err
		}
		*uuidSlice = append(*uuidSlice, tmpUuid)
	}

	return nil
}

// Value implements the driver.Valuer interface for postgres uuid[] serialization.
func (uuidSlice *UUIDSlice) Value() (driver.Value, error) {
	var uuidStrs []string
	for _, u := range *uuidSlice {
		uuidStrs = append(uuidStrs, u.String())
	}

	if len(uuidStrs) == 0 {
		return nil, nil
	}

	return fmt.Sprintf("{%s}", strings.Join(uuidStrs, ",")), nil
}
package uuid_v1

import (
	"database/sql/driver"
	"fmt"
	"github.com/google/uuid"
	"github.com/stretchr/testify/assert"
	"strings"
	"testing"
)

func TestUUIDSlice_Scan(t *testing.T) {
	var tests = []struct {
		name              string
		uuidSlice         UUIDSlice
		value             interface{}
		expectedUuidSlice UUIDSlice
		expectedErr       error
	}{
		{
			name:              "none",
			uuidSlice:         nil,
			value:             "{}",
			expectedUuidSlice: nil,
			expectedErr:       nil,
		},
		{
			name:      "one",
			uuidSlice: nil,
			value:     "{23e4aa0c-8591-4544-8cc1-8348d9d7f901}",
			expectedUuidSlice: UUIDSlice{
				uuid.MustParse("23e4aa0c-8591-4544-8cc1-8348d9d7f901"),
			},
			expectedErr: nil,
		},
		{
			name:      "two",
			uuidSlice: nil,
			value:     "{23e4aa0c-8591-4544-8cc1-8348d9d7f901,aee911e4-3fa8-4866-95ac-e4878d3e06e5}",
			expectedUuidSlice: UUIDSlice{
				uuid.MustParse("23e4aa0c-8591-4544-8cc1-8348d9d7f901"),
				uuid.MustParse("aee911e4-3fa8-4866-95ac-e4878d3e06e5"),
			},
			expectedErr: nil,
		},
		{
			name:      "three",
			uuidSlice: nil,
			value:     "{23e4aa0c-8591-4544-8cc1-8348d9d7f901,aee911e4-3fa8-4866-95ac-e4878d3e06e5,6d285174-30f6-462c-8853-de3bd88d36fb}",
			expectedUuidSlice: UUIDSlice{
				uuid.MustParse("23e4aa0c-8591-4544-8cc1-8348d9d7f901"),
				uuid.MustParse("aee911e4-3fa8-4866-95ac-e4878d3e06e5"),
				uuid.MustParse("6d285174-30f6-462c-8853-de3bd88d36fb"),
			},
			expectedErr: nil,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			var err = test.uuidSlice.Scan(test.value)
			assert.Equal(t, test.expectedErr, err)
			assert.Equal(t, test.expectedUuidSlice, test.uuidSlice)
		})
	}
}

func TestUUIDSlice_Value(t *testing.T) {
	var tests = []struct {
		name          string
		uuidSlice     UUIDSlice
		expectedValue driver.Value
		expectedErr   error
	}{
		{
			name:          "nil",
			uuidSlice:     nil,
			expectedValue: nil,
			expectedErr:   nil,
		},
		{
			name: "one",
			uuidSlice: UUIDSlice{
				uuid.MustParse("23e4aa0c-8591-4544-8cc1-8348d9d7f901"),
			},
			expectedValue: fmt.Sprintf("{%s}", strings.Join([]string{
				"23e4aa0c-8591-4544-8cc1-8348d9d7f901",
			}, ",")),
			expectedErr: nil,
		},
		{
			name: "multiple",
			uuidSlice: UUIDSlice{
				uuid.MustParse("23e4aa0c-8591-4544-8cc1-8348d9d7f901"),
				uuid.MustParse("aee911e4-3fa8-4866-95ac-e4878d3e06e5"),
			},
			expectedValue: fmt.Sprintf("{%s}", strings.Join([]string{
				"23e4aa0c-8591-4544-8cc1-8348d9d7f901",
				"aee911e4-3fa8-4866-95ac-e4878d3e06e5",
			}, ",")),
			expectedErr: nil,
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			var value, err = test.uuidSlice.Value()
			assert.Equal(t, test.expectedErr, err)
			assert.Equal(t, test.expectedValue, value)
		})
	}
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants