2019-02-02 16:18:25 +08:00
|
|
|
|
// Copyright 2017-2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
2018-12-14 18:35:51 +08:00
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
2019-02-02 16:18:25 +08:00
|
|
|
|
// You can obtain one at https://github.com/gogf/gf.
|
2018-12-14 18:35:51 +08:00
|
|
|
|
|
|
|
|
|
package gdb
|
|
|
|
|
|
|
|
|
|
import (
|
2019-07-03 19:38:52 +08:00
|
|
|
|
"database/sql"
|
2019-05-28 21:41:00 +08:00
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
2019-06-29 10:45:50 +08:00
|
|
|
|
"reflect"
|
|
|
|
|
"strings"
|
|
|
|
|
"time"
|
|
|
|
|
|
2019-07-29 21:01:19 +08:00
|
|
|
|
"github.com/gogf/gf/text/gregex"
|
|
|
|
|
"github.com/gogf/gf/text/gstr"
|
|
|
|
|
"github.com/gogf/gf/util/gconv"
|
2018-12-14 18:35:51 +08:00
|
|
|
|
)
|
|
|
|
|
|
2019-05-16 13:56:49 +08:00
|
|
|
|
// Type assert api for String().
|
|
|
|
|
type apiString interface {
|
|
|
|
|
String() string
|
|
|
|
|
}
|
|
|
|
|
|
2019-08-24 23:08:17 +08:00
|
|
|
|
// 格式化SQL语句.
|
2019-07-09 12:04:24 +08:00
|
|
|
|
func formatQuery(query string, args []interface{}) (newQuery string, newArgs []interface{}) {
|
2019-08-24 23:08:17 +08:00
|
|
|
|
return handlerSliceArguments(query, args)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理预处理占位符与slice类型的参数。
|
|
|
|
|
// 需要注意的是,
|
|
|
|
|
// 如果是链式操作,在条件参数中也会调用该方法处理查询参数,
|
|
|
|
|
// 如果是方法参数,在sql提交执行之前也会再次调用该方法处理查询语句和参数。
|
|
|
|
|
func handlerSliceArguments(query string, args []interface{}) (newQuery string, newArgs []interface{}) {
|
2019-07-09 12:04:24 +08:00
|
|
|
|
newQuery = query
|
|
|
|
|
// 查询条件参数处理,主要处理slice参数类型
|
|
|
|
|
if len(args) > 0 {
|
|
|
|
|
for index, arg := range args {
|
|
|
|
|
rv := reflect.ValueOf(arg)
|
|
|
|
|
kind := rv.Kind()
|
|
|
|
|
if kind == reflect.Ptr {
|
|
|
|
|
rv = rv.Elem()
|
|
|
|
|
kind = rv.Kind()
|
|
|
|
|
}
|
|
|
|
|
switch kind {
|
|
|
|
|
// '?'占位符支持slice类型, 这里会将slice参数拆散,并更新原有占位符'?'为多个'?',使用','符号连接。
|
|
|
|
|
case reflect.Slice, reflect.Array:
|
2019-07-24 21:21:08 +08:00
|
|
|
|
if rv.Len() == 0 {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2019-07-22 23:03:55 +08:00
|
|
|
|
// 不拆分[]byte类型
|
|
|
|
|
if _, ok := arg.([]byte); ok {
|
|
|
|
|
newArgs = append(newArgs, arg)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2019-07-09 12:04:24 +08:00
|
|
|
|
for i := 0; i < rv.Len(); i++ {
|
|
|
|
|
newArgs = append(newArgs, rv.Index(i).Interface())
|
|
|
|
|
}
|
|
|
|
|
// 如果参数直接传递slice,并且占位符数量与slice长度相等,
|
|
|
|
|
// 那么不用替换扩展占位符数量,直接使用该slice作为查询参数
|
|
|
|
|
if len(args) == 1 && gstr.Count(newQuery, "?") == rv.Len() {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
// counter用于匹配该参数的位置(与index对应)
|
|
|
|
|
counter := 0
|
|
|
|
|
newQuery, _ = gregex.ReplaceStringFunc(`\?`, newQuery, func(s string) string {
|
|
|
|
|
counter++
|
|
|
|
|
if counter == index+1 {
|
|
|
|
|
return "?" + strings.Repeat(",?", rv.Len()-1)
|
|
|
|
|
}
|
|
|
|
|
return s
|
|
|
|
|
})
|
|
|
|
|
default:
|
|
|
|
|
newArgs = append(newArgs, arg)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-17 22:26:41 +08:00
|
|
|
|
// 将预处理参数转换为底层数据库引擎支持的格式。
|
|
|
|
|
// 主要是判断参数是否为复杂数据类型,如果是,那么转换为基础类型。
|
|
|
|
|
func convertParam(value interface{}) interface{} {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
rv := reflect.ValueOf(value)
|
|
|
|
|
kind := rv.Kind()
|
|
|
|
|
if kind == reflect.Ptr {
|
|
|
|
|
rv = rv.Elem()
|
|
|
|
|
kind = rv.Kind()
|
|
|
|
|
}
|
|
|
|
|
switch kind {
|
|
|
|
|
case reflect.Struct:
|
|
|
|
|
// 底层数据库引擎支持 time.Time/*time.Time 类型
|
|
|
|
|
if v, ok := value.(time.Time); ok {
|
|
|
|
|
if v.IsZero() {
|
|
|
|
|
return "null"
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
if v, ok := value.(*time.Time); ok {
|
|
|
|
|
if v.IsZero() {
|
|
|
|
|
return ""
|
|
|
|
|
}
|
|
|
|
|
return value
|
|
|
|
|
}
|
|
|
|
|
return gconv.String(value)
|
|
|
|
|
}
|
|
|
|
|
return value
|
2019-03-17 22:26:41 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-12-14 18:35:51 +08:00
|
|
|
|
// 格式化错误信息
|
|
|
|
|
func formatError(err error, query string, args ...interface{}) error {
|
2019-07-03 19:38:52 +08:00
|
|
|
|
if err != nil && err != sql.ErrNoRows {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
errStr := fmt.Sprintf("DB ERROR: %s\n", err.Error())
|
|
|
|
|
errStr += fmt.Sprintf("DB QUERY: %s\n", query)
|
|
|
|
|
if len(args) > 0 {
|
|
|
|
|
errStr += fmt.Sprintf("DB PARAM: %v\n", args)
|
|
|
|
|
}
|
|
|
|
|
err = errors.New(errStr)
|
|
|
|
|
}
|
|
|
|
|
return err
|
2018-12-14 18:35:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 根据insert选项获得操作名称
|
2018-12-15 15:50:39 +08:00
|
|
|
|
func getInsertOperationByOption(option int) string {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
operator := "INSERT"
|
|
|
|
|
switch option {
|
|
|
|
|
case OPTION_REPLACE:
|
|
|
|
|
operator = "REPLACE"
|
|
|
|
|
case OPTION_SAVE:
|
|
|
|
|
case OPTION_IGNORE:
|
|
|
|
|
operator = "INSERT IGNORE"
|
|
|
|
|
}
|
|
|
|
|
return operator
|
2018-12-14 18:35:51 +08:00
|
|
|
|
}
|
2019-05-16 13:56:49 +08:00
|
|
|
|
|
|
|
|
|
// 将对象转换为map,如果对象带有继承对象,那么执行递归转换。
|
|
|
|
|
// 该方法用于将变量传递给数据库执行之前。
|
|
|
|
|
func structToMap(obj interface{}) map[string]interface{} {
|
|
|
|
|
data := gconv.Map(obj)
|
2019-05-28 21:41:00 +08:00
|
|
|
|
for key, value := range data {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
rv := reflect.ValueOf(value)
|
2019-05-16 13:56:49 +08:00
|
|
|
|
kind := rv.Kind()
|
|
|
|
|
if kind == reflect.Ptr {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
rv = rv.Elem()
|
2019-05-16 13:56:49 +08:00
|
|
|
|
kind = rv.Kind()
|
|
|
|
|
}
|
|
|
|
|
switch kind {
|
2019-06-19 09:06:52 +08:00
|
|
|
|
case reflect.Struct:
|
|
|
|
|
// 底层数据库引擎支持 time.Time/*time.Time 类型
|
|
|
|
|
if _, ok := value.(time.Time); ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
if _, ok := value.(*time.Time); ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
// 如果执行String方法,那么执行字符串转换
|
|
|
|
|
if s, ok := value.(apiString); ok {
|
|
|
|
|
data[key] = s.String()
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
delete(data, key)
|
|
|
|
|
for k, v := range structToMap(value) {
|
|
|
|
|
data[k] = v
|
|
|
|
|
}
|
2019-05-16 13:56:49 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return data
|
2019-05-28 21:41:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 使用递归的方式将map键值对映射到struct对象上,注意参数<pointer>是一个指向struct的指针。
|
|
|
|
|
func mapToStruct(data map[string]interface{}, pointer interface{}) error {
|
|
|
|
|
return gconv.StructDeep(data, pointer)
|
2019-06-19 09:06:52 +08:00
|
|
|
|
}
|