Fs02/grimoire

View on GitHub
adapter/postgres/postgres.go

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
// Package postgres wraps postgres (pq) driver as an adapter for grimoire.
//
// Usage:
//    // open postgres connection.
//    adapter, err := postgres.Open("postgres://postgres@localhost/grimoire_test?sslmode=disable")
//    if err != nil {
//        panic(err)
//    }
//    defer adapter.Close()
//
//    // initialize grimoire's repo.
//    repo := grimoire.New(adapter)
package postgres

import (
    db "database/sql"

    "github.com/Fs02/grimoire"
    "github.com/Fs02/grimoire/adapter/sql"
    "github.com/Fs02/grimoire/errors"
    "github.com/Fs02/grimoire/internal"
    "github.com/lib/pq"
)

// Adapter definition for postgrees database.
type Adapter struct {
    *sql.Adapter
}

var _ grimoire.Adapter = (*Adapter)(nil)

// Open postgrees connection using dsn.
func Open(dsn string) (*Adapter, error) {
    var err error

    adapter := &Adapter{
        Adapter: &sql.Adapter{
            Config: &sql.Config{
                Placeholder:         "$",
                EscapeChar:          "\"",
                Ordinal:             true,
                InsertDefaultValues: true,
                ErrorFunc:           errorFunc,
            },
        },
    }
    adapter.DB, err = db.Open("postgres", dsn)

    return adapter, err
}

// Insert inserts a record to database and returns its id.
func (adapter *Adapter) Insert(query grimoire.Query, changes map[string]interface{}, loggers ...grimoire.Logger) (interface{}, error) {
    statement, args := sql.NewBuilder(adapter.Config).Returning("id").Insert(query.Collection, changes)

    var result struct {
        ID int64
    }

    _, err := adapter.Query(&result, statement, args, loggers...)
    return result.ID, err
}

// InsertAll inserts multiple records to database and returns its ids.
func (adapter *Adapter) InsertAll(query grimoire.Query, fields []string, allchanges []map[string]interface{}, loggers ...grimoire.Logger) ([]interface{}, error) {
    statement, args := sql.NewBuilder(adapter.Config).Returning("id").InsertAll(query.Collection, fields, allchanges)

    var result []struct {
        ID int64
    }

    _, err := adapter.Query(&result, statement, args, loggers...)

    ids := make([]interface{}, 0, len(result))
    for _, r := range result {
        ids = append(ids, r.ID)
    }

    return ids, err
}

// Begin begins a new transaction.
func (adapter *Adapter) Begin() (grimoire.Adapter, error) {
    newAdapter, err := adapter.Adapter.Begin()

    return &Adapter{
        Adapter: newAdapter.(*sql.Adapter),
    }, err
}

func errorFunc(err error) error {
    if err == nil {
        return nil
    }

    if e, ok := err.(*pq.Error); ok {
        switch e.Code {
        case "23505":
            return errors.New(e.Message, internal.ExtractString(e.Message, "constraint \"", "\""), errors.UniqueConstraint)
        case "23503":
            return errors.New(e.Message, internal.ExtractString(e.Message, "constraint \"", "\""), errors.ForeignKeyConstraint)
        case "23514":
            return errors.New(e.Message, internal.ExtractString(e.Message, "constraint \"", "\""), errors.CheckConstraint)
        }
    }

    return err
}