composite.go 1.8 KB
package orm

import (
	"fmt"
	"reflect"

	"github.com/go-pg/pg/types"
)

func compositeScanner(typ reflect.Type) types.ScannerFunc {
	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
	}

	var table *Table
	return func(v reflect.Value, rd types.Reader, n int) error {
		if !v.CanSet() {
			return fmt.Errorf("pg: Scan(nonsettable %s)", v.Type())
		}

		if n == -1 {
			v.Set(reflect.Zero(v.Type()))
			return nil
		}

		if table == nil {
			table = GetTable(typ)
		}
		if v.Kind() == reflect.Ptr {
			if v.IsNil() {
				v.Set(reflect.New(v.Type().Elem()))
			}
			v = v.Elem()
		}

		p := newCompositeParser(rd)
		var elemReader *types.BytesReader

		var firstErr error
		for i := 0; ; i++ {
			elem, err := p.NextElem()
			if err != nil {
				if err == endOfComposite {
					break
				}
				return err
			}

			if i >= len(table.Fields) {
				if firstErr == nil {
					firstErr = fmt.Errorf(
						"%s has %d fields, but composite at least %d values",
						table, len(table.Fields), i)
				}
				continue
			}

			if elemReader == nil {
				elemReader = types.NewBytesReader(elem)
			} else {
				elemReader.Reset(elem)
			}

			field := table.Fields[i]
			if elem == nil {
				err = field.ScanValue(v, elemReader, -1)
			} else {
				err = field.ScanValue(v, elemReader, len(elem))
			}
			if err != nil && firstErr == nil {
				firstErr = err
			}
		}

		return firstErr
	}
}

func compositeAppender(typ reflect.Type) types.AppenderFunc {
	if typ.Kind() == reflect.Ptr {
		typ = typ.Elem()
	}

	var table *Table
	return func(b []byte, v reflect.Value, quote int) []byte {
		if table == nil {
			table = GetTable(typ)
		}
		if v.Kind() == reflect.Ptr {
			v = v.Elem()
		}

		b = append(b, "ROW("...)
		for i, f := range table.Fields {
			if i > 0 {
				b = append(b, ',')
			}
			b = f.AppendValue(b, v, quote)
		}
		b = append(b, ')')
		return b
	}
}