forked from ebhomengo/niki
1
0
Fork 0
niki/vendor/github.com/go-gorp/gorp/v3/select.go

617 lines
9.1 KiB
Go
Raw Normal View History

2024-02-18 10:42:21 +00:00
// Copyright 2012 James Cooper. All rights reserved.
2024-02-18 10:42:21 +00:00
// Use of this source code is governed by a MIT-style
2024-02-18 10:42:21 +00:00
// license that can be found in the LICENSE file.
package gorp
import (
"database/sql"
"fmt"
"reflect"
)
// SelectInt executes the given query, which should be a SELECT statement for a single
2024-02-18 10:42:21 +00:00
// integer column, and returns the value of the first row returned. If no rows are
2024-02-18 10:42:21 +00:00
// found, zero is returned.
2024-02-18 10:42:21 +00:00
func SelectInt(e SqlExecutor, query string, args ...interface{}) (int64, error) {
2024-02-18 10:42:21 +00:00
var h int64
2024-02-18 10:42:21 +00:00
err := selectVal(e, &h, query, args...)
2024-02-18 10:42:21 +00:00
if err != nil && err != sql.ErrNoRows {
2024-02-18 10:42:21 +00:00
return 0, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return h, nil
2024-02-18 10:42:21 +00:00
}
// SelectNullInt executes the given query, which should be a SELECT statement for a single
2024-02-18 10:42:21 +00:00
// integer column, and returns the value of the first row returned. If no rows are
2024-02-18 10:42:21 +00:00
// found, the empty sql.NullInt64 value is returned.
2024-02-18 10:42:21 +00:00
func SelectNullInt(e SqlExecutor, query string, args ...interface{}) (sql.NullInt64, error) {
2024-02-18 10:42:21 +00:00
var h sql.NullInt64
2024-02-18 10:42:21 +00:00
err := selectVal(e, &h, query, args...)
2024-02-18 10:42:21 +00:00
if err != nil && err != sql.ErrNoRows {
2024-02-18 10:42:21 +00:00
return h, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return h, nil
2024-02-18 10:42:21 +00:00
}
// SelectFloat executes the given query, which should be a SELECT statement for a single
2024-02-18 10:42:21 +00:00
// float column, and returns the value of the first row returned. If no rows are
2024-02-18 10:42:21 +00:00
// found, zero is returned.
2024-02-18 10:42:21 +00:00
func SelectFloat(e SqlExecutor, query string, args ...interface{}) (float64, error) {
2024-02-18 10:42:21 +00:00
var h float64
2024-02-18 10:42:21 +00:00
err := selectVal(e, &h, query, args...)
2024-02-18 10:42:21 +00:00
if err != nil && err != sql.ErrNoRows {
2024-02-18 10:42:21 +00:00
return 0, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return h, nil
2024-02-18 10:42:21 +00:00
}
// SelectNullFloat executes the given query, which should be a SELECT statement for a single
2024-02-18 10:42:21 +00:00
// float column, and returns the value of the first row returned. If no rows are
2024-02-18 10:42:21 +00:00
// found, the empty sql.NullInt64 value is returned.
2024-02-18 10:42:21 +00:00
func SelectNullFloat(e SqlExecutor, query string, args ...interface{}) (sql.NullFloat64, error) {
2024-02-18 10:42:21 +00:00
var h sql.NullFloat64
2024-02-18 10:42:21 +00:00
err := selectVal(e, &h, query, args...)
2024-02-18 10:42:21 +00:00
if err != nil && err != sql.ErrNoRows {
2024-02-18 10:42:21 +00:00
return h, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return h, nil
2024-02-18 10:42:21 +00:00
}
// SelectStr executes the given query, which should be a SELECT statement for a single
2024-02-18 10:42:21 +00:00
// char/varchar column, and returns the value of the first row returned. If no rows are
2024-02-18 10:42:21 +00:00
// found, an empty string is returned.
2024-02-18 10:42:21 +00:00
func SelectStr(e SqlExecutor, query string, args ...interface{}) (string, error) {
2024-02-18 10:42:21 +00:00
var h string
2024-02-18 10:42:21 +00:00
err := selectVal(e, &h, query, args...)
2024-02-18 10:42:21 +00:00
if err != nil && err != sql.ErrNoRows {
2024-02-18 10:42:21 +00:00
return "", err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return h, nil
2024-02-18 10:42:21 +00:00
}
// SelectNullStr executes the given query, which should be a SELECT
2024-02-18 10:42:21 +00:00
// statement for a single char/varchar column, and returns the value
2024-02-18 10:42:21 +00:00
// of the first row returned. If no rows are found, the empty
2024-02-18 10:42:21 +00:00
// sql.NullString is returned.
2024-02-18 10:42:21 +00:00
func SelectNullStr(e SqlExecutor, query string, args ...interface{}) (sql.NullString, error) {
2024-02-18 10:42:21 +00:00
var h sql.NullString
2024-02-18 10:42:21 +00:00
err := selectVal(e, &h, query, args...)
2024-02-18 10:42:21 +00:00
if err != nil && err != sql.ErrNoRows {
2024-02-18 10:42:21 +00:00
return h, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return h, nil
2024-02-18 10:42:21 +00:00
}
// SelectOne executes the given query (which should be a SELECT statement)
2024-02-18 10:42:21 +00:00
// and binds the result to holder, which must be a pointer.
2024-02-18 10:42:21 +00:00
//
2024-06-14 08:41:36 +00:00
// # If no row is found, an error (sql.ErrNoRows specifically) will be returned
2024-02-18 10:42:21 +00:00
//
2024-02-18 10:42:21 +00:00
// If more than one row is found, an error will be returned.
2024-02-18 10:42:21 +00:00
func SelectOne(m *DbMap, e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
2024-02-18 10:42:21 +00:00
t := reflect.TypeOf(holder)
2024-02-18 10:42:21 +00:00
if t.Kind() == reflect.Ptr {
2024-02-18 10:42:21 +00:00
t = t.Elem()
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("gorp: SelectOne holder must be a pointer, but got: %t", holder)
2024-02-18 10:42:21 +00:00
}
// Handle pointer to pointer
2024-02-18 10:42:21 +00:00
isptr := false
2024-02-18 10:42:21 +00:00
if t.Kind() == reflect.Ptr {
2024-02-18 10:42:21 +00:00
isptr = true
2024-02-18 10:42:21 +00:00
t = t.Elem()
2024-02-18 10:42:21 +00:00
}
if t.Kind() == reflect.Struct {
2024-02-18 10:42:21 +00:00
var nonFatalErr error
list, err := hookedselect(m, e, holder, query, args...)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
if !NonFatalError(err) { // FIXME: double negative, rename NonFatalError to FatalError
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
nonFatalErr = err
2024-02-18 10:42:21 +00:00
}
dest := reflect.ValueOf(holder)
2024-02-18 10:42:21 +00:00
if isptr {
2024-02-18 10:42:21 +00:00
dest = dest.Elem()
2024-02-18 10:42:21 +00:00
}
if list != nil && len(list) > 0 { // FIXME: invert if/else
2024-02-18 10:42:21 +00:00
// check for multiple rows
2024-02-18 10:42:21 +00:00
if len(list) > 1 {
2024-02-18 10:42:21 +00:00
return fmt.Errorf("gorp: multiple rows returned for: %s - %v", query, args)
2024-02-18 10:42:21 +00:00
}
// Initialize if nil
2024-02-18 10:42:21 +00:00
if dest.IsNil() {
2024-02-18 10:42:21 +00:00
dest.Set(reflect.New(t))
2024-02-18 10:42:21 +00:00
}
// only one row found
2024-02-18 10:42:21 +00:00
src := reflect.ValueOf(list[0])
2024-02-18 10:42:21 +00:00
dest.Elem().Set(src.Elem())
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
// No rows found, return a proper error.
2024-02-18 10:42:21 +00:00
return sql.ErrNoRows
2024-02-18 10:42:21 +00:00
}
return nonFatalErr
2024-02-18 10:42:21 +00:00
}
return selectVal(e, holder, query, args...)
2024-02-18 10:42:21 +00:00
}
func selectVal(e SqlExecutor, holder interface{}, query string, args ...interface{}) error {
2024-02-18 10:42:21 +00:00
if len(args) == 1 {
2024-02-18 10:42:21 +00:00
switch m := e.(type) {
2024-02-18 10:42:21 +00:00
case *DbMap:
2024-02-18 10:42:21 +00:00
query, args = maybeExpandNamedQuery(m, query, args)
2024-02-18 10:42:21 +00:00
case *Transaction:
2024-02-18 10:42:21 +00:00
query, args = maybeExpandNamedQuery(m.dbmap, query, args)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
rows, err := e.Query(query, args...)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer rows.Close()
if !rows.Next() {
2024-02-18 10:42:21 +00:00
if err := rows.Err(); err != nil {
2024-02-18 10:42:21 +00:00
return err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return sql.ErrNoRows
2024-02-18 10:42:21 +00:00
}
return rows.Scan(holder)
2024-02-18 10:42:21 +00:00
}
func hookedselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
2024-02-18 10:42:21 +00:00
args ...interface{}) ([]interface{}, error) {
var nonFatalErr error
list, err := rawselect(m, exec, i, query, args...)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
if !NonFatalError(err) {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
nonFatalErr = err
2024-02-18 10:42:21 +00:00
}
// Determine where the results are: written to i, or returned in list
2024-02-18 10:42:21 +00:00
if t, _ := toSliceType(i); t == nil {
2024-02-18 10:42:21 +00:00
for _, v := range list {
2024-02-18 10:42:21 +00:00
if v, ok := v.(HasPostGet); ok {
2024-02-18 10:42:21 +00:00
err := v.PostGet(exec)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
resultsValue := reflect.Indirect(reflect.ValueOf(i))
2024-02-18 10:42:21 +00:00
for i := 0; i < resultsValue.Len(); i++ {
2024-02-18 10:42:21 +00:00
if v, ok := resultsValue.Index(i).Interface().(HasPostGet); ok {
2024-02-18 10:42:21 +00:00
err := v.PostGet(exec)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return list, nonFatalErr
2024-02-18 10:42:21 +00:00
}
func rawselect(m *DbMap, exec SqlExecutor, i interface{}, query string,
2024-02-18 10:42:21 +00:00
args ...interface{}) ([]interface{}, error) {
2024-02-18 10:42:21 +00:00
var (
appendToSlice = false // Write results to i directly?
intoStruct = true // Selecting into a struct?
pointerElements = true // Are the slice elements pointers (vs values)?
2024-02-18 10:42:21 +00:00
)
var nonFatalErr error
tableName := ""
2024-02-18 10:42:21 +00:00
var dynObj DynamicTable
2024-02-18 10:42:21 +00:00
isDynamic := false
2024-02-18 10:42:21 +00:00
if dynObj, isDynamic = i.(DynamicTable); isDynamic {
2024-02-18 10:42:21 +00:00
tableName = dynObj.TableName()
2024-02-18 10:42:21 +00:00
}
// get type for i, verifying it's a supported destination
2024-02-18 10:42:21 +00:00
t, err := toType(i)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
var err2 error
2024-02-18 10:42:21 +00:00
if t, err2 = toSliceType(i); t == nil {
2024-02-18 10:42:21 +00:00
if err2 != nil {
2024-02-18 10:42:21 +00:00
return nil, err2
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
pointerElements = t.Kind() == reflect.Ptr
2024-02-18 10:42:21 +00:00
if pointerElements {
2024-02-18 10:42:21 +00:00
t = t.Elem()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
appendToSlice = true
2024-02-18 10:42:21 +00:00
intoStruct = t.Kind() == reflect.Struct
2024-02-18 10:42:21 +00:00
}
// If the caller supplied a single struct/map argument, assume a "named
2024-02-18 10:42:21 +00:00
// parameter" query. Extract the named arguments from the struct/map, create
2024-02-18 10:42:21 +00:00
// the flat arg slice, and rewrite the query to use the dialect's placeholder.
2024-02-18 10:42:21 +00:00
if len(args) == 1 {
2024-02-18 10:42:21 +00:00
query, args = maybeExpandNamedQuery(m, query, args)
2024-02-18 10:42:21 +00:00
}
// Run the query
2024-02-18 10:42:21 +00:00
rows, err := exec.Query(query, args...)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
defer rows.Close()
// Fetch the column names as returned from db
2024-02-18 10:42:21 +00:00
cols, err := rows.Columns()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
if !intoStruct && len(cols) > 1 {
2024-02-18 10:42:21 +00:00
return nil, fmt.Errorf("gorp: select into non-struct slice requires 1 column, got %d", len(cols))
2024-02-18 10:42:21 +00:00
}
var colToFieldIndex [][]int
2024-02-18 10:42:21 +00:00
if intoStruct {
2024-02-18 10:42:21 +00:00
colToFieldIndex, err = columnToFieldIndex(m, t, tableName, cols)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
if !NonFatalError(err) {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
nonFatalErr = err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
conv := m.TypeConverter
// Add results to one of these two slices.
2024-02-18 10:42:21 +00:00
var (
list = make([]interface{}, 0)
2024-02-18 10:42:21 +00:00
sliceValue = reflect.Indirect(reflect.ValueOf(i))
)
for {
2024-02-18 10:42:21 +00:00
if !rows.Next() {
2024-02-18 10:42:21 +00:00
// if error occured return rawselect
2024-02-18 10:42:21 +00:00
if rows.Err() != nil {
2024-02-18 10:42:21 +00:00
return nil, rows.Err()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
// time to exit from outer "for" loop
2024-02-18 10:42:21 +00:00
break
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
v := reflect.New(t)
if isDynamic {
2024-02-18 10:42:21 +00:00
v.Interface().(DynamicTable).SetTableName(tableName)
2024-02-18 10:42:21 +00:00
}
dest := make([]interface{}, len(cols))
custScan := make([]CustomScanner, 0)
for x := range cols {
2024-02-18 10:42:21 +00:00
f := v.Elem()
2024-02-18 10:42:21 +00:00
if intoStruct {
2024-02-18 10:42:21 +00:00
index := colToFieldIndex[x]
2024-02-18 10:42:21 +00:00
if index == nil {
2024-02-18 10:42:21 +00:00
// this field is not present in the struct, so create a dummy
2024-02-18 10:42:21 +00:00
// value for rows.Scan to scan into
2024-02-18 10:42:21 +00:00
var dummy dummyField
2024-02-18 10:42:21 +00:00
dest[x] = &dummy
2024-02-18 10:42:21 +00:00
continue
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
f = f.FieldByIndex(index)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
target := f.Addr().Interface()
2024-02-18 10:42:21 +00:00
if conv != nil {
2024-02-18 10:42:21 +00:00
scanner, ok := conv.FromDb(target)
2024-02-18 10:42:21 +00:00
if ok {
2024-02-18 10:42:21 +00:00
target = scanner.Holder
2024-02-18 10:42:21 +00:00
custScan = append(custScan, scanner)
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
dest[x] = target
2024-02-18 10:42:21 +00:00
}
err = rows.Scan(dest...)
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
for _, c := range custScan {
2024-02-18 10:42:21 +00:00
err = c.Bind()
2024-02-18 10:42:21 +00:00
if err != nil {
2024-02-18 10:42:21 +00:00
return nil, err
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if appendToSlice {
2024-02-18 10:42:21 +00:00
if !pointerElements {
2024-02-18 10:42:21 +00:00
v = v.Elem()
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
sliceValue.Set(reflect.Append(sliceValue, v))
2024-02-18 10:42:21 +00:00
} else {
2024-02-18 10:42:21 +00:00
list = append(list, v.Interface())
2024-02-18 10:42:21 +00:00
}
2024-02-18 10:42:21 +00:00
}
if appendToSlice && sliceValue.IsNil() {
2024-02-18 10:42:21 +00:00
sliceValue.Set(reflect.MakeSlice(sliceValue.Type(), 0, 0))
2024-02-18 10:42:21 +00:00
}
return list, nonFatalErr
2024-02-18 10:42:21 +00:00
}