2020-01-29 14:22:44 +03:00
|
|
|
package gorm
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"database/sql"
|
2020-01-29 22:03:06 +03:00
|
|
|
"database/sql/driver"
|
2020-01-29 14:22:44 +03:00
|
|
|
"fmt"
|
2020-02-23 14:41:29 +03:00
|
|
|
"reflect"
|
2020-07-05 07:23:45 +03:00
|
|
|
"sort"
|
2020-01-29 22:03:06 +03:00
|
|
|
"strconv"
|
2020-01-29 14:22:44 +03:00
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
|
2020-06-02 04:16:07 +03:00
|
|
|
"gorm.io/gorm/clause"
|
|
|
|
"gorm.io/gorm/schema"
|
2020-06-30 11:53:54 +03:00
|
|
|
"gorm.io/gorm/utils"
|
2020-01-29 14:22:44 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
// Statement statement
|
|
|
|
type Statement struct {
|
2020-03-09 15:37:01 +03:00
|
|
|
*DB
|
2020-03-03 09:18:12 +03:00
|
|
|
Table string
|
|
|
|
Model interface{}
|
2020-05-29 02:35:45 +03:00
|
|
|
Unscoped bool
|
2020-03-03 09:18:12 +03:00
|
|
|
Dest interface{}
|
|
|
|
ReflectValue reflect.Value
|
|
|
|
Clauses map[string]clause.Clause
|
2020-06-05 14:19:08 +03:00
|
|
|
Distinct bool
|
2020-03-03 09:18:12 +03:00
|
|
|
Selects []string // selected columns
|
|
|
|
Omits []string // omit columns
|
2020-04-15 04:14:24 +03:00
|
|
|
Joins map[string][]interface{}
|
|
|
|
Preloads map[string][]interface{}
|
2020-03-03 09:18:12 +03:00
|
|
|
Settings sync.Map
|
2020-03-09 08:10:48 +03:00
|
|
|
ConnPool ConnPool
|
2020-03-03 09:18:12 +03:00
|
|
|
Schema *schema.Schema
|
2020-03-09 08:10:48 +03:00
|
|
|
Context context.Context
|
2020-03-03 09:18:12 +03:00
|
|
|
RaiseErrorOnNotFound bool
|
2020-06-05 15:24:15 +03:00
|
|
|
UpdatingColumn bool
|
2020-03-09 10:32:55 +03:00
|
|
|
SQL strings.Builder
|
|
|
|
Vars []interface{}
|
|
|
|
NamedVars []sql.NamedArg
|
2020-06-30 17:47:21 +03:00
|
|
|
CurDestIndex int
|
2020-05-28 08:12:56 +03:00
|
|
|
attrs []interface{}
|
|
|
|
assigns []interface{}
|
2020-01-29 14:22:44 +03:00
|
|
|
}
|
|
|
|
|
2020-03-12 03:39:42 +03:00
|
|
|
// StatementModifier statement modifier interface
|
|
|
|
type StatementModifier interface {
|
|
|
|
ModifyStatement(*Statement)
|
2020-01-30 10:14:48 +03:00
|
|
|
}
|
|
|
|
|
2020-01-29 14:22:44 +03:00
|
|
|
// Write write string
|
2020-03-09 12:07:00 +03:00
|
|
|
func (stmt *Statement) WriteString(str string) (int, error) {
|
|
|
|
return stmt.SQL.WriteString(str)
|
2020-01-29 14:22:44 +03:00
|
|
|
}
|
|
|
|
|
2020-01-30 10:14:48 +03:00
|
|
|
// Write write string
|
2020-03-09 12:07:00 +03:00
|
|
|
func (stmt *Statement) WriteByte(c byte) error {
|
2020-01-30 10:14:48 +03:00
|
|
|
return stmt.SQL.WriteByte(c)
|
|
|
|
}
|
|
|
|
|
2020-03-08 18:30:16 +03:00
|
|
|
// WriteQuoted write quoted value
|
|
|
|
func (stmt *Statement) WriteQuoted(value interface{}) error {
|
|
|
|
stmt.QuoteTo(&stmt.SQL, value)
|
|
|
|
return nil
|
2020-01-29 14:22:44 +03:00
|
|
|
}
|
|
|
|
|
2020-03-08 18:30:16 +03:00
|
|
|
// QuoteTo write quoted value to writer
|
2020-06-08 08:45:41 +03:00
|
|
|
func (stmt *Statement) QuoteTo(writer clause.Writer, field interface{}) {
|
2020-02-02 09:40:44 +03:00
|
|
|
switch v := field.(type) {
|
|
|
|
case clause.Table:
|
2020-02-07 18:45:35 +03:00
|
|
|
if v.Name == clause.CurrentTable {
|
2020-03-08 18:30:16 +03:00
|
|
|
stmt.DB.Dialector.QuoteTo(writer, stmt.Table)
|
2020-05-24 06:32:59 +03:00
|
|
|
} else if v.Raw {
|
|
|
|
writer.WriteString(v.Name)
|
2020-02-04 04:51:19 +03:00
|
|
|
} else {
|
2020-03-08 18:30:16 +03:00
|
|
|
stmt.DB.Dialector.QuoteTo(writer, v.Name)
|
2020-02-04 04:51:19 +03:00
|
|
|
}
|
2020-02-04 03:56:15 +03:00
|
|
|
|
2020-02-02 09:40:44 +03:00
|
|
|
if v.Alias != "" {
|
2020-03-08 18:30:16 +03:00
|
|
|
writer.WriteString(" AS ")
|
|
|
|
stmt.DB.Dialector.QuoteTo(writer, v.Alias)
|
2020-02-02 09:40:44 +03:00
|
|
|
}
|
|
|
|
case clause.Column:
|
|
|
|
if v.Table != "" {
|
2020-02-04 03:56:15 +03:00
|
|
|
if v.Table == clause.CurrentTable {
|
2020-03-08 18:30:16 +03:00
|
|
|
stmt.DB.Dialector.QuoteTo(writer, stmt.Table)
|
2020-02-04 03:56:15 +03:00
|
|
|
} else {
|
2020-03-08 18:30:16 +03:00
|
|
|
stmt.DB.Dialector.QuoteTo(writer, v.Table)
|
2020-02-04 03:56:15 +03:00
|
|
|
}
|
2020-03-08 18:30:16 +03:00
|
|
|
writer.WriteByte('.')
|
2020-02-02 09:40:44 +03:00
|
|
|
}
|
|
|
|
|
2020-02-04 03:56:15 +03:00
|
|
|
if v.Name == clause.PrimaryKey {
|
|
|
|
if stmt.Schema != nil && stmt.Schema.PrioritizedPrimaryField != nil {
|
2020-03-08 18:30:16 +03:00
|
|
|
stmt.DB.Dialector.QuoteTo(writer, stmt.Schema.PrioritizedPrimaryField.DBName)
|
2020-06-02 06:30:21 +03:00
|
|
|
} else if len(stmt.Schema.DBNames) > 0 {
|
|
|
|
stmt.DB.Dialector.QuoteTo(writer, stmt.Schema.DBNames[0])
|
2020-02-04 03:56:15 +03:00
|
|
|
}
|
2020-05-24 06:32:59 +03:00
|
|
|
} else if v.Raw {
|
|
|
|
writer.WriteString(v.Name)
|
2020-02-04 03:56:15 +03:00
|
|
|
} else {
|
2020-03-08 18:30:16 +03:00
|
|
|
stmt.DB.Dialector.QuoteTo(writer, v.Name)
|
2020-02-04 03:56:15 +03:00
|
|
|
}
|
2020-02-05 06:14:58 +03:00
|
|
|
|
2020-02-02 09:40:44 +03:00
|
|
|
if v.Alias != "" {
|
2020-03-08 18:30:16 +03:00
|
|
|
writer.WriteString(" AS ")
|
|
|
|
stmt.DB.Dialector.QuoteTo(writer, v.Alias)
|
2020-02-02 09:40:44 +03:00
|
|
|
}
|
2020-03-12 08:05:22 +03:00
|
|
|
case string:
|
|
|
|
stmt.DB.Dialector.QuoteTo(writer, v)
|
2020-05-14 07:19:12 +03:00
|
|
|
case []string:
|
|
|
|
writer.WriteByte('(')
|
|
|
|
for idx, d := range v {
|
2020-06-08 08:45:41 +03:00
|
|
|
if idx > 0 {
|
2020-05-14 07:19:12 +03:00
|
|
|
writer.WriteString(",")
|
|
|
|
}
|
|
|
|
stmt.DB.Dialector.QuoteTo(writer, d)
|
|
|
|
}
|
|
|
|
writer.WriteByte(')')
|
2020-02-02 09:40:44 +03:00
|
|
|
default:
|
2020-03-08 18:30:16 +03:00
|
|
|
stmt.DB.Dialector.QuoteTo(writer, fmt.Sprint(field))
|
2020-02-02 09:40:44 +03:00
|
|
|
}
|
2020-03-08 18:30:16 +03:00
|
|
|
}
|
2020-02-02 09:40:44 +03:00
|
|
|
|
2020-03-08 18:30:16 +03:00
|
|
|
// Quote returns quoted value
|
2020-06-08 08:45:41 +03:00
|
|
|
func (stmt *Statement) Quote(field interface{}) string {
|
2020-03-08 18:30:16 +03:00
|
|
|
var builder strings.Builder
|
|
|
|
stmt.QuoteTo(&builder, field)
|
|
|
|
return builder.String()
|
2020-01-30 10:14:48 +03:00
|
|
|
}
|
|
|
|
|
2020-01-29 14:22:44 +03:00
|
|
|
// Write write string
|
2020-03-09 12:07:00 +03:00
|
|
|
func (stmt *Statement) AddVar(writer clause.Writer, vars ...interface{}) {
|
2020-01-29 22:03:06 +03:00
|
|
|
for idx, v := range vars {
|
|
|
|
if idx > 0 {
|
2020-03-09 12:07:00 +03:00
|
|
|
writer.WriteByte(',')
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
|
|
|
|
2020-02-07 18:45:35 +03:00
|
|
|
switch v := v.(type) {
|
|
|
|
case sql.NamedArg:
|
|
|
|
if len(v.Name) > 0 {
|
|
|
|
stmt.NamedVars = append(stmt.NamedVars, v)
|
2020-03-09 12:07:00 +03:00
|
|
|
writer.WriteByte('@')
|
|
|
|
writer.WriteString(v.Name)
|
2020-02-07 18:45:35 +03:00
|
|
|
} else {
|
|
|
|
stmt.Vars = append(stmt.Vars, v.Value)
|
2020-03-09 12:59:54 +03:00
|
|
|
stmt.DB.Dialector.BindVarTo(writer, stmt, v.Value)
|
2020-02-07 18:45:35 +03:00
|
|
|
}
|
2020-02-22 15:57:29 +03:00
|
|
|
case clause.Column, clause.Table:
|
2020-03-09 12:07:00 +03:00
|
|
|
stmt.QuoteTo(writer, v)
|
2020-02-22 15:57:29 +03:00
|
|
|
case clause.Expr:
|
2020-06-02 17:13:53 +03:00
|
|
|
var varStr strings.Builder
|
|
|
|
var sql = v.SQL
|
|
|
|
for _, arg := range v.Vars {
|
|
|
|
stmt.Vars = append(stmt.Vars, arg)
|
|
|
|
stmt.DB.Dialector.BindVarTo(&varStr, stmt, arg)
|
|
|
|
sql = strings.Replace(sql, "?", varStr.String(), 1)
|
|
|
|
varStr.Reset()
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.WriteString(sql)
|
2020-05-30 16:05:27 +03:00
|
|
|
case driver.Valuer:
|
|
|
|
stmt.Vars = append(stmt.Vars, v)
|
|
|
|
stmt.DB.Dialector.BindVarTo(writer, stmt, v)
|
2020-06-18 04:32:31 +03:00
|
|
|
case []byte:
|
|
|
|
stmt.Vars = append(stmt.Vars, v)
|
|
|
|
stmt.DB.Dialector.BindVarTo(writer, stmt, v)
|
2020-02-07 18:45:35 +03:00
|
|
|
case []interface{}:
|
|
|
|
if len(v) > 0 {
|
2020-03-09 12:07:00 +03:00
|
|
|
writer.WriteByte('(')
|
|
|
|
stmt.AddVar(writer, v...)
|
|
|
|
writer.WriteByte(')')
|
2020-01-29 22:03:06 +03:00
|
|
|
} else {
|
2020-03-09 12:07:00 +03:00
|
|
|
writer.WriteString("(NULL)")
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
2020-06-01 16:26:23 +03:00
|
|
|
case *DB:
|
2020-06-01 17:31:50 +03:00
|
|
|
subdb := v.Session(&Session{DryRun: true, WithConditions: true}).getInstance()
|
|
|
|
subdb.Statement.Vars = append(subdb.Statement.Vars, stmt.Vars...)
|
|
|
|
subdb.callbacks.Query().Execute(subdb)
|
|
|
|
writer.WriteString(subdb.Statement.SQL.String())
|
|
|
|
stmt.Vars = subdb.Statement.Vars
|
2020-02-07 18:45:35 +03:00
|
|
|
default:
|
2020-05-23 11:08:50 +03:00
|
|
|
switch rv := reflect.ValueOf(v); rv.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
if rv.Len() == 0 {
|
|
|
|
writer.WriteString("(NULL)")
|
|
|
|
} 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 14:22:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// AddClause add clause
|
2020-02-03 05:40:03 +03:00
|
|
|
func (stmt *Statement) AddClause(v clause.Interface) {
|
2020-03-12 03:39:42 +03:00
|
|
|
if optimizer, ok := v.(StatementModifier); ok {
|
|
|
|
optimizer.ModifyStatement(stmt)
|
2020-06-06 17:52:08 +03:00
|
|
|
} else {
|
2020-06-14 06:46:17 +03:00
|
|
|
name := v.Name()
|
|
|
|
c, _ := stmt.Clauses[name]
|
|
|
|
c.Name = name
|
2020-06-06 17:52:08 +03:00
|
|
|
v.MergeClause(&c)
|
2020-06-14 06:46:17 +03:00
|
|
|
stmt.Clauses[name] = c
|
2020-01-30 10:14:48 +03:00
|
|
|
}
|
2020-01-29 14:22:44 +03:00
|
|
|
}
|
2020-01-29 22:03:06 +03:00
|
|
|
|
2020-02-03 05:40:03 +03:00
|
|
|
// AddClauseIfNotExists add clause if not exists
|
|
|
|
func (stmt *Statement) AddClauseIfNotExists(v clause.Interface) {
|
2020-06-06 17:52:08 +03:00
|
|
|
if c, ok := stmt.Clauses[v.Name()]; !ok || c.Expression == nil {
|
2020-02-07 18:45:35 +03:00
|
|
|
stmt.AddClause(v)
|
2020-02-03 05:40:03 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-08 06:38:51 +03:00
|
|
|
// BuildCondition build condition
|
2020-06-08 08:45:41 +03:00
|
|
|
func (stmt *Statement) BuildCondition(query interface{}, args ...interface{}) (conds []clause.Expression) {
|
2020-01-29 22:03:06 +03:00
|
|
|
if sql, ok := query.(string); ok {
|
2020-06-01 05:02:20 +03:00
|
|
|
// if it is a number, then treats it as primary key
|
|
|
|
if _, err := strconv.Atoi(sql); err != nil {
|
|
|
|
if sql == "" && len(args) == 0 {
|
|
|
|
return
|
|
|
|
} else if len(args) == 0 || (len(args) > 0 && strings.Contains(sql, "?")) || strings.Contains(sql, "@") {
|
2020-06-05 16:23:20 +03:00
|
|
|
// looks like a where condition
|
2020-06-01 05:02:20 +03:00
|
|
|
return []clause.Expression{clause.Expr{SQL: sql, Vars: args}}
|
|
|
|
} else if len(args) == 1 {
|
|
|
|
return []clause.Expression{clause.Eq{Column: sql, Value: args[0]}}
|
|
|
|
}
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
args = append([]interface{}{query}, args...)
|
|
|
|
for _, arg := range args {
|
|
|
|
if valuer, ok := arg.(driver.Valuer); ok {
|
|
|
|
arg, _ = valuer.Value()
|
|
|
|
}
|
|
|
|
|
|
|
|
switch v := arg.(type) {
|
2020-01-30 10:14:48 +03:00
|
|
|
case clause.Expression:
|
2020-05-28 08:12:56 +03:00
|
|
|
conds = append(conds, v)
|
2020-06-19 20:55:30 +03:00
|
|
|
case *DB:
|
|
|
|
if cs, ok := v.Statement.Clauses["WHERE"]; ok {
|
|
|
|
if where, ok := cs.Expression.(clause.Where); ok {
|
|
|
|
conds = append(conds, clause.And(where.Exprs...))
|
|
|
|
} else if cs.Expression != nil {
|
|
|
|
conds = append(conds, cs.Expression)
|
|
|
|
}
|
|
|
|
}
|
2020-01-29 22:03:06 +03:00
|
|
|
case map[interface{}]interface{}:
|
|
|
|
for i, j := range v {
|
2020-05-28 08:12:56 +03:00
|
|
|
conds = append(conds, clause.Eq{Column: i, Value: j})
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
|
|
|
case map[string]string:
|
2020-07-05 07:23:45 +03:00
|
|
|
var keys = make([]string, 0, len(v))
|
|
|
|
for i := range v {
|
|
|
|
keys = append(keys, i)
|
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
|
|
|
for _, key := range keys {
|
|
|
|
conds = append(conds, clause.Eq{Column: key, Value: v[key]})
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
|
|
|
case map[string]interface{}:
|
2020-07-05 07:23:45 +03:00
|
|
|
var keys = make([]string, 0, len(v))
|
|
|
|
for i := range v {
|
|
|
|
keys = append(keys, i)
|
|
|
|
}
|
|
|
|
sort.Strings(keys)
|
|
|
|
|
|
|
|
for _, key := range keys {
|
|
|
|
reflectValue := reflect.Indirect(reflect.ValueOf(v[key]))
|
2020-07-05 06:53:10 +03:00
|
|
|
switch reflectValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
values := make([]interface{}, reflectValue.Len())
|
|
|
|
for i := 0; i < reflectValue.Len(); i++ {
|
|
|
|
values[i] = reflectValue.Index(i).Interface()
|
|
|
|
}
|
|
|
|
|
2020-07-05 07:23:45 +03:00
|
|
|
conds = append(conds, clause.IN{Column: key, Values: values})
|
2020-07-05 06:53:10 +03:00
|
|
|
default:
|
2020-07-05 07:23:45 +03:00
|
|
|
conds = append(conds, clause.Eq{Column: key, Value: v[key]})
|
2020-07-05 06:53:10 +03:00
|
|
|
}
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
|
|
|
default:
|
2020-05-28 08:12:56 +03:00
|
|
|
reflectValue := reflect.Indirect(reflect.ValueOf(arg))
|
|
|
|
if s, err := schema.Parse(arg, stmt.DB.cacheStore, stmt.DB.NamingStrategy); err == nil {
|
|
|
|
switch reflectValue.Kind() {
|
|
|
|
case reflect.Struct:
|
2020-05-28 11:10:10 +03:00
|
|
|
for _, field := range s.Fields {
|
2020-06-27 03:04:12 +03:00
|
|
|
if field.Readable {
|
|
|
|
if v, isZero := field.ValueOf(reflectValue); !isZero {
|
|
|
|
if field.DBName == "" {
|
|
|
|
conds = append(conds, clause.Eq{Column: clause.Column{Table: s.Table, Name: field.Name}, Value: v})
|
|
|
|
} else {
|
|
|
|
conds = append(conds, clause.Eq{Column: clause.Column{Table: s.Table, Name: field.DBName}, Value: v})
|
|
|
|
}
|
2020-05-28 11:10:10 +03:00
|
|
|
}
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
for i := 0; i < reflectValue.Len(); i++ {
|
2020-05-28 11:10:10 +03:00
|
|
|
for _, field := range s.Fields {
|
2020-06-27 03:04:12 +03:00
|
|
|
if field.Readable {
|
|
|
|
if v, isZero := field.ValueOf(reflectValue.Index(i)); !isZero {
|
|
|
|
if field.DBName == "" {
|
|
|
|
conds = append(conds, clause.Eq{Column: clause.Column{Table: s.Table, Name: field.Name}, Value: v})
|
|
|
|
} else {
|
|
|
|
conds = append(conds, clause.Eq{Column: clause.Column{Table: s.Table, Name: field.DBName}, Value: v})
|
|
|
|
}
|
2020-05-28 11:10:10 +03:00
|
|
|
}
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-06-01 05:02:20 +03:00
|
|
|
} else if len(conds) == 0 {
|
2020-07-05 06:53:10 +03:00
|
|
|
if len(args) == 1 {
|
|
|
|
switch reflectValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
values := make([]interface{}, reflectValue.Len())
|
|
|
|
for i := 0; i < reflectValue.Len(); i++ {
|
|
|
|
values[i] = reflectValue.Index(i).Interface()
|
|
|
|
}
|
|
|
|
|
|
|
|
if len(values) > 0 {
|
|
|
|
conds = append(conds, clause.IN{Column: clause.PrimaryColumn, Values: values})
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-01 05:02:20 +03:00
|
|
|
conds = append(conds, clause.IN{Column: clause.PrimaryColumn, Values: args})
|
2020-05-28 08:12:56 +03:00
|
|
|
}
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-05-28 08:12:56 +03:00
|
|
|
return
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
|
|
|
|
2020-01-30 10:14:48 +03:00
|
|
|
// Build build sql with clauses names
|
2020-02-03 05:40:03 +03:00
|
|
|
func (stmt *Statement) Build(clauses ...string) {
|
2020-02-02 14:32:27 +03:00
|
|
|
var firstClauseWritten bool
|
2020-01-30 10:14:48 +03:00
|
|
|
|
|
|
|
for _, name := range clauses {
|
|
|
|
if c, ok := stmt.Clauses[name]; ok {
|
2020-02-02 14:32:27 +03:00
|
|
|
if firstClauseWritten {
|
2020-01-30 10:14:48 +03:00
|
|
|
stmt.WriteByte(' ')
|
|
|
|
}
|
|
|
|
|
2020-02-02 14:32:27 +03:00
|
|
|
firstClauseWritten = true
|
2020-02-03 05:40:03 +03:00
|
|
|
if b, ok := stmt.DB.ClauseBuilders[name]; ok {
|
2020-05-29 17:34:35 +03:00
|
|
|
b(c, stmt)
|
2020-02-03 05:40:03 +03:00
|
|
|
} else {
|
|
|
|
c.Build(stmt)
|
|
|
|
}
|
2020-01-30 10:14:48 +03:00
|
|
|
}
|
|
|
|
}
|
2020-02-02 14:32:27 +03:00
|
|
|
// TODO handle named vars
|
2020-01-29 22:03:06 +03:00
|
|
|
}
|
2020-02-20 18:04:03 +03:00
|
|
|
|
|
|
|
func (stmt *Statement) Parse(value interface{}) (err error) {
|
2020-02-24 03:51:35 +03:00
|
|
|
if stmt.Schema, err = schema.Parse(value, stmt.DB.cacheStore, stmt.DB.NamingStrategy); err == nil && stmt.Table == "" {
|
|
|
|
stmt.Table = stmt.Schema.Table
|
2020-02-20 18:04:03 +03:00
|
|
|
}
|
|
|
|
return err
|
|
|
|
}
|
2020-03-09 10:32:55 +03:00
|
|
|
|
2020-05-24 12:24:23 +03:00
|
|
|
func (stmt *Statement) clone() *Statement {
|
|
|
|
newStmt := &Statement{
|
|
|
|
Table: stmt.Table,
|
|
|
|
Model: stmt.Model,
|
|
|
|
Dest: stmt.Dest,
|
|
|
|
ReflectValue: stmt.ReflectValue,
|
|
|
|
Clauses: map[string]clause.Clause{},
|
2020-06-05 14:19:08 +03:00
|
|
|
Distinct: stmt.Distinct,
|
2020-05-24 12:24:23 +03:00
|
|
|
Selects: stmt.Selects,
|
|
|
|
Omits: stmt.Omits,
|
|
|
|
Joins: map[string][]interface{}{},
|
|
|
|
Preloads: map[string][]interface{}{},
|
|
|
|
ConnPool: stmt.ConnPool,
|
|
|
|
Schema: stmt.Schema,
|
|
|
|
Context: stmt.Context,
|
|
|
|
RaiseErrorOnNotFound: stmt.RaiseErrorOnNotFound,
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, c := range stmt.Clauses {
|
|
|
|
newStmt.Clauses[k] = c
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, p := range stmt.Preloads {
|
|
|
|
newStmt.Preloads[k] = p
|
|
|
|
}
|
|
|
|
|
|
|
|
for k, j := range stmt.Joins {
|
|
|
|
newStmt.Joins[k] = j
|
|
|
|
}
|
|
|
|
|
2020-06-19 07:38:03 +03:00
|
|
|
stmt.Settings.Range(func(k, v interface{}) bool {
|
|
|
|
newStmt.Settings.Store(k, v)
|
|
|
|
return true
|
|
|
|
})
|
|
|
|
|
2020-05-24 12:24:23 +03:00
|
|
|
return newStmt
|
|
|
|
}
|
2020-06-30 11:53:54 +03:00
|
|
|
|
|
|
|
// Helpers
|
|
|
|
// SetColumn set column's value
|
|
|
|
func (stmt *Statement) SetColumn(name string, value interface{}) {
|
|
|
|
if v, ok := stmt.Dest.(map[string]interface{}); ok {
|
|
|
|
v[name] = value
|
|
|
|
} else if stmt.Schema != nil {
|
|
|
|
if field := stmt.Schema.LookUpField(name); field != nil {
|
2020-06-30 17:47:21 +03:00
|
|
|
switch stmt.ReflectValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
field.Set(stmt.ReflectValue.Index(stmt.CurDestIndex), value)
|
|
|
|
case reflect.Struct:
|
|
|
|
field.Set(stmt.ReflectValue, value)
|
|
|
|
}
|
2020-06-30 11:53:54 +03: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 := reflect.ValueOf(stmt.Model)
|
|
|
|
for modelValue.Kind() == reflect.Ptr {
|
|
|
|
modelValue = modelValue.Elem()
|
|
|
|
}
|
|
|
|
|
2020-06-30 17:47:21 +03:00
|
|
|
switch modelValue.Kind() {
|
|
|
|
case reflect.Slice, reflect.Array:
|
|
|
|
modelValue = stmt.ReflectValue.Index(stmt.CurDestIndex)
|
|
|
|
}
|
|
|
|
|
2020-06-30 11:53:54 +03:00
|
|
|
selectColumns, restricted := stmt.SelectAndOmitColumns(false, true)
|
|
|
|
changed := func(field *schema.Field) bool {
|
2020-06-30 17:47:21 +03:00
|
|
|
fieldValue, _ := field.ValueOf(modelValue)
|
2020-06-30 11:53:54 +03:00
|
|
|
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
|
|
|
|
if v, ok := stmt.Dest.(map[string]interface{}); ok {
|
|
|
|
if fv, ok := v[field.Name]; ok {
|
|
|
|
return !utils.AssertEqual(fv, fieldValue)
|
|
|
|
} else if fv, ok := v[field.DBName]; ok {
|
|
|
|
return !utils.AssertEqual(fv, fieldValue)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
changedValue, _ := field.ValueOf(stmt.ReflectValue)
|
|
|
|
return !utils.AssertEqual(changedValue, fieldValue)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
|
|
|
// select columns
|
|
|
|
for _, column := range stmt.Selects {
|
|
|
|
if column == "*" {
|
|
|
|
notRestricted = true
|
|
|
|
for _, dbName := range stmt.Schema.DBNames {
|
|
|
|
results[dbName] = true
|
|
|
|
}
|
|
|
|
} else if column == clause.Associations {
|
|
|
|
for _, rel := range stmt.Schema.Relationships.Relations {
|
|
|
|
results[rel.Name] = true
|
|
|
|
}
|
|
|
|
} else if field := stmt.Schema.LookUpField(column); field != nil && field.DBName != "" {
|
|
|
|
results[field.DBName] = true
|
|
|
|
} else {
|
|
|
|
results[column] = true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// omit columns
|
|
|
|
for _, omit := range stmt.Omits {
|
|
|
|
if omit == clause.Associations {
|
|
|
|
for _, rel := range stmt.Schema.Relationships.Relations {
|
|
|
|
results[rel.Name] = false
|
|
|
|
}
|
|
|
|
} else if field := stmt.Schema.LookUpField(omit); field != nil && field.DBName != "" {
|
|
|
|
results[field.DBName] = false
|
|
|
|
} else {
|
|
|
|
results[omit] = false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if stmt.Schema != nil {
|
|
|
|
for _, field := range stmt.Schema.Fields {
|
|
|
|
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
|
|
|
|
}
|