From 5dace165f5f3bd882e579e69b9574597b7abadc4 Mon Sep 17 00:00:00 2001 From: Jack Christensen Date: Sat, 12 Jul 2014 21:40:48 -0500 Subject: [PATCH] Add example custom type --- README.md | 2 +- example_custom_type_test.go | 99 +++++++++++++++++++++++++++++++++++++ 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 example_custom_type_test.go diff --git a/README.md b/README.md index 8b4106cb..ae8d1a8f 100644 --- a/README.md +++ b/README.md @@ -145,7 +145,7 @@ added for additional types like point, hstore, numeric, etc. that do not have direct mappings in Go by the types implementing Scanner, TextEncoder, and optionally BinaryEncoder. To enable binary format for custom types, a prepared statement must be used and the field description of the returned field must have -FormatCode set to BinaryFormatCode. See example_value_transcoder_test.go for an +FormatCode set to BinaryFormatCode. See example_custom_type_test.go for an example of a custom type for the PostgreSQL point type. ### Null Mapping diff --git a/example_custom_type_test.go b/example_custom_type_test.go new file mode 100644 index 00000000..9e623faa --- /dev/null +++ b/example_custom_type_test.go @@ -0,0 +1,99 @@ +package pgx_test + +import ( + "errors" + "fmt" + "github.com/jackc/pgx" + "regexp" + "strconv" +) + +var pointRegexp *regexp.Regexp = regexp.MustCompile(`^\((.*),(.*)\)$`) + +// NullPoint represents a point that may be null. +// +// If Valid is false then the value is NULL. +type NullPoint struct { + X, Y float64 // Coordinates of point + Valid bool // Valid is true if not NULL +} + +const pointOid = 600 + +func (p *NullPoint) Scan(vr *pgx.ValueReader) error { + if vr.Type().DataType != pointOid { + return pgx.SerializationError(fmt.Sprintf("NullPoint.Scan cannot decode OID %d", vr.Type().DataType)) + } + + if vr.Len() == -1 { + p.X, p.Y, p.Valid = 0, 0, false + return nil + } + + switch vr.Type().FormatCode { + case pgx.TextFormatCode: + s := vr.ReadString(vr.Len()) + match := pointRegexp.FindStringSubmatch(s) + if match == nil { + return pgx.SerializationError(fmt.Sprintf("Received invalid point: %v", s)) + } + + var err error + p.X, err = strconv.ParseFloat(match[1], 64) + if err != nil { + return pgx.SerializationError(fmt.Sprintf("Received invalid point: %v", s)) + } + p.Y, err = strconv.ParseFloat(match[2], 64) + if err != nil { + return pgx.SerializationError(fmt.Sprintf("Received invalid point: %v", s)) + } + case pgx.BinaryFormatCode: + return errors.New("binary format not implemented") + default: + return fmt.Errorf("unknown format %v", vr.Type().FormatCode) + } + + p.Valid = true + return vr.Err() +} + +func (p NullPoint) EncodeText() (string, byte, error) { + if p.Valid { + return fmt.Sprintf("point(%v,%v)", p.X, p.Y), pgx.SafeText, nil + } else { + return "", pgx.NullText, nil + } +} + +func (p NullPoint) String() string { + if p.Valid { + return fmt.Sprintf("%v, %v", p.X, p.Y) + } + return "null point" +} + +func Example_CustomType() { + conn, err := pgx.Connect(*defaultConnConfig) + if err != nil { + fmt.Printf("Unable to establish connection: %v", err) + return + } + + var p NullPoint + err = conn.QueryRow("select null::point").Scan(&p) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(p) + + err = conn.QueryRow("select point(1.5,2.5)").Scan(&p) + if err != nil { + fmt.Println(err) + return + } + fmt.Println(p) + // Output: + // null point + // 1.5, 2.5 +}