Skip to content

nex-prospect/go-postgres-types

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

2 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Go PostgreSQL Types

Go Reference Go Report Card License: MIT

Custom Go types for seamless PostgreSQL array handling with GORM and database/sql.

๐Ÿš€ Features

  • UUIDArray: Proper handling of PostgreSQL UUID arrays (uuid[])
  • Full sql.Scanner and driver.Valuer implementation
  • Works with GORM's db.Raw().Scan() methods
  • Zero external dependencies (except google/uuid and test libs)
  • Comprehensive test coverage
  • Helper methods for common operations

๐Ÿ“ฆ Installation

go get github.com/nex-prospect/go-postgres-types

๐ŸŽฏ The Problem

When using GORM with raw SQL queries that return PostgreSQL arrays, you'll encounter this error:

[error] failed to parse field: TagIDs, error: unsupported data type: &[]

This happens because PostgreSQL returns arrays in the format {uuid1,uuid2,uuid3}, but Go's standard []uuid.UUID type cannot scan this format directly.

โœ… The Solution

Use postgres.UUIDArray instead:

import "github.com/nex-prospect/go-postgres-types/postgres"

type Conversation struct {
    LeadID     uuid.UUID         `json:"lead_id"`
    LeadTagIDs postgres.UUIDArray `json:"lead_tag_ids,omitempty"`
}

๐Ÿ“– Usage Examples

Basic Usage with GORM Raw Queries

package main

import (
    "github.com/nex-prospect/go-postgres-types/postgres"
    "github.com/google/uuid"
    "gorm.io/gorm"
)

type User struct {
    ID      uuid.UUID         `json:"id"`
    Name    string            `json:"name"`
    TagIDs  postgres.UUIDArray `json:"tag_ids"`
}

func GetUsersWithTags(db *gorm.DB) ([]User, error) {
    var users []User

    query := `
        SELECT
            u.id,
            u.name,
            COALESCE(array_agg(ut.tag_id), ARRAY[]::uuid[]) as tag_ids
        FROM users u
        LEFT JOIN user_tags ut ON u.id = ut.user_id
        GROUP BY u.id, u.name
    `

    err := db.Raw(query).Scan(&users).Error
    return users, err
}

Working with Empty Arrays

// PostgreSQL returns empty arrays as {}
query := `
    SELECT
        id,
        COALESCE(tag_ids, ARRAY[]::uuid[]) as tag_ids
    FROM users
`

var user User
db.Raw(query).Scan(&user)
// user.TagIDs will be an empty postgres.UUIDArray, not nil

Inserting/Updating with UUIDArray

func UpdateUserTags(db *gorm.DB, userID uuid.UUID, tagIDs postgres.UUIDArray) error {
    return db.Exec(
        "UPDATE users SET tag_ids = ? WHERE id = ?",
        tagIDs,
        userID,
    ).Error
}

Helper Methods

tags := postgres.UUIDArray{
    uuid.MustParse("550e8400-e29b-41d4-a716-446655440000"),
    uuid.MustParse("6ba7b810-9dad-11d1-80b4-00c04fd430c8"),
}

// Check if contains a UUID
if tags.Contains(someUUID) {
    fmt.Println("Tag found!")
}

// Get length
fmt.Printf("Number of tags: %d\n", tags.Len())

// Check if empty
if tags.IsEmpty() {
    fmt.Println("No tags")
}

// Convert to regular []uuid.UUID slice
regularSlice := tags.ToUUIDSlice()

// Convert from regular []uuid.UUID slice
tags = postgres.FromUUIDSlice(regularSlice)

๐Ÿ”ง PostgreSQL Query Examples

Aggregate UUIDs into Array

-- Basic aggregation
SELECT
    lead_id,
    array_agg(tag_id) as tag_ids
FROM lead_tags
GROUP BY lead_id;

-- With COALESCE for NULL handling
SELECT
    l.id as lead_id,
    COALESCE(array_agg(lt.tag_id), ARRAY[]::uuid[]) as tag_ids
FROM leads l
LEFT JOIN lead_tags lt ON l.id = lt.lead_id
GROUP BY l.id;

-- Filter out NULLs in aggregation
SELECT
    lead_id,
    array_agg(tag_id) FILTER (WHERE tag_id IS NOT NULL) as tag_ids
FROM lead_tags
GROUP BY lead_id;

CTE with Array Aggregation

WITH lead_tags AS (
    SELECT
        lead_id,
        array_agg(tag_id) as tag_ids
    FROM lead_tags
    GROUP BY lead_id
)
SELECT
    l.id,
    l.name,
    COALESCE(lt.tag_ids, ARRAY[]::uuid[]) as tag_ids
FROM leads l
LEFT JOIN lead_tags lt ON l.id = lt.lead_id;

๐Ÿงช Testing

Run tests with coverage:

go test -v -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out

Run benchmarks:

go test -bench=. -benchmem

๐Ÿ“Š Benchmarks

BenchmarkUUIDArray_Scan_Empty-8       5000000    250 ns/op    64 B/op    2 allocs/op
BenchmarkUUIDArray_Scan_Single-8      2000000    850 ns/op   128 B/op    4 allocs/op
BenchmarkUUIDArray_Scan_Multiple-8    1000000   1850 ns/op   256 B/op    6 allocs/op
BenchmarkUUIDArray_Value_Multiple-8   3000000    450 ns/op   192 B/op    5 allocs/op

๐Ÿค Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add some amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

๐Ÿ“ License

This project is licensed under the MIT License - see the LICENSE file for details.

๐Ÿ› Known Issues

None at the moment. If you find a bug, please open an issue!

๐Ÿ”ฎ Future Types (Planned)

  • StringArray for text[] PostgreSQL arrays
  • IntArray for integer[] PostgreSQL arrays
  • JSONBArray for jsonb[] PostgreSQL arrays
  • TimestampArray for timestamp[] PostgreSQL arrays

๐Ÿ’ก Why This Library?

This library was created to solve a common pain point when working with PostgreSQL arrays in Go:

  1. GORM + Raw Queries: When using db.Raw().Scan(), GORM cannot automatically convert PostgreSQL array formats
  2. Type Safety: Standard []uuid.UUID doesn't implement the necessary scanner interfaces
  3. Nil Handling: Proper handling of NULL and empty arrays from PostgreSQL
  4. Developer Experience: Simple drop-in replacement with helpful utility methods

๐Ÿ™ Acknowledgments

Created while solving real-world problems in microservice architectures at Nex Prospect.

๐Ÿ“ง Contact


Made with โค๏ธ for the Go and PostgreSQL community

About

Custom Go types for seamless PostgreSQL array handling with GORM

Resources

License

Contributing

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published