Files

766 lines
20 KiB
Go
Raw Permalink Normal View History

2020-01-29 19:22:44 +08:00
package gorm
import (
"context"
"database/sql"
2020-01-30 03:03:06 +08:00
"database/sql/driver"
2020-01-29 19:22:44 +08:00
"fmt"
2020-02-23 19:41:29 +08:00
"reflect"
"regexp"
2020-07-05 12:23:45 +08:00
"sort"
2020-01-30 03:03:06 +08:00
"strconv"
2020-01-29 19:22:44 +08:00
"strings"
"sync"
2020-06-02 09:16:07 +08:00
"gorm.io/gorm/clause"
"gorm.io/gorm/logger"
2020-06-02 09:16:07 +08:00
"gorm.io/gorm/schema"
2020-06-30 16:53:54 +08:00
"gorm.io/gorm/utils"
2020-01-29 19:22:44 +08:00
)
2020-01-30 15:14:48 +08:00
// Statement statement
type Statement struct {
2020-03-09 20:37:01 +08:00
*DB
2020-07-17 21:19:11 +08:00
TableExpr *clause.Expr
2020-03-03 14:18:12 +08:00
Table string
Model interface{}
2020-05-29 07:35:45 +08:00
Unscoped bool
2020-03-03 14:18:12 +08:00
Dest interface{}
ReflectValue reflect.Value
Clauses map[string]clause.Clause
2021-04-28 17:19:30 +08:00
BuildClauses []string
2020-06-05 19:19:08 +08:00
Distinct bool
2024-06-24 17:42:59 +08:00
Selects []string // selected columns
Omits []string // omit columns
ColumnMapping map[string]string // map columns
2020-08-23 10:40:37 +08:00
Joins []join
2020-04-15 09:14:24 +08:00
Preloads map[string][]interface{}
2020-03-03 14:18:12 +08:00
Settings sync.Map
2020-03-09 13:10:48 +08:00
ConnPool ConnPool
2020-03-03 14:18:12 +08:00
Schema *schema.Schema
2020-03-09 13:10:48 +08:00
Context context.Context
2020-03-03 14:18:12 +08:00
RaiseErrorOnNotFound bool
2020-11-17 17:49:43 +08:00
SkipHooks bool
2020-03-09 15:32:55 +08:00
SQL strings.Builder
Vars []interface{}
2020-06-30 22:47:21 +08:00
CurDestIndex int
2020-05-28 13:12:56 +08:00
attrs []interface{}
assigns []interface{}
2021-02-25 18:49:01 +08:00
scopes []func(*DB) *DB
2025-05-25 15:40:40 +08:00
Result *result
2020-01-29 19:22:44 +08:00
}
2020-08-23 10:40:37 +08:00
type join struct {
2025-05-25 15:40:40 +08:00
Name string
Alias string
Conds []interface{}
On *clause.Where
Selects []string
Omits []string
Expression clause.Expression
JoinType clause.JoinType
2020-08-23 10:40:37 +08:00
}
2020-03-12 08:39:42 +08:00
// StatementModifier statement modifier interface
type StatementModifier interface {
ModifyStatement(*Statement)
2020-01-30 15:14:48 +08:00
}
2021-05-31 10:08:06 +08:00
// WriteString write string
2020-03-09 17:07:00 +08:00
func (stmt *Statement) WriteString(str string) (int, error) {
return stmt.SQL.WriteString(str)
2020-01-29 19:22:44 +08:00
}
2021-05-31 10:08:06 +08:00
// WriteByte write byte
2020-03-09 17:07:00 +08:00
func (stmt *Statement) WriteByte(c byte) error {
2020-01-30 15:14:48 +08:00
return stmt.SQL.WriteByte(c)
}
2020-03-08 23:30:16 +08:00
// WriteQuoted write quoted value
2020-07-16 11:27:04 +08:00
func (stmt *Statement) WriteQuoted(value interface{}) {
2020-03-08 23:30:16 +08:00
stmt.QuoteTo(&stmt.SQL, value)
2020-01-29 19:22:44 +08:00
}
2020-03-08 23:30:16 +08:00
// QuoteTo write quoted value to writer
2020-06-08 13:45:41 +08:00
func (stmt *Statement) QuoteTo(writer clause.Writer, field interface{}) {
write := func(raw bool, str string) {
if raw {
writer.WriteString(str)
} else {
stmt.DB.Dialector.QuoteTo(writer, str)
}
}
2020-02-02 14:40:44 +08:00
switch v := field.(type) {
case clause.Table:
2020-02-07 23:45:35 +08:00
if v.Name == clause.CurrentTable {
2020-07-17 21:19:11 +08:00
if stmt.TableExpr != nil {
stmt.TableExpr.Build(stmt)
2020-07-10 21:11:28 +08:00
} else {
write(v.Raw, stmt.Table)
2020-07-10 21:11:28 +08:00
}
2020-02-04 09:51:19 +08:00
} else {
write(v.Raw, v.Name)
2020-02-04 09:51:19 +08:00
}
2020-02-04 08:56:15 +08:00
2020-02-02 14:40:44 +08:00
if v.Alias != "" {
writer.WriteByte(' ')
write(v.Raw, v.Alias)
2020-02-02 14:40:44 +08:00
}
case clause.Column:
if v.Table != "" {
2020-02-04 08:56:15 +08:00
if v.Table == clause.CurrentTable {
write(v.Raw, stmt.Table)
2020-02-04 08:56:15 +08:00
} else {
write(v.Raw, v.Table)
2020-02-04 08:56:15 +08:00
}
2020-03-08 23:30:16 +08:00
writer.WriteByte('.')
2020-02-02 14:40:44 +08:00
}
2020-02-04 08:56:15 +08:00
if v.Name == clause.PrimaryKey {
if stmt.Schema == nil {
2020-07-26 10:03:58 +08:00
stmt.DB.AddError(ErrModelValueRequired)
} else if stmt.Schema.PrioritizedPrimaryField != nil {
write(v.Raw, stmt.Schema.PrioritizedPrimaryField.DBName)
} else if len(stmt.Schema.DBNames) > 0 {
write(v.Raw, stmt.Schema.DBNames[0])
} else {
stmt.DB.AddError(ErrModelAccessibleFieldsRequired) //nolint:typecheck,errcheck
2020-02-04 08:56:15 +08:00
}
} else {
write(v.Raw, v.Name)
2020-02-04 08:56:15 +08:00
}
2020-02-05 11:14:58 +08:00
2020-02-02 14:40:44 +08:00
if v.Alias != "" {
2020-03-08 23:30:16 +08:00
writer.WriteString(" AS ")
write(v.Raw, v.Alias)
2020-02-02 14:40:44 +08:00
}
case []clause.Column:
writer.WriteByte('(')
for idx, d := range v {
if idx > 0 {
writer.WriteByte(',')
}
stmt.QuoteTo(writer, d)
}
writer.WriteByte(')')
2021-08-19 14:33:18 +08:00
case clause.Expr:
v.Build(stmt)
2020-03-12 13:05:22 +08:00
case string:
stmt.DB.Dialector.QuoteTo(writer, v)
2020-05-14 12:19:12 +08:00
case []string:
writer.WriteByte('(')
for idx, d := range v {
2020-06-08 13:45:41 +08:00
if idx > 0 {
writer.WriteByte(',')
2020-05-14 12:19:12 +08:00
}
stmt.DB.Dialector.QuoteTo(writer, d)
}
writer.WriteByte(')')
2020-02-02 14:40:44 +08:00
default:
2020-03-08 23:30:16 +08:00
stmt.DB.Dialector.QuoteTo(writer, fmt.Sprint(field))
2020-02-02 14:40:44 +08:00
}
2020-03-08 23:30:16 +08:00
}
2020-02-02 14:40:44 +08:00
2020-03-08 23:30:16 +08:00
// Quote returns quoted value
2020-06-08 13:45:41 +08:00
func (stmt *Statement) Quote(field interface{}) string {
2020-03-08 23:30:16 +08:00
var builder strings.Builder
stmt.QuoteTo(&builder, field)
return builder.String()
2020-01-30 15:14:48 +08:00
}
2021-05-31 10:08:06 +08:00
// AddVar add var
2020-03-09 17:07:00 +08:00
func (stmt *Statement) AddVar(writer clause.Writer, vars ...interface{}) {
2020-01-30 03:03:06 +08:00
for idx, v := range vars {
if idx > 0 {
2020-03-09 17:07:00 +08:00
writer.WriteByte(',')
2020-01-30 03:03:06 +08:00
}
2020-02-07 23:45:35 +08:00
switch v := v.(type) {
case sql.NamedArg:
2020-07-10 12:28:24 +08:00
stmt.Vars = append(stmt.Vars, v.Value)
2020-02-22 20:57:29 +08:00
case clause.Column, clause.Table:
2020-03-09 17:07:00 +08:00
stmt.QuoteTo(writer, v)
2020-08-27 15:03:57 +08:00
case Valuer:
reflectValue := reflect.ValueOf(v)
if reflectValue.Kind() == reflect.Ptr && reflectValue.IsNil() {
stmt.AddVar(writer, nil)
} else {
stmt.AddVar(writer, v.GormValue(stmt.Context, stmt.DB))
}
case clause.Interface:
c := clause.Clause{Name: v.Name()}
v.MergeClause(&c)
c.Build(stmt)
2022-01-28 18:48:32 +08:00
case clause.Expression:
v.Build(stmt)
2020-05-30 21:05:27 +08:00
case driver.Valuer:
stmt.Vars = append(stmt.Vars, v)
stmt.DB.Dialector.BindVarTo(writer, stmt, v)
2020-06-18 09:32:31 +08:00
case []byte:
stmt.Vars = append(stmt.Vars, v)
stmt.DB.Dialector.BindVarTo(writer, stmt, v)
2020-02-07 23:45:35 +08:00
case []interface{}:
if len(v) > 0 {
2020-03-09 17:07:00 +08:00
writer.WriteByte('(')
stmt.AddVar(writer, v...)
writer.WriteByte(')')
2020-01-30 03:03:06 +08:00
} else {
2020-03-09 17:07:00 +08:00
writer.WriteString("(NULL)")
2020-01-30 03:03:06 +08:00
}
2025-05-25 15:40:40 +08:00
case interface{ getInstance() *DB }:
cv := v.getInstance()
subdb := cv.Session(&Session{Logger: logger.Discard, DryRun: true}).getInstance()
if cv.Statement.SQL.Len() > 0 {
var (
vars = subdb.Statement.Vars
2025-05-25 15:40:40 +08:00
sql = cv.Statement.SQL.String()
)
subdb.Statement.Vars = make([]interface{}, 0, len(vars))
for _, vv := range vars {
subdb.Statement.Vars = append(subdb.Statement.Vars, vv)
bindvar := strings.Builder{}
2025-05-25 15:40:40 +08:00
cv.BindVarTo(&bindvar, subdb.Statement, vv)
sql = strings.Replace(sql, bindvar.String(), "?", 1)
}
subdb.Statement.SQL.Reset()
subdb.Statement.Vars = stmt.Vars
if strings.Contains(sql, "@") {
clause.NamedExpr{SQL: sql, Vars: vars}.Build(subdb.Statement)
} else {
clause.Expr{SQL: sql, Vars: vars}.Build(subdb.Statement)
}
} else {
subdb.Statement.Vars = append(stmt.Vars, subdb.Statement.Vars...)
subdb.callbacks.Query().Execute(subdb)
}
2020-06-01 22:31:50 +08:00
writer.WriteString(subdb.Statement.SQL.String())
stmt.Vars = subdb.Statement.Vars
2020-02-07 23:45:35 +08:00
default:
2020-05-23 16:08:50 +08:00
switch rv := reflect.ValueOf(v); rv.Kind() {
case reflect.Slice, reflect.Array:
if rv.Len() == 0 {
writer.WriteString("(NULL)")
} else if rv.Type().Elem() == reflect.TypeOf(uint8(0)) {
stmt.Vars = append(stmt.Vars, v)
stmt.DB.Dialector.BindVarTo(writer, stmt, v)
2020-05-23 16:08:50 +08:00
} else {
writer.WriteByte('(')
for i := 0; i < rv.Len(); i++ {
if i > 0 {
writer.WriteByte(',')
}
stmt.AddVar(writer, rv.Index(i).Interface())
}
writer.WriteByte(')')
}
default:
stmt.Vars = append(stmt.Vars, v)
stmt.DB.Dialector.BindVarTo(writer, stmt, v)
}
2020-01-29 19:22:44 +08:00
}
}
}
// AddClause add clause
2020-02-03 10:40:03 +08:00
func (stmt *Statement) AddClause(v clause.Interface) {
2020-03-12 08:39:42 +08:00
if optimizer, ok := v.(StatementModifier); ok {
optimizer.ModifyStatement(stmt)
2020-06-06 22:52:08 +08:00
} else {
2020-06-14 11:46:17 +08:00
name := v.Name()
2020-07-16 11:27:04 +08:00
c := stmt.Clauses[name]
2020-06-14 11:46:17 +08:00
c.Name = name
2020-06-06 22:52:08 +08:00
v.MergeClause(&c)
2020-06-14 11:46:17 +08:00
stmt.Clauses[name] = c
2020-01-30 15:14:48 +08:00
}
2020-01-29 19:22:44 +08:00
}
2020-01-30 03:03:06 +08:00
2020-02-03 10:40:03 +08:00
// AddClauseIfNotExists add clause if not exists
func (stmt *Statement) AddClauseIfNotExists(v clause.Interface) {
2020-06-06 22:52:08 +08:00
if c, ok := stmt.Clauses[v.Name()]; !ok || c.Expression == nil {
2020-02-07 23:45:35 +08:00
stmt.AddClause(v)
2020-02-03 10:40:03 +08:00
}
}
// BuildCondition build condition
2020-11-10 18:38:24 +08:00
func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) []clause.Expression {
2020-07-10 12:28:24 +08:00
if s, ok := query.(string); ok {
2020-06-01 10:02:20 +08:00
// if it is a number, then treats it as primary key
2020-07-10 12:28:24 +08:00
if _, err := strconv.Atoi(s); err != nil {
if s == "" && len(args) == 0 {
2020-11-10 18:38:24 +08:00
return nil
}
if len(args) == 0 || (len(args) > 0 && strings.Contains(s, "?")) {
2020-06-05 21:23:20 +08:00
// looks like a where condition
2020-07-10 12:28:24 +08:00
return []clause.Expression{clause.Expr{SQL: s, Vars: args}}
}
if len(args) > 0 && strings.Contains(s, "@") {
2020-07-10 12:28:24 +08:00
// looks like a named query
return []clause.Expression{clause.NamedExpr{SQL: s, Vars: args}}
}
2021-11-23 17:11:52 +08:00
if strings.Contains(strings.TrimSpace(s), " ") {
// looks like a where condition
return []clause.Expression{clause.Expr{SQL: s, Vars: args}}
}
if len(args) == 1 {
2020-07-10 12:28:24 +08:00
return []clause.Expression{clause.Eq{Column: s, Value: args[0]}}
2020-06-01 10:02:20 +08:00
}
2020-01-30 03:03:06 +08:00
}
}
2020-11-10 18:38:24 +08:00
conds := make([]clause.Expression, 0, 4)
2020-01-30 03:03:06 +08:00
args = append([]interface{}{query}, args...)
for idx, arg := range args {
2023-02-02 17:54:51 +08:00
if arg == nil {
continue
}
2020-01-30 03:03:06 +08:00
if valuer, ok := arg.(driver.Valuer); ok {
arg, _ = valuer.Value()
}
2025-05-25 15:40:40 +08:00
curTable := stmt.Table
if curTable == "" {
curTable = clause.CurrentTable
}
2020-01-30 03:03:06 +08:00
switch v := arg.(type) {
2020-01-30 15:14:48 +08:00
case clause.Expression:
2020-05-28 13:12:56 +08:00
conds = append(conds, v)
2020-06-20 01:55:30 +08:00
case *DB:
2023-04-11 12:01:23 +08:00
v.executeScopes()
2022-04-20 17:21:38 +08:00
if cs, ok := v.Statement.Clauses["WHERE"]; ok {
2020-06-20 01:55:30 +08:00
if where, ok := cs.Expression.(clause.Where); ok {
if len(where.Exprs) == 1 {
if orConds, ok := where.Exprs[0].(clause.OrConditions); ok {
2025-07-21 12:57:12 +09:00
if len(orConds.Exprs) == 1 {
where.Exprs[0] = clause.AndConditions(orConds)
}
}
}
2020-06-20 01:55:30 +08:00
conds = append(conds, clause.And(where.Exprs...))
} else if cs.Expression != nil {
2020-06-20 01:55:30 +08:00
conds = append(conds, cs.Expression)
}
}
2020-01-30 03:03:06 +08:00
case map[interface{}]interface{}:
for i, j := range v {
2020-05-28 13:12:56 +08:00
conds = append(conds, clause.Eq{Column: i, Value: j})
2020-01-30 03:03:06 +08:00
}
case map[string]string:
2022-01-06 15:02:53 +08:00
keys := make([]string, 0, len(v))
2020-07-05 12:23:45 +08:00
for i := range v {
keys = append(keys, i)
}
sort.Strings(keys)
for _, key := range keys {
2025-05-25 15:40:40 +08:00
column := clause.Column{Name: key, Table: curTable}
if strings.Contains(key, ".") {
column = clause.Column{Name: key}
}
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.Eq{Column: column, Value: v[key]})
2020-01-30 03:03:06 +08:00
}
case map[string]interface{}:
2022-01-06 15:02:53 +08:00
keys := make([]string, 0, len(v))
2020-07-05 12:23:45 +08:00
for i := range v {
keys = append(keys, i)
}
sort.Strings(keys)
for _, key := range keys {
reflectValue := reflect.Indirect(reflect.ValueOf(v[key]))
2025-05-25 15:40:40 +08:00
column := clause.Column{Name: key, Table: curTable}
if strings.Contains(key, ".") {
column = clause.Column{Name: key}
}
2020-07-05 11:53:10 +08:00
switch reflectValue.Kind() {
case reflect.Slice, reflect.Array:
if _, ok := v[key].(driver.Valuer); ok {
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.Eq{Column: column, Value: v[key]})
} else if _, ok := v[key].(Valuer); ok {
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.Eq{Column: column, Value: v[key]})
} else {
2021-04-19 06:03:39 -07:00
// optimize reflect value length
valueLen := reflectValue.Len()
values := make([]interface{}, valueLen)
for i := 0; i < valueLen; i++ {
values[i] = reflectValue.Index(i).Interface()
}
2020-07-05 11:53:10 +08:00
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.IN{Column: column, Values: values})
}
2020-07-05 11:53:10 +08:00
default:
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.Eq{Column: column, Value: v[key]})
2020-07-05 11:53:10 +08:00
}
2020-01-30 03:03:06 +08:00
}
default:
reflectValue := reflect.Indirect(reflect.ValueOf(arg))
for reflectValue.Kind() == reflect.Ptr {
reflectValue = reflectValue.Elem()
}
if s, err := schema.Parse(arg, stmt.DB.cacheStore, stmt.DB.NamingStrategy); err == nil {
selectedColumns := map[string]bool{}
if idx == 0 {
for _, v := range args[1:] {
if vs, ok := v.(string); ok {
selectedColumns[vs] = true
}
}
}
restricted := len(selectedColumns) != 0
switch reflectValue.Kind() {
case reflect.Struct:
for _, field := range s.Fields {
selected := selectedColumns[field.DBName] || selectedColumns[field.Name]
if selected || (!restricted && field.Readable) {
2022-02-16 15:30:43 +08:00
if v, isZero := field.ValueOf(stmt.Context, reflectValue); !isZero || selected {
if field.DBName != "" {
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.Eq{Column: clause.Column{Table: curTable, Name: field.DBName}, Value: v})
} else if field.DataType != "" {
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.Eq{Column: clause.Column{Table: curTable, Name: field.Name}, Value: v})
}
2020-05-28 16:10:10 +08:00
}
2020-05-28 13:12:56 +08:00
}
}
case reflect.Slice, reflect.Array:
for i := 0; i < reflectValue.Len(); i++ {
2020-05-28 16:10:10 +08:00
for _, field := range s.Fields {
selected := selectedColumns[field.DBName] || selectedColumns[field.Name]
if selected || (!restricted && field.Readable) {
2022-02-16 15:30:43 +08:00
if v, isZero := field.ValueOf(stmt.Context, reflectValue.Index(i)); !isZero || selected {
if field.DBName != "" {
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.Eq{Column: clause.Column{Table: curTable, Name: field.DBName}, Value: v})
} else if field.DataType != "" {
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.Eq{Column: clause.Column{Table: curTable, Name: field.Name}, Value: v})
2020-06-27 08:04:12 +08:00
}
2020-05-28 16:10:10 +08:00
}
2020-05-28 13:12:56 +08:00
}
}
2020-07-05 11:53:10 +08:00
}
}
if restricted {
break
}
} else if !reflectValue.IsValid() {
stmt.AddError(ErrInvalidData)
} else if len(conds) == 0 {
if len(args) == 1 {
switch reflectValue.Kind() {
case reflect.Slice, reflect.Array:
2021-04-19 06:03:39 -07:00
// optimize reflect value length
valueLen := reflectValue.Len()
values := make([]interface{}, valueLen)
for i := 0; i < valueLen; i++ {
values[i] = reflectValue.Index(i).Interface()
}
if len(values) > 0 {
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.IN{Column: clause.Column{Table: curTable, Name: clause.PrimaryKey}, Values: values})
return []clause.Expression{clause.And(conds...)}
}
return nil
}
}
2025-05-25 15:40:40 +08:00
conds = append(conds, clause.IN{Column: clause.Column{Table: curTable, Name: clause.PrimaryKey}, Values: args})
2020-05-28 13:12:56 +08:00
}
2020-01-30 03:03:06 +08:00
}
}
if len(conds) > 0 {
return []clause.Expression{clause.And(conds...)}
}
return nil
2020-01-30 03:03:06 +08:00
}
2020-01-30 15:14:48 +08:00
// Build build sql with clauses names
2020-02-03 10:40:03 +08:00
func (stmt *Statement) Build(clauses ...string) {
2020-02-02 19:32:27 +08:00
var firstClauseWritten bool
2020-01-30 15:14:48 +08:00
for _, name := range clauses {
if c, ok := stmt.Clauses[name]; ok {
2020-02-02 19:32:27 +08:00
if firstClauseWritten {
2020-01-30 15:14:48 +08:00
stmt.WriteByte(' ')
}
2020-02-02 19:32:27 +08:00
firstClauseWritten = true
2020-02-03 10:40:03 +08:00
if b, ok := stmt.DB.ClauseBuilders[name]; ok {
2020-05-29 22:34:35 +08:00
b(c, stmt)
2020-02-03 10:40:03 +08:00
} else {
c.Build(stmt)
}
2020-01-30 15:14:48 +08:00
}
}
2020-01-30 03:03:06 +08:00
}
2020-02-20 23:04:03 +08:00
func (stmt *Statement) Parse(value interface{}) (err error) {
return stmt.ParseWithSpecialTableName(value, "")
}
func (stmt *Statement) ParseWithSpecialTableName(value interface{}, specialTableName string) (err error) {
if stmt.Schema, err = schema.ParseWithSpecialTableName(value, stmt.DB.cacheStore, stmt.DB.NamingStrategy, specialTableName); err == nil && stmt.Table == "" {
2020-07-19 21:30:24 +08:00
if tables := strings.Split(stmt.Schema.Table, "."); len(tables) == 2 {
stmt.TableExpr = &clause.Expr{SQL: stmt.Quote(stmt.Schema.Table)}
stmt.Table = tables[1]
return
}
2020-02-24 08:51:35 +08:00
stmt.Table = stmt.Schema.Table
2020-02-20 23:04:03 +08:00
}
return err
}
2020-03-09 15:32:55 +08:00
2020-05-24 17:24:23 +08:00
func (stmt *Statement) clone() *Statement {
newStmt := &Statement{
2020-08-13 16:28:21 +08:00
TableExpr: stmt.TableExpr,
2020-05-24 17:24:23 +08:00
Table: stmt.Table,
Model: stmt.Model,
Unscoped: stmt.Unscoped,
2020-05-24 17:24:23 +08:00
Dest: stmt.Dest,
ReflectValue: stmt.ReflectValue,
Clauses: map[string]clause.Clause{},
2020-06-05 19:19:08 +08:00
Distinct: stmt.Distinct,
2020-05-24 17:24:23 +08:00
Selects: stmt.Selects,
Omits: stmt.Omits,
2024-06-24 17:42:59 +08:00
ColumnMapping: stmt.ColumnMapping,
2020-05-24 17:24:23 +08:00
Preloads: map[string][]interface{}{},
ConnPool: stmt.ConnPool,
Schema: stmt.Schema,
Context: stmt.Context,
RaiseErrorOnNotFound: stmt.RaiseErrorOnNotFound,
2020-11-17 17:49:43 +08:00
SkipHooks: stmt.SkipHooks,
2025-05-25 15:40:40 +08:00
Result: stmt.Result,
2020-05-24 17:24:23 +08:00
}
2021-02-09 17:05:50 +08:00
if stmt.SQL.Len() > 0 {
newStmt.SQL.WriteString(stmt.SQL.String())
newStmt.Vars = make([]interface{}, 0, len(stmt.Vars))
newStmt.Vars = append(newStmt.Vars, stmt.Vars...)
}
2020-05-24 17:24:23 +08:00
for k, c := range stmt.Clauses {
newStmt.Clauses[k] = c
}
for k, p := range stmt.Preloads {
newStmt.Preloads[k] = p
}
2020-08-23 10:40:37 +08:00
if len(stmt.Joins) > 0 {
newStmt.Joins = make([]join, len(stmt.Joins))
copy(newStmt.Joins, stmt.Joins)
2020-05-24 17:24:23 +08:00
}
2021-02-25 22:01:59 +08:00
if len(stmt.scopes) > 0 {
newStmt.scopes = make([]func(*DB) *DB, len(stmt.scopes))
copy(newStmt.scopes, stmt.scopes)
2021-02-25 18:49:01 +08:00
}
stmt.Settings.Range(func(k, v interface{}) bool {
newStmt.Settings.Store(k, v)
return true
})
2020-05-24 17:24:23 +08:00
return newStmt
}
2020-06-30 16:53:54 +08:00
// SetColumn set column's value
//
// stmt.SetColumn("Name", "jinzhu") // Hooks Method
// stmt.SetColumn("Name", "jinzhu", true) // Callbacks Method
func (stmt *Statement) SetColumn(name string, value interface{}, fromCallbacks ...bool) {
2020-06-30 16:53:54 +08:00
if v, ok := stmt.Dest.(map[string]interface{}); ok {
v[name] = value
} else if v, ok := stmt.Dest.([]map[string]interface{}); ok {
for _, m := range v {
m[name] = value
}
2020-06-30 16:53:54 +08:00
} else if stmt.Schema != nil {
if field := stmt.Schema.LookUpField(name); field != nil {
destValue := reflect.ValueOf(stmt.Dest)
for destValue.Kind() == reflect.Ptr {
destValue = destValue.Elem()
}
if stmt.ReflectValue != destValue {
if !destValue.CanAddr() {
destValueCanAddr := reflect.New(destValue.Type())
destValueCanAddr.Elem().Set(destValue)
stmt.Dest = destValueCanAddr.Interface()
destValue = destValueCanAddr.Elem()
}
switch destValue.Kind() {
case reflect.Struct:
2022-03-23 17:24:25 +08:00
stmt.AddError(field.Set(stmt.Context, destValue, value))
default:
stmt.AddError(ErrInvalidData)
}
}
2020-06-30 22:47:21 +08:00
switch stmt.ReflectValue.Kind() {
case reflect.Slice, reflect.Array:
if len(fromCallbacks) > 0 {
for i := 0; i < stmt.ReflectValue.Len(); i++ {
2022-03-23 17:24:25 +08:00
stmt.AddError(field.Set(stmt.Context, stmt.ReflectValue.Index(i), value))
}
} else {
2022-03-23 17:24:25 +08:00
stmt.AddError(field.Set(stmt.Context, stmt.ReflectValue.Index(stmt.CurDestIndex), value))
}
2020-06-30 22:47:21 +08:00
case reflect.Struct:
2021-05-23 11:21:56 +08:00
if !stmt.ReflectValue.CanAddr() {
stmt.AddError(ErrInvalidValue)
return
}
2022-03-23 17:24:25 +08:00
stmt.AddError(field.Set(stmt.Context, stmt.ReflectValue, value))
2020-06-30 22:47:21 +08:00
}
2020-06-30 16:53:54 +08:00
} else {
stmt.AddError(ErrInvalidField)
}
} else {
stmt.AddError(ErrInvalidField)
}
}
// Changed check model changed or not when updating
func (stmt *Statement) Changed(fields ...string) bool {
modelValue := stmt.ReflectValue
2020-06-30 22:47:21 +08:00
switch modelValue.Kind() {
case reflect.Slice, reflect.Array:
modelValue = stmt.ReflectValue.Index(stmt.CurDestIndex)
}
2020-06-30 16:53:54 +08:00
selectColumns, restricted := stmt.SelectAndOmitColumns(false, true)
changed := func(field *schema.Field) bool {
2022-02-16 15:30:43 +08:00
fieldValue, _ := field.ValueOf(stmt.Context, modelValue)
2020-06-30 16:53:54 +08:00
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
if mv, mok := stmt.Dest.(map[string]interface{}); mok {
if fv, ok := mv[field.Name]; ok {
2020-06-30 16:53:54 +08:00
return !utils.AssertEqual(fv, fieldValue)
} else if fv, ok := mv[field.DBName]; ok {
2020-06-30 16:53:54 +08:00
return !utils.AssertEqual(fv, fieldValue)
}
} else {
destValue := reflect.ValueOf(stmt.Dest)
for destValue.Kind() == reflect.Ptr {
destValue = destValue.Elem()
}
2022-02-16 15:30:43 +08:00
changedValue, zero := field.ValueOf(stmt.Context, destValue)
if v {
return !utils.AssertEqual(changedValue, fieldValue)
}
return !zero && !utils.AssertEqual(changedValue, fieldValue)
2020-06-30 16:53:54 +08:00
}
}
return false
}
if len(fields) == 0 {
for _, field := range stmt.Schema.FieldsByDBName {
if changed(field) {
return true
}
}
} else {
for _, name := range fields {
if field := stmt.Schema.LookUpField(name); field != nil {
if changed(field) {
return true
}
}
}
}
return false
}
var matchName = func() func(tableColumn string) (table, column string) {
nameMatcher := regexp.MustCompile(`^(?:\W?(\w+?)\W?\.)?(?:(\*)|\W?(\w+?)\W?)$`)
return func(tableColumn string) (table, column string) {
if matches := nameMatcher.FindStringSubmatch(tableColumn); len(matches) == 4 {
table = matches[1]
star := matches[2]
columnName := matches[3]
if star != "" {
return table, star
}
return table, columnName
}
return "", ""
}
}()
2020-06-30 16:53:54 +08:00
// SelectAndOmitColumns get select and omit columns, select -> true, omit -> false
func (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpdate bool) (map[string]bool, bool) {
results := map[string]bool{}
notRestricted := false
2023-01-12 16:52:17 +08:00
processColumn := func(column string, result bool) {
if stmt.Schema == nil {
2023-01-12 16:52:17 +08:00
results[column] = result
} else if column == "*" {
2023-01-12 16:52:17 +08:00
notRestricted = result
2020-06-30 16:53:54 +08:00
for _, dbName := range stmt.Schema.DBNames {
2023-01-12 16:52:17 +08:00
results[dbName] = result
2020-06-30 16:53:54 +08:00
}
} else if column == clause.Associations {
2020-06-30 16:53:54 +08:00
for _, rel := range stmt.Schema.Relationships.Relations {
2023-01-12 16:52:17 +08:00
results[rel.Name] = result
2020-06-30 16:53:54 +08:00
}
} else if field := stmt.Schema.LookUpField(column); field != nil && field.DBName != "" {
2023-01-12 16:52:17 +08:00
results[field.DBName] = result
} else if table, col := matchName(column); col != "" && (table == stmt.Table || table == "") {
if col == "*" {
2023-01-12 16:52:17 +08:00
for _, dbName := range stmt.Schema.DBNames {
results[dbName] = result
}
} else {
results[col] = result
2023-01-12 16:52:17 +08:00
}
2020-06-30 16:53:54 +08:00
} else {
2023-01-12 16:52:17 +08:00
results[column] = result
2020-06-30 16:53:54 +08:00
}
}
2023-01-12 16:52:17 +08:00
// select columns
for _, column := range stmt.Selects {
processColumn(column, true)
}
2020-06-30 16:53:54 +08:00
// omit columns
2023-01-12 16:52:17 +08:00
for _, column := range stmt.Omits {
processColumn(column, false)
2020-06-30 16:53:54 +08:00
}
if stmt.Schema != nil {
for _, field := range stmt.Schema.FieldsByName {
2020-06-30 16:53:54 +08:00
name := field.DBName
if name == "" {
name = field.Name
}
if requireCreate && !field.Creatable {
results[name] = false
} else if requireUpdate && !field.Updatable {
results[name] = false
}
}
}
return results, !notRestricted && len(stmt.Selects) > 0
}