mirror of
https://github.com/neilotoole/sq.git
synced 2024-12-24 16:51:34 +03:00
142 lines
2.8 KiB
Go
142 lines
2.8 KiB
Go
|
// Package record holds the record.Record type, which is the
|
||
|
// core type for holding query results.
|
||
|
package record
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"time"
|
||
|
|
||
|
"github.com/neilotoole/sq/libsq/core/errz"
|
||
|
)
|
||
|
|
||
|
// Record is a []any row of field values returned from a query.
|
||
|
//
|
||
|
// In the codebase, we distinguish between a "Record" and
|
||
|
// a "ScanRow", although both are []any and are closely related.
|
||
|
//
|
||
|
// An instance of ScanRow is passed to the sql rows.Scan method, and
|
||
|
// its elements may include implementations of the sql.Scanner interface
|
||
|
// such as sql.NullString, sql.NullInt64 or even driver-specific types.
|
||
|
//
|
||
|
// A Record is typically built from a ScanRow, unwrapping and
|
||
|
// munging elements such that the Record only contains standard types:
|
||
|
//
|
||
|
// nil, *int64, *float64, *bool, *string, *[]byte, *time.Time
|
||
|
//
|
||
|
// It is an error for a Record to contain elements of any other type.
|
||
|
type Record []any
|
||
|
|
||
|
// Valid checks that each element of the record vals is
|
||
|
// of an acceptable type. On the first unacceptable element,
|
||
|
// the index of that element and an error are returned. On
|
||
|
// success (-1, nil) is returned.
|
||
|
//
|
||
|
// These acceptable types, per the stdlib sql pkg, are:
|
||
|
//
|
||
|
// nil, *int64, *float64, *bool, *string, *[]byte, *time.Time
|
||
|
func Valid(_ Meta, rec Record) (i int, err error) {
|
||
|
// FIXME: Valid should check the values of rec to see if they match recMeta's kinds
|
||
|
|
||
|
var val any
|
||
|
for i, val = range rec {
|
||
|
switch val := val.(type) {
|
||
|
case nil, *int64, *float64, *bool, *string, *[]byte, *time.Time:
|
||
|
continue
|
||
|
default:
|
||
|
return i, errz.Errorf("field [%d] has unacceptable record value type %T", i, val)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return -1, nil
|
||
|
}
|
||
|
|
||
|
// Equal returns true if rec1 and rec2 contain
|
||
|
// the same values.
|
||
|
func Equal(a, b Record) bool { //nolint:gocognit
|
||
|
switch {
|
||
|
case a == nil && b == nil:
|
||
|
return true
|
||
|
case a == nil || b == nil:
|
||
|
return false
|
||
|
case len(a) != len(b):
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
var i int
|
||
|
var va, vb any
|
||
|
|
||
|
for i, va = range a {
|
||
|
vb = b[i]
|
||
|
|
||
|
if va == nil && vb == nil {
|
||
|
continue
|
||
|
}
|
||
|
|
||
|
if va == nil || vb == nil {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
switch va := va.(type) {
|
||
|
case *string:
|
||
|
vb, ok := vb.(*string)
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if *va != *vb {
|
||
|
return false
|
||
|
}
|
||
|
case *bool:
|
||
|
vb, ok := vb.(*bool)
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if *va != *vb {
|
||
|
return false
|
||
|
}
|
||
|
case *int64:
|
||
|
vb, ok := vb.(*int64)
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if *va != *vb {
|
||
|
return false
|
||
|
}
|
||
|
case *float64:
|
||
|
vb, ok := vb.(*float64)
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if *va != *vb {
|
||
|
return false
|
||
|
}
|
||
|
case *time.Time:
|
||
|
vb, ok := vb.(*time.Time)
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if *va != *vb {
|
||
|
return false
|
||
|
}
|
||
|
case *[]byte:
|
||
|
vb, ok := vb.(*[]byte)
|
||
|
if !ok {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
if !bytes.Equal(*va, *vb) {
|
||
|
return false
|
||
|
}
|
||
|
default:
|
||
|
// Shouldn't happen
|
||
|
return false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true
|
||
|
}
|