package pgx
import (
)
type Rows interface {
Close()
Err() error
CommandTag() pgconn.CommandTag
FieldDescriptions() []pgconn.FieldDescription
Next() bool
Scan(dest ...any) error
Values() ([]any, error)
RawValues() [][]byte
Conn() *Conn
}
type Row interface {
Scan(dest ...any) error
}
type RowScanner interface {
ScanRow(rows Rows) error
}
type connRow baseRows
func ( *connRow) ( ...any) ( error) {
:= (*baseRows)()
if .Err() != nil {
return .Err()
}
for , := range {
if , := .(*pgtype.DriverBytes); {
.Close()
return fmt.Errorf("cannot scan into *pgtype.DriverBytes from QueryRow")
}
}
if !.Next() {
if .Err() == nil {
return ErrNoRows
}
return .Err()
}
.Scan(...)
.Close()
return .Err()
}
type baseRows struct {
typeMap *pgtype.Map
resultReader *pgconn.ResultReader
values [][]byte
commandTag pgconn.CommandTag
err error
closed bool
scanPlans []pgtype.ScanPlan
scanTypes []reflect.Type
conn *Conn
multiResultReader *pgconn.MultiResultReader
queryTracer QueryTracer
batchTracer BatchTracer
ctx context.Context
startTime time.Time
sql string
args []any
rowCount int
}
func ( *baseRows) () []pgconn.FieldDescription {
return .resultReader.FieldDescriptions()
}
func ( *baseRows) () {
if .closed {
return
}
.closed = true
if .resultReader != nil {
var error
.commandTag, = .resultReader.Close()
if .err == nil {
.err =
}
}
if .multiResultReader != nil {
:= .multiResultReader.Close()
if .err == nil {
.err =
}
}
if .err != nil && .conn != nil && .sql != "" {
if := .conn.statementCache; != nil {
.Invalidate(.sql)
}
if := .conn.descriptionCache; != nil {
.Invalidate(.sql)
}
}
if .batchTracer != nil {
.batchTracer.TraceBatchQuery(.ctx, .conn, TraceBatchQueryData{SQL: .sql, Args: .args, CommandTag: .commandTag, Err: .err})
} else if .queryTracer != nil {
.queryTracer.TraceQueryEnd(.ctx, .conn, TraceQueryEndData{.commandTag, .err})
}
.values = nil
.scanPlans = nil
.scanTypes = nil
.ctx = nil
.sql = ""
.args = nil
}
func ( *baseRows) () pgconn.CommandTag {
return .commandTag
}
func ( *baseRows) () error {
return .err
}
func ( *baseRows) ( error) {
if .err != nil {
return
}
.err =
.Close()
}
func ( *baseRows) () bool {
if .closed {
return false
}
if .resultReader.NextRow() {
.rowCount++
.values = .resultReader.Values()
return true
} else {
.Close()
return false
}
}
func ( *baseRows) ( ...any) error {
:= .typeMap
:= .FieldDescriptions()
:= .values
if len() != len() {
:= fmt.Errorf("number of field descriptions must equal number of values, got %d and %d", len(), len())
.fatal()
return
}
if len() == 1 {
if , := [0].(RowScanner); {
:= .ScanRow()
if != nil {
.fatal()
}
return
}
}
if len() != len() {
:= fmt.Errorf("number of field descriptions must equal number of destinations, got %d and %d", len(), len())
.fatal()
return
}
if .scanPlans == nil {
.scanPlans = make([]pgtype.ScanPlan, len())
.scanTypes = make([]reflect.Type, len())
for := range {
.scanPlans[] = .PlanScan([].DataTypeOID, [].Format, [])
.scanTypes[] = reflect.TypeOf([])
}
}
for , := range {
if == nil {
continue
}
if .scanTypes[] != reflect.TypeOf() {
.scanPlans[] = .PlanScan([].DataTypeOID, [].Format, [])
.scanTypes[] = reflect.TypeOf([])
}
:= .scanPlans[].Scan([], )
if != nil {
= ScanArgError{ColumnIndex: , FieldName: [].Name, Err: }
.fatal()
return
}
}
return nil
}
func ( *baseRows) () ([]any, error) {
if .closed {
return nil, errors.New("rows is closed")
}
:= make([]any, 0, len(.FieldDescriptions()))
for := range .FieldDescriptions() {
:= .values[]
:= &.FieldDescriptions()[]
if == nil {
= append(, nil)
continue
}
if , := .typeMap.TypeForOID(.DataTypeOID); {
, := .Codec.DecodeValue(.typeMap, .DataTypeOID, .Format, )
if != nil {
.fatal()
}
= append(, )
} else {
switch .Format {
case TextFormatCode:
= append(, string())
case BinaryFormatCode:
:= make([]byte, len())
copy(, )
= append(, )
default:
.fatal(errors.New("unknown format code"))
}
}
if .Err() != nil {
return nil, .Err()
}
}
return , .Err()
}
func ( *baseRows) () [][]byte {
return .values
}
func ( *baseRows) () *Conn {
return .conn
}
type ScanArgError struct {
ColumnIndex int
FieldName string
Err error
}
func ( ScanArgError) () string {
if .FieldName == "?column?" {
return fmt.Sprintf("can't scan into dest[%d]: %v", .ColumnIndex, .Err)
}
return fmt.Sprintf("can't scan into dest[%d] (col: %s): %v", .ColumnIndex, .FieldName, .Err)
}
func ( ScanArgError) () error {
return .Err
}
func ( *pgtype.Map, []pgconn.FieldDescription, [][]byte, ...any) error {
if len() != len() {
return fmt.Errorf("number of field descriptions must equal number of values, got %d and %d", len(), len())
}
if len() != len() {
return fmt.Errorf("number of field descriptions must equal number of destinations, got %d and %d", len(), len())
}
for , := range {
if == nil {
continue
}
:= .Scan([].DataTypeOID, [].Format, [], )
if != nil {
return ScanArgError{ColumnIndex: , FieldName: [].Name, Err: }
}
}
return nil
}
func ( *pgtype.Map, *pgconn.ResultReader) Rows {
return &baseRows{
typeMap: ,
resultReader: ,
}
}
func ( Rows, []any, func() error) (pgconn.CommandTag, error) {
defer .Close()
for .Next() {
:= .Scan(...)
if != nil {
return pgconn.CommandTag{},
}
= ()
if != nil {
return pgconn.CommandTag{},
}
}
if := .Err(); != nil {
return pgconn.CommandTag{},
}
return .CommandTag(), nil
}
type CollectableRow interface {
FieldDescriptions() []pgconn.FieldDescription
Scan(dest ...any) error
Values() ([]any, error)
RawValues() [][]byte
}
type RowToFunc[ any] func(row CollectableRow) (, error)
func [ any, ~[]]( , Rows, RowToFunc[]) (, error) {
defer .Close()
for .Next() {
, := ()
if != nil {
return nil,
}
= append(, )
}
if := .Err(); != nil {
return nil,
}
return , nil
}
func [ any]( Rows, RowToFunc[]) ([], error) {
return AppendRows([]{}, , )
}
func [ any]( Rows, RowToFunc[]) (, error) {
defer .Close()
var
var error
if !.Next() {
if = .Err(); != nil {
return ,
}
return , ErrNoRows
}
, = ()
if != nil {
return ,
}
.Close()
return , .Err()
}
func [ any]( Rows, RowToFunc[]) (, error) {
defer .Close()
var (
error
)
if !.Next() {
if = .Err(); != nil {
return ,
}
return , ErrNoRows
}
, = ()
if != nil {
return ,
}
if .Next() {
var
return , ErrTooManyRows
}
return , .Err()
}
func [ any]( CollectableRow) (, error) {
var
:= .Scan(&)
return ,
}
func [ any]( CollectableRow) (*, error) {
var
:= .Scan(&)
return &,
}
func ( CollectableRow) (map[string]any, error) {
var map[string]any
:= .Scan((*mapRowScanner)(&))
return ,
}
type mapRowScanner map[string]any
func ( *mapRowScanner) ( Rows) error {
, := .Values()
if != nil {
return
}
* = make(mapRowScanner, len())
for := range {
(*)[string(.FieldDescriptions()[].Name)] = []
}
return nil
}
func [ any]( CollectableRow) (, error) {
var
:= (&positionalStructRowScanner{ptrToStruct: &}).ScanRow()
return ,
}
func [ any]( CollectableRow) (*, error) {
var
:= (&positionalStructRowScanner{ptrToStruct: &}).ScanRow()
return &,
}
type positionalStructRowScanner struct {
ptrToStruct any
}
func ( *positionalStructRowScanner) ( CollectableRow) error {
:= reflect.TypeOf(.ptrToStruct).Elem()
:= lookupStructFields()
if len(.RawValues()) > len() {
return fmt.Errorf(
"got %d values, but dst struct has only %d fields",
len(.RawValues()),
len(),
)
}
:= setupStructScanTargets(.ptrToStruct, )
return .Scan(...)
}
var positionalStructFieldMap sync.Map
func ( reflect.Type) []structRowField {
if , := positionalStructFieldMap.Load(); {
return .([]structRowField)
}
:= make([]int, 0, 1)
:= computeStructFields(, make([]structRowField, 0, .NumField()), &)
, := positionalStructFieldMap.LoadOrStore(, )
return .([]structRowField)
}
func (
reflect.Type,
[]structRowField,
*[]int,
) []structRowField {
:= len(*)
* = append(*, 0)
for := 0; < .NumField(); ++ {
:= .Field()
(*)[] =
if .Anonymous && .Type.Kind() == reflect.Struct {
= (.Type, , )
} else if .PkgPath == "" {
, := .Tag.Lookup(structTagKey)
if == "-" {
continue
}
= append(, structRowField{
path: append([]int(nil), *...),
})
}
}
* = (*)[:]
return
}
func [ any]( CollectableRow) (, error) {
var
:= (&namedStructRowScanner{ptrToStruct: &}).ScanRow()
return ,
}
func [ any]( CollectableRow) (*, error) {
var
:= (&namedStructRowScanner{ptrToStruct: &}).ScanRow()
return &,
}
func [ any]( CollectableRow) (, error) {
var
:= (&namedStructRowScanner{ptrToStruct: &, lax: true}).ScanRow()
return ,
}
func [ any]( CollectableRow) (*, error) {
var
:= (&namedStructRowScanner{ptrToStruct: &, lax: true}).ScanRow()
return &,
}
type namedStructRowScanner struct {
ptrToStruct any
lax bool
}
func ( *namedStructRowScanner) ( CollectableRow) error {
:= reflect.TypeOf(.ptrToStruct).Elem()
:= .FieldDescriptions()
, := lookupNamedStructFields(, )
if != nil {
return
}
if !.lax && .missingField != "" {
return fmt.Errorf("cannot find field %s in returned row", .missingField)
}
:= .fields
:= setupStructScanTargets(.ptrToStruct, )
return .Scan(...)
}
var namedStructFieldMap sync.Map
type namedStructFieldsKey struct {
t reflect.Type
colNames string
}
type namedStructFields struct {
fields []structRowField
missingField string
}
func (
reflect.Type,
[]pgconn.FieldDescription,
) (*namedStructFields, error) {
:= namedStructFieldsKey{
t: ,
colNames: joinFieldNames(),
}
if , := namedStructFieldMap.Load(); {
return .(*namedStructFields), nil
}
:= make([]int, 0, 1)
, := computeNamedStructFields(
,
,
make([]structRowField, len()),
&,
)
for , := range {
if .path == nil {
return nil, fmt.Errorf(
"struct doesn't have corresponding row field %s",
[].Name,
)
}
}
, := namedStructFieldMap.LoadOrStore(
,
&namedStructFields{fields: , missingField: },
)
return .(*namedStructFields), nil
}
func ( []pgconn.FieldDescription) string {
switch len() {
case 0:
return ""
case 1:
return [0].Name
}
:= len() - 1
for , := range {
+= len(.Name)
}
var strings.Builder
.Grow()
.WriteString([0].Name)
for , := range [1:] {
.WriteByte(0)
.WriteString(.Name)
}
return .String()
}
func (
[]pgconn.FieldDescription,
reflect.Type,
[]structRowField,
*[]int,
) ([]structRowField, string) {
var string
:= len(*)
* = append(*, 0)
for := 0; < .NumField(); ++ {
:= .Field()
(*)[] =
if .PkgPath != "" && !.Anonymous {
continue
}
if .Anonymous && .Type.Kind() == reflect.Struct {
var string
, = (
,
.Type,
,
,
)
if == "" {
=
}
} else {
, := .Tag.Lookup(structTagKey)
if {
, _, _ = strings.Cut(, ",")
}
if == "-" {
continue
}
:=
if ! {
= .Name
}
:= fieldPosByName(, , !)
if == -1 {
if == "" {
=
}
continue
}
[] = structRowField{
path: append([]int(nil), *...),
}
}
}
* = (*)[:]
return ,
}
const structTagKey = "db"
func ( []pgconn.FieldDescription, string, bool) ( int) {
= -1
if {
= strings.ReplaceAll(, "_", "")
}
for , := range {
if {
if strings.EqualFold(strings.ReplaceAll(.Name, "_", ""), ) {
return
}
} else {
if .Name == {
return
}
}
}
return
}
type structRowField struct {
path []int
}
func ( any, []structRowField) []any {
:= make([]any, len())
:= reflect.ValueOf().Elem()
for , := range {
[] = .FieldByIndex(.path).Addr().Interface()
}
return
}