You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
255 lines
5.6 KiB
255 lines
5.6 KiB
// Copyright (c) 2012-present The upper.io/db authors. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining
|
|
// a copy of this software and associated documentation files (the
|
|
// "Software"), to deal in the Software without restriction, including
|
|
// without limitation the rights to use, copy, modify, merge, publish,
|
|
// distribute, sublicense, and/or sell copies of the Software, and to
|
|
// permit persons to whom the Software is furnished to do so, subject to
|
|
// the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be
|
|
// included in all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
package sqlbuilder
|
|
|
|
import (
|
|
"reflect"
|
|
|
|
"database/sql"
|
|
"database/sql/driver"
|
|
db "github.com/upper/db/v4"
|
|
"github.com/upper/db/v4/internal/reflectx"
|
|
)
|
|
|
|
type sessValueConverter interface {
|
|
ConvertValue(interface{}) interface{}
|
|
}
|
|
|
|
type valueConverter interface {
|
|
ConvertValue(in interface{}) (out interface {
|
|
sql.Scanner
|
|
driver.Valuer
|
|
})
|
|
}
|
|
|
|
var Mapper = reflectx.NewMapper("db")
|
|
|
|
// fetchRow receives a *sql.Rows value and tries to map all the rows into a
|
|
// single struct given by the pointer `dst`.
|
|
func fetchRow(iter *iterator, dst interface{}) error {
|
|
var columns []string
|
|
var err error
|
|
|
|
rows := iter.cursor
|
|
|
|
dstv := reflect.ValueOf(dst)
|
|
|
|
if dstv.IsNil() || dstv.Kind() != reflect.Ptr {
|
|
return ErrExpectingPointer
|
|
}
|
|
|
|
itemV := dstv.Elem()
|
|
|
|
if columns, err = rows.Columns(); err != nil {
|
|
return err
|
|
}
|
|
|
|
reset(dst)
|
|
|
|
next := rows.Next()
|
|
|
|
if !next {
|
|
if err = rows.Err(); err != nil {
|
|
return err
|
|
}
|
|
return db.ErrNoMoreRows
|
|
}
|
|
|
|
itemT := itemV.Type()
|
|
item, err := fetchResult(iter, itemT, columns)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if itemT.Kind() == reflect.Ptr {
|
|
itemV.Set(item)
|
|
} else {
|
|
itemV.Set(reflect.Indirect(item))
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// fetchRows receives a *sql.Rows value and tries to map all the rows into a
|
|
// slice of structs given by the pointer `dst`.
|
|
func fetchRows(iter *iterator, dst interface{}) error {
|
|
var err error
|
|
rows := iter.cursor
|
|
defer rows.Close()
|
|
|
|
// Destination.
|
|
dstv := reflect.ValueOf(dst)
|
|
|
|
if dstv.IsNil() || dstv.Kind() != reflect.Ptr {
|
|
return ErrExpectingPointer
|
|
}
|
|
|
|
if dstv.Elem().Kind() != reflect.Slice {
|
|
return ErrExpectingSlicePointer
|
|
}
|
|
|
|
if dstv.Kind() != reflect.Ptr || dstv.Elem().Kind() != reflect.Slice || dstv.IsNil() {
|
|
return ErrExpectingSliceMapStruct
|
|
}
|
|
|
|
var columns []string
|
|
if columns, err = rows.Columns(); err != nil {
|
|
return err
|
|
}
|
|
|
|
slicev := dstv.Elem()
|
|
itemT := slicev.Type().Elem()
|
|
|
|
reset(dst)
|
|
|
|
for rows.Next() {
|
|
item, err := fetchResult(iter, itemT, columns)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if itemT.Kind() == reflect.Ptr {
|
|
slicev = reflect.Append(slicev, item)
|
|
} else {
|
|
slicev = reflect.Append(slicev, reflect.Indirect(item))
|
|
}
|
|
}
|
|
|
|
dstv.Elem().Set(slicev)
|
|
|
|
return rows.Err()
|
|
}
|
|
|
|
func fetchResult(iter *iterator, itemT reflect.Type, columns []string) (reflect.Value, error) {
|
|
|
|
var item reflect.Value
|
|
var err error
|
|
rows := iter.cursor
|
|
|
|
objT := itemT
|
|
|
|
switch objT.Kind() {
|
|
case reflect.Map:
|
|
item = reflect.MakeMap(objT)
|
|
case reflect.Struct:
|
|
item = reflect.New(objT)
|
|
case reflect.Ptr:
|
|
objT = itemT.Elem()
|
|
if objT.Kind() != reflect.Struct {
|
|
return item, ErrExpectingMapOrStruct
|
|
}
|
|
item = reflect.New(objT)
|
|
default:
|
|
return item, ErrExpectingMapOrStruct
|
|
}
|
|
|
|
switch objT.Kind() {
|
|
case reflect.Struct:
|
|
|
|
values := make([]interface{}, len(columns))
|
|
typeMap := Mapper.TypeMap(itemT)
|
|
fieldMap := typeMap.Names
|
|
|
|
for i, k := range columns {
|
|
fi, ok := fieldMap[k]
|
|
if !ok {
|
|
values[i] = new(interface{})
|
|
continue
|
|
}
|
|
|
|
// Check for deprecated jsonb tag.
|
|
if _, hasJSONBTag := fi.Options["jsonb"]; hasJSONBTag {
|
|
return item, errDeprecatedJSONBTag
|
|
}
|
|
|
|
f := reflectx.FieldByIndexes(item, fi.Index)
|
|
|
|
// TODO: type switch + scanner
|
|
|
|
if w, ok := f.Interface().(valueConverter); ok {
|
|
wrapper := w.ConvertValue(f.Addr().Interface())
|
|
z := reflect.ValueOf(wrapper)
|
|
values[i] = z.Interface()
|
|
continue
|
|
} else {
|
|
values[i] = f.Addr().Interface()
|
|
}
|
|
|
|
if unmarshaler, ok := values[i].(db.Unmarshaler); ok {
|
|
values[i] = scanner{unmarshaler}
|
|
continue
|
|
}
|
|
|
|
if converter, ok := iter.sess.(sessValueConverter); ok {
|
|
values[i] = converter.ConvertValue(values[i])
|
|
continue
|
|
}
|
|
}
|
|
|
|
if err = rows.Scan(values...); err != nil {
|
|
return item, err
|
|
}
|
|
|
|
case reflect.Map:
|
|
|
|
columns, err := rows.Columns()
|
|
if err != nil {
|
|
return item, err
|
|
}
|
|
|
|
values := make([]interface{}, len(columns))
|
|
for i := range values {
|
|
if itemT.Elem().Kind() == reflect.Interface {
|
|
values[i] = new(interface{})
|
|
} else {
|
|
values[i] = reflect.New(itemT.Elem()).Interface()
|
|
}
|
|
}
|
|
|
|
if err = rows.Scan(values...); err != nil {
|
|
return item, err
|
|
}
|
|
|
|
for i, column := range columns {
|
|
item.SetMapIndex(reflect.ValueOf(column), reflect.Indirect(reflect.ValueOf(values[i])))
|
|
}
|
|
}
|
|
|
|
return item, nil
|
|
}
|
|
|
|
func reset(data interface{}) {
|
|
// Resetting element.
|
|
v := reflect.ValueOf(data).Elem()
|
|
t := v.Type()
|
|
|
|
var z reflect.Value
|
|
|
|
switch v.Kind() {
|
|
case reflect.Slice:
|
|
z = reflect.MakeSlice(t, 0, v.Cap())
|
|
default:
|
|
z = reflect.Zero(t)
|
|
}
|
|
|
|
v.Set(z)
|
|
}
|