mirror of https://github.com/jackc/pgx.git
Add pgtype.Map.SQLScanner
This enables compatibility with database/sql for types that cannot implement Scan themselves.non-blocking
parent
f1a4ae3070
commit
fccaebc93d
|
@ -1808,3 +1808,41 @@ func (m *Map) Encode(oid uint32, formatCode int16, value any, buf []byte) (newBu
|
||||||
|
|
||||||
return newBuf, nil
|
return newBuf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SQLScanner returns a database/sql.Scanner for v. This is necessary for types like Array[T] and Range[T] where the
|
||||||
|
// type needs assistance from Map to implement the sql.Scanner interface. It is not necessary for types like Box that
|
||||||
|
// implement sql.Scanner directly.
|
||||||
|
//
|
||||||
|
// This uses the type of v to look up the PostgreSQL OID that v presumably came from. This means v must be registered
|
||||||
|
// with m by calling RegisterDefaultPgType.
|
||||||
|
func (m *Map) SQLScanner(v any) sql.Scanner {
|
||||||
|
if s, ok := v.(sql.Scanner); ok {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
return &sqlScannerWrapper{m: m, v: v}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sqlScannerWrapper struct {
|
||||||
|
m *Map
|
||||||
|
v any
|
||||||
|
}
|
||||||
|
|
||||||
|
func (w *sqlScannerWrapper) Scan(src any) error {
|
||||||
|
t, ok := w.m.TypeForValue(w.v)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("cannot convert to sql.Scanner: cannot find registered type for %T", w.v)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bufSrc []byte
|
||||||
|
switch src := src.(type) {
|
||||||
|
case string:
|
||||||
|
bufSrc = []byte(src)
|
||||||
|
case []byte:
|
||||||
|
bufSrc = src
|
||||||
|
default:
|
||||||
|
bufSrc = []byte(fmt.Sprint(bufSrc))
|
||||||
|
}
|
||||||
|
|
||||||
|
return w.m.Scan(t.OID, TextFormatCode, bufSrc, w.v)
|
||||||
|
}
|
||||||
|
|
|
@ -23,13 +23,12 @@
|
||||||
// connStr := stdlib.RegisterConnConfig(connConfig)
|
// connStr := stdlib.RegisterConnConfig(connConfig)
|
||||||
// db, _ := sql.Open("pgx", connStr)
|
// db, _ := sql.Open("pgx", connStr)
|
||||||
//
|
//
|
||||||
// pgx uses standard PostgreSQL positional parameters in queries. e.g. $1, $2.
|
// pgx uses standard PostgreSQL positional parameters in queries. e.g. $1, $2. It does not support named parameters.
|
||||||
// It does not support named parameters.
|
|
||||||
//
|
//
|
||||||
// db.QueryRow("select * from users where id=$1", userID)
|
// db.QueryRow("select * from users where id=$1", userID)
|
||||||
//
|
//
|
||||||
// In Go 1.13 and above (*sql.Conn) Raw() can be used to get a *pgx.Conn from the standard
|
// In Go 1.13 and above (*sql.Conn) Raw() can be used to get a *pgx.Conn from the standard database/sql.DB connection
|
||||||
// database/sql.DB connection pool. This allows operations that use pgx specific functionality.
|
// pool. This allows operations that use pgx specific functionality.
|
||||||
//
|
//
|
||||||
// // Given db is a *sql.DB
|
// // Given db is a *sql.DB
|
||||||
// conn, err := db.Conn(context.Background())
|
// conn, err := db.Conn(context.Background())
|
||||||
|
@ -46,6 +45,15 @@
|
||||||
// if err != nil {
|
// if err != nil {
|
||||||
// // handle error that occurred while using *pgx.Conn
|
// // handle error that occurred while using *pgx.Conn
|
||||||
// }
|
// }
|
||||||
|
//
|
||||||
|
// PostgreSQL Specific Data Types
|
||||||
|
//
|
||||||
|
// The pgtype package provides support for PostgreSQL specific types. *pgtype.Map.SQLScanner is an adapter that makes
|
||||||
|
// these types usable as a sql.Scanner.
|
||||||
|
//
|
||||||
|
// m := pgtype.NewMap()
|
||||||
|
// var a []int64
|
||||||
|
// err := db.QueryRow("select '{1,2,3}'::bigint[]").Scan(m.SQLScanner(&a))
|
||||||
package stdlib
|
package stdlib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
|
|
@ -15,6 +15,7 @@ import (
|
||||||
|
|
||||||
"github.com/jackc/pgx/v5"
|
"github.com/jackc/pgx/v5"
|
||||||
"github.com/jackc/pgx/v5/pgconn"
|
"github.com/jackc/pgx/v5/pgconn"
|
||||||
|
"github.com/jackc/pgx/v5/pgtype"
|
||||||
"github.com/jackc/pgx/v5/stdlib"
|
"github.com/jackc/pgx/v5/stdlib"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -373,6 +374,37 @@ func TestConnSimpleSlicePassThrough(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConnQueryScanArray(t *testing.T) {
|
||||||
|
testWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {
|
||||||
|
m := pgtype.NewMap()
|
||||||
|
|
||||||
|
var a []int64
|
||||||
|
err := db.QueryRow("select '{1,2,3}'::bigint[]").Scan(m.SQLScanner(&a))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, []int64{1, 2, 3}, a)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestConnQueryScanRange(t *testing.T) {
|
||||||
|
testWithAllQueryExecModes(t, func(t *testing.T, db *sql.DB) {
|
||||||
|
m := pgtype.NewMap()
|
||||||
|
|
||||||
|
var r pgtype.Range[pgtype.Int4]
|
||||||
|
err := db.QueryRow("select int4range(1, 5)").Scan(m.SQLScanner(&r))
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(
|
||||||
|
t,
|
||||||
|
pgtype.Range[pgtype.Int4]{
|
||||||
|
Lower: pgtype.Int4{Int32: 1, Valid: true},
|
||||||
|
Upper: pgtype.Int4{Int32: 5, Valid: true},
|
||||||
|
LowerType: pgtype.Inclusive,
|
||||||
|
UpperType: pgtype.Exclusive,
|
||||||
|
Valid: true,
|
||||||
|
},
|
||||||
|
r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Test type that pgx would handle natively in binary, but since it is not a
|
// Test type that pgx would handle natively in binary, but since it is not a
|
||||||
// database/sql native type should be passed through as a string
|
// database/sql native type should be passed through as a string
|
||||||
func TestConnQueryRowPgxBinary(t *testing.T) {
|
func TestConnQueryRowPgxBinary(t *testing.T) {
|
||||||
|
|
Loading…
Reference in New Issue