Add documentation
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
// Package pgtype converts between Go and PostgreSQL values.
|
||||
/*
|
||||
The primary type is the Map type. It is a map of PostgreSQL types identified by OID (object ID) to a Codec. A Codec is
|
||||
responsible for converting between Go and PostgreSQL values. NewMap creates a Map with all supported standard PostgreSQL
|
||||
types already registered. Additional types can be registered with Map.RegisterType.
|
||||
|
||||
Use Map.Scan and Map.Encode to decode PostgreSQL values to Go and encode Go values to PostgreSQL respectively.
|
||||
|
||||
JSON Support
|
||||
|
||||
pgtype automatically marshals and unmarshals data from json and jsonb PostgreSQL types.
|
||||
|
||||
Array Support
|
||||
|
||||
ArrayCodec implements support for arrays. If pgtype supports type T then it can easily support []T by registering an
|
||||
ArrayCodec for the appropriate PostgreSQL OID.
|
||||
|
||||
Composite Support
|
||||
|
||||
CompositeCodec implements support for PostgreSQL composite types. Go structs can be scanned into if the public fields of
|
||||
the struct are in the exact order and type of the PostgreSQL type or by implementing CompositeIndexScanner and
|
||||
CompositeIndexGetter.
|
||||
|
||||
Enum Support
|
||||
|
||||
PostgreSQL enums can usually be treated as text. However, EnumCodec implements support for interning strings which can reduce memory usage.
|
||||
|
||||
Array, Composite, and Enum Type Registration
|
||||
|
||||
Array, composite, and enum types can be easily registered from a pgx.Conn with the LoadType method.
|
||||
|
||||
Extending Existing Type Support
|
||||
|
||||
Generally, all Codecs will support interfaces that can be implemented to enable scanning and encoding. For example,
|
||||
PointCodec can use any Go type that implements the PointScanner and PointValuer interfaces. So rather than use
|
||||
pgtype.Point and application can directly use its own point type with pgtype as long as it implements those interfaces.
|
||||
|
||||
Sometimes pgx supports a PostgreSQL type such as numeric but the Go type is in an external package that does not have
|
||||
pgx support such as github.com/shopspring/decimal. These types can be registered with pgtype with custom conversion
|
||||
logic. See https://github.com/jackc/pgx-shopspring-decimal and https://github.com/jackc/pgx-gofrs-uuid for a example
|
||||
integrations.
|
||||
|
||||
Entirely New Type Support
|
||||
|
||||
If the PostgreSQL type is not already supported then an OID / Codec mapping can be registered with Map.RegisterType.
|
||||
There is no difference between a Codec defined and registered by the application and a Codec built in to pgtype. See any
|
||||
of the Codecs in pgtype for Codec examples and for examples of type registration.
|
||||
|
||||
Encoding Unknown Types
|
||||
|
||||
pgtype works best when the OID of the PostgreSQL type is known. But in some cases such as using the simple protocol the
|
||||
OID is unknown. In this case Map.RegisterDefaultPgType can be used to register an assumed OID for a particular Go type.
|
||||
|
||||
Overview of Scanning Implementation
|
||||
|
||||
The first step is to use the OID to lookup the correct Codec. If the OID is unavailable, Map will try to find the OID
|
||||
from previous calls of Map.RegisterDefaultPgType. The Map will call the Codec's PlanScan method to get a plan for
|
||||
scanning into the Go value. A Codec will support scanning into one or more Go types. Oftentime these Go types are
|
||||
interfaces rather than explicit types. For example, PointCodec can use any Go type that implments the PointScanner and
|
||||
PointValuer interfaces.
|
||||
|
||||
If a Go value is not supported directly by a Codec then Map will try wrapping it with additional logic and try again.
|
||||
For example, Int8Codec does not support scanning into a renamed type (e.g. type myInt64 int64). But Map will detect that
|
||||
myInt64 is a renamed type and create a plan that converts the value to the underlying int64 type and then passes that to
|
||||
the Codec (see TryFindUnderlyingTypeScanPlan).
|
||||
|
||||
These plan wrappers are contained in Map.TryWrapScanPlanFuncs. By default these contain shared logic to handle renamed
|
||||
types, pointers to pointers, slices, composite types, etc. Additional plan wrappers can be added to seamlessly integrate
|
||||
types that do not support pgx directly. For example, the before mentioned
|
||||
https://github.com/jackc/pgx-shopspring-decimal package detects decimal.Decimal values, wraps them in something
|
||||
implementing NumericScanner and passes that to the Codec.
|
||||
|
||||
Map.Scan and Map.Encode are convenience methods that wrap Map.PlanScan and Map.PlanEncode. Determining how to scan or
|
||||
encode a particular type may be a time consuming operation. Hence the planning and execution steps of a conversion are
|
||||
internally separated.
|
||||
*/
|
||||
package pgtype
|
||||
@@ -0,0 +1,75 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
"github.com/jackc/pgx/v5/pgtype"
|
||||
)
|
||||
|
||||
// Point represents a point that may be null.
|
||||
type Point struct {
|
||||
X, Y float32 // Coordinates of point
|
||||
Valid bool
|
||||
}
|
||||
|
||||
func (p *Point) ScanPoint(v pgtype.Point) error {
|
||||
*p = Point{
|
||||
X: float32(v.P.X),
|
||||
Y: float32(v.P.Y),
|
||||
Valid: v.Valid,
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p Point) PointValue() (pgtype.Point, error) {
|
||||
return pgtype.Point{
|
||||
P: pgtype.Vec2{X: float64(p.X), Y: float64(p.Y)},
|
||||
Valid: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (src *Point) String() string {
|
||||
if !src.Valid {
|
||||
return "null point"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%.1f, %.1f", src.X, src.Y)
|
||||
}
|
||||
|
||||
func Example_CustomType() {
|
||||
conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to establish connection: %v", err)
|
||||
return
|
||||
}
|
||||
defer conn.Close(context.Background())
|
||||
|
||||
if conn.PgConn().ParameterStatus("crdb_version") != "" {
|
||||
// Skip test / example when running on CockroachDB which doesn't support the point type. Since an example can't be
|
||||
// skipped fake success instead.
|
||||
fmt.Println("null point")
|
||||
fmt.Println("1.5, 2.5")
|
||||
return
|
||||
}
|
||||
|
||||
p := &Point{}
|
||||
err = conn.QueryRow(context.Background(), "select null::point").Scan(p)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
fmt.Println(p)
|
||||
|
||||
err = conn.QueryRow(context.Background(), "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
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
package pgtype_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/jackc/pgx/v5"
|
||||
)
|
||||
|
||||
func Example_JSON() {
|
||||
conn, err := pgx.Connect(context.Background(), os.Getenv("PGX_TEST_DATABASE"))
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to establish connection: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
type person struct {
|
||||
Name string `json:"name"`
|
||||
Age int `json:"age"`
|
||||
}
|
||||
|
||||
input := person{
|
||||
Name: "John",
|
||||
Age: 42,
|
||||
}
|
||||
|
||||
var output person
|
||||
|
||||
err = conn.QueryRow(context.Background(), "select $1::json", input).Scan(&output)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Println(output.Name, output.Age)
|
||||
// Output:
|
||||
// John 42
|
||||
}
|
||||
+2
-1
@@ -132,6 +132,7 @@ const (
|
||||
BinaryFormatCode = 1
|
||||
)
|
||||
|
||||
// A Codec converts between Go and PostgreSQL values.
|
||||
type Codec interface {
|
||||
// FormatSupported returns true if the format is supported.
|
||||
FormatSupported(int16) bool
|
||||
@@ -139,7 +140,7 @@ type Codec interface {
|
||||
// PreferredFormat returns the preferred format.
|
||||
PreferredFormat() int16
|
||||
|
||||
// PlanEncode returns an Encode plan for encoding value into PostgreSQL format for oid and format. If no plan can be
|
||||
// PlanEncode returns an EncodePlan for encoding value into PostgreSQL format for oid and format. If no plan can be
|
||||
// found then nil is returned.
|
||||
PlanEncode(m *Map, oid uint32, format int16, value interface{}) EncodePlan
|
||||
|
||||
|
||||
Reference in New Issue
Block a user