package sql
import (
"bytes"
"database/sql/driver"
"errors"
"fmt"
"reflect"
"strconv"
"time"
"unicode"
"unicode/utf8"
_ "unsafe"
)
var errNilPtr = errors .New ("destination pointer is nil" )
func describeNamedValue(nv *driver .NamedValue ) string {
if len (nv .Name ) == 0 {
return fmt .Sprintf ("$%d" , nv .Ordinal )
}
return fmt .Sprintf ("with name %q" , nv .Name )
}
func validateNamedValueName(name string ) error {
if len (name ) == 0 {
return nil
}
r , _ := utf8 .DecodeRuneInString (name )
if unicode .IsLetter (r ) {
return nil
}
return fmt .Errorf ("name %q does not begin with a letter" , name )
}
type ccChecker struct {
cci driver .ColumnConverter
want int
}
func (c ccChecker ) CheckNamedValue (nv *driver .NamedValue ) error {
if c .cci == nil {
return driver .ErrSkip
}
index := nv .Ordinal - 1
if c .want <= index {
return nil
}
if vr , ok := nv .Value .(driver .Valuer ); ok {
sv , err := callValuerValue (vr )
if err != nil {
return err
}
if !driver .IsValue (sv ) {
return fmt .Errorf ("non-subset type %T returned from Value" , sv )
}
nv .Value = sv
}
var err error
arg := nv .Value
nv .Value , err = c .cci .ColumnConverter (index ).ConvertValue (arg )
if err != nil {
return err
}
if !driver .IsValue (nv .Value ) {
return fmt .Errorf ("driver ColumnConverter error converted %T to unsupported type %T" , arg , nv .Value )
}
return nil
}
func defaultCheckNamedValue(nv *driver .NamedValue ) (err error ) {
nv .Value , err = driver .DefaultParameterConverter .ConvertValue (nv .Value )
return err
}
func driverArgsConnLocked(ci driver .Conn , ds *driverStmt , args []any ) ([]driver .NamedValue , error ) {
nvargs := make ([]driver .NamedValue , len (args ))
want := -1
var si driver .Stmt
var cc ccChecker
if ds != nil {
si = ds .si
want = ds .si .NumInput ()
cc .want = want
}
nvc , ok := si .(driver .NamedValueChecker )
if !ok {
nvc , _ = ci .(driver .NamedValueChecker )
}
cci , ok := si .(driver .ColumnConverter )
if ok {
cc .cci = cci
}
var err error
var n int
for _ , arg := range args {
nv := &nvargs [n ]
if np , ok := arg .(NamedArg ); ok {
if err = validateNamedValueName (np .Name ); err != nil {
return nil , err
}
arg = np .Value
nv .Name = np .Name
}
nv .Ordinal = n + 1
nv .Value = arg
checker := defaultCheckNamedValue
nextCC := false
switch {
case nvc != nil :
nextCC = cci != nil
checker = nvc .CheckNamedValue
case cci != nil :
checker = cc .CheckNamedValue
}
nextCheck :
err = checker (nv )
switch err {
case nil :
n ++
continue
case driver .ErrRemoveArgument :
nvargs = nvargs [:len (nvargs )-1 ]
continue
case driver .ErrSkip :
if nextCC {
nextCC = false
checker = cc .CheckNamedValue
} else {
checker = defaultCheckNamedValue
}
goto nextCheck
default :
return nil , fmt .Errorf ("sql: converting argument %s type: %w" , describeNamedValue (nv ), err )
}
}
if want != -1 && len (nvargs ) != want {
return nil , fmt .Errorf ("sql: expected %d arguments, got %d" , want , len (nvargs ))
}
return nvargs , nil
}
func convertAssign(dest , src any ) error {
return convertAssignRows (dest , src , nil )
}
func convertAssignRows(dest , src any , rows *Rows ) error {
switch s := src .(type ) {
case string :
switch d := dest .(type ) {
case *string :
if d == nil {
return errNilPtr
}
*d = s
return nil
case *[]byte :
if d == nil {
return errNilPtr
}
*d = []byte (s )
return nil
case *RawBytes :
if d == nil {
return errNilPtr
}
*d = rows .setrawbuf (append (rows .rawbuf (), s ...))
return nil
}
case []byte :
switch d := dest .(type ) {
case *string :
if d == nil {
return errNilPtr
}
*d = string (s )
return nil
case *any :
if d == nil {
return errNilPtr
}
*d = bytes .Clone (s )
return nil
case *[]byte :
if d == nil {
return errNilPtr
}
*d = bytes .Clone (s )
return nil
case *RawBytes :
if d == nil {
return errNilPtr
}
*d = s
return nil
}
case time .Time :
switch d := dest .(type ) {
case *time .Time :
*d = s
return nil
case *string :
*d = s .Format (time .RFC3339Nano )
return nil
case *[]byte :
if d == nil {
return errNilPtr
}
*d = []byte (s .Format (time .RFC3339Nano ))
return nil
case *RawBytes :
if d == nil {
return errNilPtr
}
*d = rows .setrawbuf (s .AppendFormat (rows .rawbuf (), time .RFC3339Nano ))
return nil
}
case decimalDecompose :
switch d := dest .(type ) {
case decimalCompose :
return d .Compose (s .Decompose (nil ))
}
case nil :
switch d := dest .(type ) {
case *any :
if d == nil {
return errNilPtr
}
*d = nil
return nil
case *[]byte :
if d == nil {
return errNilPtr
}
*d = nil
return nil
case *RawBytes :
if d == nil {
return errNilPtr
}
*d = nil
return nil
}
case driver .Rows :
switch d := dest .(type ) {
case *Rows :
if d == nil {
return errNilPtr
}
if rows == nil {
return errors .New ("invalid context to convert cursor rows, missing parent *Rows" )
}
rows .closemu .Lock ()
*d = Rows {
dc : rows .dc ,
releaseConn : func (error ) {},
rowsi : s ,
}
parentCancel := rows .cancel
rows .cancel = func () {
d .close (rows .lasterr )
if parentCancel != nil {
parentCancel ()
}
}
rows .closemu .Unlock ()
return nil
}
}
var sv reflect .Value
switch d := dest .(type ) {
case *string :
sv = reflect .ValueOf (src )
switch sv .Kind () {
case reflect .Bool ,
reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 ,
reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 ,
reflect .Float32 , reflect .Float64 :
*d = asString (src )
return nil
}
case *[]byte :
sv = reflect .ValueOf (src )
if b , ok := asBytes (nil , sv ); ok {
*d = b
return nil
}
case *RawBytes :
sv = reflect .ValueOf (src )
if b , ok := asBytes (rows .rawbuf (), sv ); ok {
*d = rows .setrawbuf (b )
return nil
}
case *bool :
bv , err := driver .Bool .ConvertValue (src )
if err == nil {
*d = bv .(bool )
}
return err
case *any :
*d = src
return nil
}
if scanner , ok := dest .(Scanner ); ok {
return scanner .Scan (src )
}
dpv := reflect .ValueOf (dest )
if dpv .Kind () != reflect .Pointer {
return errors .New ("destination not a pointer" )
}
if dpv .IsNil () {
return errNilPtr
}
if !sv .IsValid () {
sv = reflect .ValueOf (src )
}
dv := reflect .Indirect (dpv )
if sv .IsValid () && sv .Type ().AssignableTo (dv .Type ()) {
switch b := src .(type ) {
case []byte :
dv .Set (reflect .ValueOf (bytes .Clone (b )))
default :
dv .Set (sv )
}
return nil
}
if dv .Kind () == sv .Kind () && sv .Type ().ConvertibleTo (dv .Type ()) {
dv .Set (sv .Convert (dv .Type ()))
return nil
}
switch dv .Kind () {
case reflect .Pointer :
if src == nil {
dv .SetZero ()
return nil
}
dv .Set (reflect .New (dv .Type ().Elem ()))
return convertAssignRows (dv .Interface (), src , rows )
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
if src == nil {
return fmt .Errorf ("converting NULL to %s is unsupported" , dv .Kind ())
}
s := asString (src )
i64 , err := strconv .ParseInt (s , 10 , dv .Type ().Bits ())
if err != nil {
err = strconvErr (err )
return fmt .Errorf ("converting driver.Value type %T (%q) to a %s: %v" , src , s , dv .Kind (), err )
}
dv .SetInt (i64 )
return nil
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
if src == nil {
return fmt .Errorf ("converting NULL to %s is unsupported" , dv .Kind ())
}
s := asString (src )
u64 , err := strconv .ParseUint (s , 10 , dv .Type ().Bits ())
if err != nil {
err = strconvErr (err )
return fmt .Errorf ("converting driver.Value type %T (%q) to a %s: %v" , src , s , dv .Kind (), err )
}
dv .SetUint (u64 )
return nil
case reflect .Float32 , reflect .Float64 :
if src == nil {
return fmt .Errorf ("converting NULL to %s is unsupported" , dv .Kind ())
}
s := asString (src )
f64 , err := strconv .ParseFloat (s , dv .Type ().Bits ())
if err != nil {
err = strconvErr (err )
return fmt .Errorf ("converting driver.Value type %T (%q) to a %s: %v" , src , s , dv .Kind (), err )
}
dv .SetFloat (f64 )
return nil
case reflect .String :
if src == nil {
return fmt .Errorf ("converting NULL to %s is unsupported" , dv .Kind ())
}
switch v := src .(type ) {
case string :
dv .SetString (v )
return nil
case []byte :
dv .SetString (string (v ))
return nil
}
}
return fmt .Errorf ("unsupported Scan, storing driver.Value type %T into type %T" , src , dest )
}
func strconvErr(err error ) error {
if ne , ok := err .(*strconv .NumError ); ok {
return ne .Err
}
return err
}
func asString(src any ) string {
switch v := src .(type ) {
case string :
return v
case []byte :
return string (v )
}
rv := reflect .ValueOf (src )
switch rv .Kind () {
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
return strconv .FormatInt (rv .Int (), 10 )
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
return strconv .FormatUint (rv .Uint (), 10 )
case reflect .Float64 :
return strconv .FormatFloat (rv .Float (), 'g' , -1 , 64 )
case reflect .Float32 :
return strconv .FormatFloat (rv .Float (), 'g' , -1 , 32 )
case reflect .Bool :
return strconv .FormatBool (rv .Bool ())
}
return fmt .Sprintf ("%v" , src )
}
func asBytes(buf []byte , rv reflect .Value ) (b []byte , ok bool ) {
switch rv .Kind () {
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
return strconv .AppendInt (buf , rv .Int (), 10 ), true
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 :
return strconv .AppendUint (buf , rv .Uint (), 10 ), true
case reflect .Float32 :
return strconv .AppendFloat (buf , rv .Float (), 'g' , -1 , 32 ), true
case reflect .Float64 :
return strconv .AppendFloat (buf , rv .Float (), 'g' , -1 , 64 ), true
case reflect .Bool :
return strconv .AppendBool (buf , rv .Bool ()), true
case reflect .String :
s := rv .String ()
return append (buf , s ...), true
}
return
}
var valuerReflectType = reflect .TypeFor [driver .Valuer ]()
func callValuerValue(vr driver .Valuer ) (v driver .Value , err error ) {
if rv := reflect .ValueOf (vr ); rv .Kind () == reflect .Pointer &&
rv .IsNil () &&
rv .Type ().Elem ().Implements (valuerReflectType ) {
return nil , nil
}
return vr .Value ()
}
type decimal interface {
decimalDecompose
decimalCompose
}
type decimalDecompose interface {
Decompose(buf []byte ) (form byte , negative bool , coefficient []byte , exponent int32 )
}
type decimalCompose interface {
Compose(form byte , negative bool , coefficient []byte , exponent int32 ) error
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .