2017-01-03 11:20:28 +03:00
// Copyright 2016 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xorm
import (
"errors"
"fmt"
"reflect"
2019-06-23 18:22:43 +03:00
"sort"
2017-01-03 11:20:28 +03:00
"strconv"
"strings"
2019-08-27 05:17:23 +03:00
"xorm.io/builder"
2019-06-23 18:22:43 +03:00
"xorm.io/core"
2017-01-03 11:20:28 +03:00
)
// Insert insert one or more beans
func ( session * Session ) Insert ( beans ... interface { } ) ( int64 , error ) {
var affected int64
var err error
2017-08-22 14:39:52 +03:00
if session . isAutoClose {
2017-01-03 11:20:28 +03:00
defer session . Close ( )
}
2019-09-25 22:02:54 +03:00
session . autoResetStatement = false
defer func ( ) {
session . autoResetStatement = true
session . resetStatement ( )
} ( )
2017-01-03 11:20:28 +03:00
for _ , bean := range beans {
2019-06-23 18:22:43 +03:00
switch bean . ( type ) {
case map [ string ] interface { } :
cnt , err := session . insertMapInterface ( bean . ( map [ string ] interface { } ) )
if err != nil {
return affected , err
}
affected += cnt
case [ ] map [ string ] interface { } :
s := bean . ( [ ] map [ string ] interface { } )
for i := 0 ; i < len ( s ) ; i ++ {
cnt , err := session . insertMapInterface ( s [ i ] )
if err != nil {
return affected , err
}
affected += cnt
}
case map [ string ] string :
cnt , err := session . insertMapString ( bean . ( map [ string ] string ) )
if err != nil {
return affected , err
}
affected += cnt
case [ ] map [ string ] string :
s := bean . ( [ ] map [ string ] string )
for i := 0 ; i < len ( s ) ; i ++ {
cnt , err := session . insertMapString ( s [ i ] )
if err != nil {
return affected , err
}
affected += cnt
}
default :
sliceValue := reflect . Indirect ( reflect . ValueOf ( bean ) )
if sliceValue . Kind ( ) == reflect . Slice {
size := sliceValue . Len ( )
if size > 0 {
if session . engine . SupportInsertMany ( ) {
cnt , err := session . innerInsertMulti ( bean )
2017-01-03 11:20:28 +03:00
if err != nil {
return affected , err
}
affected += cnt
2019-06-23 18:22:43 +03:00
} else {
for i := 0 ; i < size ; i ++ {
cnt , err := session . innerInsert ( sliceValue . Index ( i ) . Interface ( ) )
if err != nil {
return affected , err
}
affected += cnt
}
2017-01-03 11:20:28 +03:00
}
}
2019-06-23 18:22:43 +03:00
} else {
cnt , err := session . innerInsert ( bean )
if err != nil {
return affected , err
}
affected += cnt
2017-01-03 11:20:28 +03:00
}
}
}
return affected , err
}
func ( session * Session ) innerInsertMulti ( rowsSlicePtr interface { } ) ( int64 , error ) {
sliceValue := reflect . Indirect ( reflect . ValueOf ( rowsSlicePtr ) )
if sliceValue . Kind ( ) != reflect . Slice {
return 0 , errors . New ( "needs a pointer to a slice" )
}
if sliceValue . Len ( ) <= 0 {
return 0 , errors . New ( "could not insert a empty slice" )
}
2018-07-20 05:10:17 +03:00
if err := session . statement . setRefBean ( sliceValue . Index ( 0 ) . Interface ( ) ) ; err != nil {
2017-05-02 03:50:33 +03:00
return 0 , err
}
2017-01-03 11:20:28 +03:00
2018-07-20 05:10:17 +03:00
tableName := session . statement . TableName ( )
if len ( tableName ) <= 0 {
2017-01-03 11:20:28 +03:00
return 0 , ErrTableNotFound
}
2017-08-22 14:39:52 +03:00
table := session . statement . RefTable
2017-01-03 11:20:28 +03:00
size := sliceValue . Len ( )
var colNames [ ] string
var colMultiPlaces [ ] string
var args [ ] interface { }
var cols [ ] * core . Column
for i := 0 ; i < size ; i ++ {
v := sliceValue . Index ( i )
vv := reflect . Indirect ( v )
elemValue := v . Interface ( )
var colPlaces [ ] string
// handle BeforeInsertProcessor
// !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
for _ , closure := range session . beforeClosures {
closure ( elemValue )
}
if processor , ok := interface { } ( elemValue ) . ( BeforeInsertProcessor ) ; ok {
processor . BeforeInsert ( )
}
// --
if i == 0 {
for _ , col := range table . Columns ( ) {
ptrFieldValue , err := col . ValueOfV ( & vv )
if err != nil {
return 0 , err
}
fieldValue := * ptrFieldValue
if col . IsAutoIncrement && isZero ( fieldValue . Interface ( ) ) {
continue
}
if col . MapType == core . ONLYFROMDB {
continue
}
if col . IsDeleted {
continue
}
2018-07-20 05:10:17 +03:00
if session . statement . omitColumnMap . contain ( col . Name ) {
continue
2017-01-03 11:20:28 +03:00
}
2018-07-20 05:10:17 +03:00
if len ( session . statement . columnMap ) > 0 && ! session . statement . columnMap . contain ( col . Name ) {
continue
2017-01-03 11:20:28 +03:00
}
2017-08-22 14:39:52 +03:00
if ( col . IsCreated || col . IsUpdated ) && session . statement . UseAutoTime {
2017-10-01 19:52:35 +03:00
val , t := session . engine . nowTime ( col )
2017-01-03 11:20:28 +03:00
args = append ( args , val )
var colName = col . Name
session . afterClosures = append ( session . afterClosures , func ( bean interface { } ) {
col := table . GetColumn ( colName )
setColumnTime ( bean , col , t )
} )
2017-08-22 14:39:52 +03:00
} else if col . IsVersion && session . statement . checkVersion {
2017-01-03 11:20:28 +03:00
args = append ( args , 1 )
var colName = col . Name
session . afterClosures = append ( session . afterClosures , func ( bean interface { } ) {
col := table . GetColumn ( colName )
setColumnInt ( bean , col , 1 )
} )
} else {
arg , err := session . value2Interface ( col , fieldValue )
if err != nil {
return 0 , err
}
args = append ( args , arg )
}
colNames = append ( colNames , col . Name )
cols = append ( cols , col )
colPlaces = append ( colPlaces , "?" )
}
} else {
for _ , col := range cols {
ptrFieldValue , err := col . ValueOfV ( & vv )
if err != nil {
return 0 , err
}
fieldValue := * ptrFieldValue
if col . IsAutoIncrement && isZero ( fieldValue . Interface ( ) ) {
continue
}
if col . MapType == core . ONLYFROMDB {
continue
}
if col . IsDeleted {
continue
}
2018-07-20 05:10:17 +03:00
if session . statement . omitColumnMap . contain ( col . Name ) {
continue
2017-01-03 11:20:28 +03:00
}
2018-07-20 05:10:17 +03:00
if len ( session . statement . columnMap ) > 0 && ! session . statement . columnMap . contain ( col . Name ) {
continue
2017-01-03 11:20:28 +03:00
}
2017-08-22 14:39:52 +03:00
if ( col . IsCreated || col . IsUpdated ) && session . statement . UseAutoTime {
2017-10-01 19:52:35 +03:00
val , t := session . engine . nowTime ( col )
2017-01-03 11:20:28 +03:00
args = append ( args , val )
var colName = col . Name
session . afterClosures = append ( session . afterClosures , func ( bean interface { } ) {
col := table . GetColumn ( colName )
setColumnTime ( bean , col , t )
} )
2017-08-22 14:39:52 +03:00
} else if col . IsVersion && session . statement . checkVersion {
2017-01-03 11:20:28 +03:00
args = append ( args , 1 )
var colName = col . Name
session . afterClosures = append ( session . afterClosures , func ( bean interface { } ) {
col := table . GetColumn ( colName )
setColumnInt ( bean , col , 1 )
} )
} else {
arg , err := session . value2Interface ( col , fieldValue )
if err != nil {
return 0 , err
}
args = append ( args , arg )
}
colPlaces = append ( colPlaces , "?" )
}
}
colMultiPlaces = append ( colMultiPlaces , strings . Join ( colPlaces , ", " ) )
}
cleanupProcessorsClosures ( & session . beforeClosures )
2018-07-20 05:10:17 +03:00
var sql string
2017-08-22 14:39:52 +03:00
if session . engine . dialect . DBType ( ) == core . ORACLE {
2019-08-27 05:17:23 +03:00
temp := fmt . Sprintf ( ") INTO %s (%v) VALUES (" ,
2017-09-13 08:18:22 +03:00
session . engine . Quote ( tableName ) ,
2019-08-27 05:17:23 +03:00
quoteColumns ( colNames , session . engine . Quote , "," ) )
sql = fmt . Sprintf ( "INSERT ALL INTO %s (%v) VALUES (%v) SELECT 1 FROM DUAL" ,
2017-09-13 08:18:22 +03:00
session . engine . Quote ( tableName ) ,
2019-08-27 05:17:23 +03:00
quoteColumns ( colNames , session . engine . Quote , "," ) ,
2017-04-07 04:47:25 +03:00
strings . Join ( colMultiPlaces , temp ) )
} else {
2019-08-27 05:17:23 +03:00
sql = fmt . Sprintf ( "INSERT INTO %s (%v) VALUES (%v)" ,
2017-09-13 08:18:22 +03:00
session . engine . Quote ( tableName ) ,
2019-08-27 05:17:23 +03:00
quoteColumns ( colNames , session . engine . Quote , "," ) ,
2017-04-07 04:47:25 +03:00
strings . Join ( colMultiPlaces , "),(" ) )
}
2018-07-20 05:10:17 +03:00
res , err := session . exec ( sql , args ... )
2017-01-03 11:20:28 +03:00
if err != nil {
return 0 , err
}
2018-07-20 05:10:17 +03:00
session . cacheInsert ( tableName )
2017-01-03 11:20:28 +03:00
lenAfterClosures := len ( session . afterClosures )
for i := 0 ; i < size ; i ++ {
elemValue := reflect . Indirect ( sliceValue . Index ( i ) ) . Addr ( ) . Interface ( )
// handle AfterInsertProcessor
2017-08-22 14:39:52 +03:00
if session . isAutoCommit {
2017-01-03 11:20:28 +03:00
// !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
for _ , closure := range session . afterClosures {
closure ( elemValue )
}
if processor , ok := interface { } ( elemValue ) . ( AfterInsertProcessor ) ; ok {
processor . AfterInsert ( )
}
} else {
if lenAfterClosures > 0 {
if value , has := session . afterInsertBeans [ elemValue ] ; has && value != nil {
* value = append ( * value , session . afterClosures ... )
} else {
afterClosures := make ( [ ] func ( interface { } ) , lenAfterClosures )
copy ( afterClosures , session . afterClosures )
session . afterInsertBeans [ elemValue ] = & afterClosures
}
} else {
if _ , ok := interface { } ( elemValue ) . ( AfterInsertProcessor ) ; ok {
session . afterInsertBeans [ elemValue ] = nil
}
}
}
}
cleanupProcessorsClosures ( & session . afterClosures )
return res . RowsAffected ( )
}
// InsertMulti insert multiple records
func ( session * Session ) InsertMulti ( rowsSlicePtr interface { } ) ( int64 , error ) {
2017-08-22 14:39:52 +03:00
if session . isAutoClose {
2017-01-03 11:20:28 +03:00
defer session . Close ( )
}
sliceValue := reflect . Indirect ( reflect . ValueOf ( rowsSlicePtr ) )
if sliceValue . Kind ( ) != reflect . Slice {
return 0 , ErrParamsType
}
if sliceValue . Len ( ) <= 0 {
return 0 , nil
}
return session . innerInsertMulti ( rowsSlicePtr )
}
func ( session * Session ) innerInsert ( bean interface { } ) ( int64 , error ) {
2018-07-20 05:10:17 +03:00
if err := session . statement . setRefBean ( bean ) ; err != nil {
2017-05-02 03:50:33 +03:00
return 0 , err
}
2017-08-22 14:39:52 +03:00
if len ( session . statement . TableName ( ) ) <= 0 {
2017-01-03 11:20:28 +03:00
return 0 , ErrTableNotFound
}
2017-08-22 14:39:52 +03:00
table := session . statement . RefTable
2017-01-03 11:20:28 +03:00
// handle BeforeInsertProcessor
for _ , closure := range session . beforeClosures {
closure ( bean )
}
cleanupProcessorsClosures ( & session . beforeClosures ) // cleanup after used
if processor , ok := interface { } ( bean ) . ( BeforeInsertProcessor ) ; ok {
processor . BeforeInsert ( )
}
2018-07-20 05:10:17 +03:00
colNames , args , err := session . genInsertColumns ( bean )
2017-01-03 11:20:28 +03:00
if err != nil {
return 0 , err
}
2019-09-24 16:22:39 +03:00
exprs := session . statement . exprColumns
colPlaces := strings . Repeat ( "?, " , len ( colNames ) )
if exprs . Len ( ) <= 0 && len ( colPlaces ) > 0 {
colPlaces = colPlaces [ 0 : len ( colPlaces ) - 2 ]
2017-01-03 11:20:28 +03:00
}
2017-09-13 08:18:22 +03:00
var tableName = session . statement . TableName ( )
2019-06-23 18:22:43 +03:00
var output string
if session . engine . dialect . DBType ( ) == core . MSSQL && len ( table . AutoIncrement ) > 0 {
output = fmt . Sprintf ( " OUTPUT Inserted.%s" , table . AutoIncrement )
}
2019-08-27 05:17:23 +03:00
2019-09-24 16:22:39 +03:00
var buf = builder . NewWriter ( )
if _ , err := buf . WriteString ( fmt . Sprintf ( "INSERT INTO %s" , session . engine . Quote ( tableName ) ) ) ; err != nil {
return 0 , err
}
if len ( colPlaces ) <= 0 {
if session . engine . dialect . DBType ( ) == core . MYSQL {
if _ , err := buf . WriteString ( " VALUES ()" ) ; err != nil {
2019-08-27 05:17:23 +03:00
return 0 , err
}
} else {
2019-09-24 16:22:39 +03:00
if _ , err := buf . WriteString ( fmt . Sprintf ( "%s DEFAULT VALUES" , output ) ) ; err != nil {
return 0 , err
}
2019-08-27 05:17:23 +03:00
}
2017-08-22 14:39:52 +03:00
} else {
2019-09-24 16:22:39 +03:00
if _ , err := buf . WriteString ( " (" ) ; err != nil {
return 0 , err
}
if err := writeStrings ( buf , append ( colNames , exprs . colNames ... ) , "`" , "`" ) ; err != nil {
return 0 , err
}
if session . statement . cond . IsValid ( ) {
if _ , err := buf . WriteString ( fmt . Sprintf ( ")%s SELECT " , output ) ) ; err != nil {
return 0 , err
}
if err := session . statement . writeArgs ( buf , args ) ; err != nil {
return 0 , err
}
if len ( exprs . args ) > 0 {
if _ , err := buf . WriteString ( "," ) ; err != nil {
return 0 , err
}
}
if err := exprs . writeArgs ( buf ) ; err != nil {
return 0 , err
}
if _ , err := buf . WriteString ( fmt . Sprintf ( " FROM %v WHERE " , session . engine . Quote ( tableName ) ) ) ; err != nil {
return 0 , err
}
if err := session . statement . cond . WriteTo ( buf ) ; err != nil {
return 0 , err
}
2017-08-22 14:39:52 +03:00
} else {
2019-09-24 16:22:39 +03:00
buf . Append ( args ... )
if _ , err := buf . WriteString ( fmt . Sprintf ( ")%s VALUES (%v" ,
output ,
colPlaces ) ) ; err != nil {
return 0 , err
}
if err := exprs . writeArgs ( buf ) ; err != nil {
return 0 , err
}
if _ , err := buf . WriteString ( ")" ) ; err != nil {
return 0 , err
}
2017-08-22 14:39:52 +03:00
}
}
2017-01-03 11:20:28 +03:00
2019-06-23 18:22:43 +03:00
if len ( table . AutoIncrement ) > 0 && session . engine . dialect . DBType ( ) == core . POSTGRES {
2019-09-24 16:22:39 +03:00
if _ , err := buf . WriteString ( " RETURNING " + session . engine . Quote ( table . AutoIncrement ) ) ; err != nil {
return 0 , err
}
2019-06-23 18:22:43 +03:00
}
2019-09-24 16:22:39 +03:00
sqlStr := buf . String ( )
args = buf . Args ( )
2017-01-03 11:20:28 +03:00
handleAfterInsertProcessorFunc := func ( bean interface { } ) {
2017-08-22 14:39:52 +03:00
if session . isAutoCommit {
2017-01-03 11:20:28 +03:00
for _ , closure := range session . afterClosures {
closure ( bean )
}
if processor , ok := interface { } ( bean ) . ( AfterInsertProcessor ) ; ok {
processor . AfterInsert ( )
}
} else {
lenAfterClosures := len ( session . afterClosures )
if lenAfterClosures > 0 {
if value , has := session . afterInsertBeans [ bean ] ; has && value != nil {
* value = append ( * value , session . afterClosures ... )
} else {
afterClosures := make ( [ ] func ( interface { } ) , lenAfterClosures )
copy ( afterClosures , session . afterClosures )
session . afterInsertBeans [ bean ] = & afterClosures
}
} else {
if _ , ok := interface { } ( bean ) . ( AfterInsertProcessor ) ; ok {
session . afterInsertBeans [ bean ] = nil
}
}
}
cleanupProcessorsClosures ( & session . afterClosures ) // cleanup after used
}
// for postgres, many of them didn't implement lastInsertId, so we should
// implemented it ourself.
2017-08-22 14:39:52 +03:00
if session . engine . dialect . DBType ( ) == core . ORACLE && len ( table . AutoIncrement ) > 0 {
2017-09-13 08:18:22 +03:00
res , err := session . queryBytes ( "select seq_atable.currval from dual" , args ... )
2017-01-03 11:20:28 +03:00
if err != nil {
return 0 , err
}
2018-01-27 18:20:59 +03:00
defer handleAfterInsertProcessorFunc ( bean )
2017-01-03 11:20:28 +03:00
2018-07-20 05:10:17 +03:00
session . cacheInsert ( tableName )
2017-01-03 11:20:28 +03:00
2017-08-22 14:39:52 +03:00
if table . Version != "" && session . statement . checkVersion {
2017-01-03 11:20:28 +03:00
verValue , err := table . VersionColumn ( ) . ValueOf ( bean )
if err != nil {
2017-08-22 14:39:52 +03:00
session . engine . logger . Error ( err )
2017-01-03 11:20:28 +03:00
} else if verValue . IsValid ( ) && verValue . CanSet ( ) {
2018-12-12 04:01:41 +03:00
session . incrVersionFieldValue ( verValue )
2017-01-03 11:20:28 +03:00
}
}
if len ( res ) < 1 {
return 0 , errors . New ( "insert no error but not returned id" )
}
idByte := res [ 0 ] [ table . AutoIncrement ]
id , err := strconv . ParseInt ( string ( idByte ) , 10 , 64 )
if err != nil || id <= 0 {
return 1 , err
}
aiValue , err := table . AutoIncrColumn ( ) . ValueOf ( bean )
if err != nil {
2017-08-22 14:39:52 +03:00
session . engine . logger . Error ( err )
2017-01-03 11:20:28 +03:00
}
if aiValue == nil || ! aiValue . IsValid ( ) || ! aiValue . CanSet ( ) {
return 1 , nil
}
aiValue . Set ( int64ToIntValue ( id , aiValue . Type ( ) ) )
return 1 , nil
2019-06-23 18:22:43 +03:00
} else if len ( table . AutoIncrement ) > 0 && ( session . engine . dialect . DBType ( ) == core . POSTGRES || session . engine . dialect . DBType ( ) == core . MSSQL ) {
2017-09-13 08:18:22 +03:00
res , err := session . queryBytes ( sqlStr , args ... )
2017-01-03 11:20:28 +03:00
if err != nil {
return 0 , err
}
2018-01-27 18:20:59 +03:00
defer handleAfterInsertProcessorFunc ( bean )
2017-01-03 11:20:28 +03:00
2018-07-20 05:10:17 +03:00
session . cacheInsert ( tableName )
2017-01-03 11:20:28 +03:00
2017-08-22 14:39:52 +03:00
if table . Version != "" && session . statement . checkVersion {
2017-01-03 11:20:28 +03:00
verValue , err := table . VersionColumn ( ) . ValueOf ( bean )
if err != nil {
2017-08-22 14:39:52 +03:00
session . engine . logger . Error ( err )
2017-01-03 11:20:28 +03:00
} else if verValue . IsValid ( ) && verValue . CanSet ( ) {
2018-12-12 04:01:41 +03:00
session . incrVersionFieldValue ( verValue )
2017-01-03 11:20:28 +03:00
}
}
if len ( res ) < 1 {
2019-06-23 18:22:43 +03:00
return 0 , errors . New ( "insert successfully but not returned id" )
2017-01-03 11:20:28 +03:00
}
idByte := res [ 0 ] [ table . AutoIncrement ]
id , err := strconv . ParseInt ( string ( idByte ) , 10 , 64 )
if err != nil || id <= 0 {
return 1 , err
}
aiValue , err := table . AutoIncrColumn ( ) . ValueOf ( bean )
if err != nil {
2017-08-22 14:39:52 +03:00
session . engine . logger . Error ( err )
2017-01-03 11:20:28 +03:00
}
if aiValue == nil || ! aiValue . IsValid ( ) || ! aiValue . CanSet ( ) {
return 1 , nil
}
aiValue . Set ( int64ToIntValue ( id , aiValue . Type ( ) ) )
return 1 , nil
} else {
res , err := session . exec ( sqlStr , args ... )
if err != nil {
return 0 , err
}
defer handleAfterInsertProcessorFunc ( bean )
2018-07-20 05:10:17 +03:00
session . cacheInsert ( tableName )
2017-01-03 11:20:28 +03:00
2017-08-22 14:39:52 +03:00
if table . Version != "" && session . statement . checkVersion {
2017-01-03 11:20:28 +03:00
verValue , err := table . VersionColumn ( ) . ValueOf ( bean )
if err != nil {
2017-08-22 14:39:52 +03:00
session . engine . logger . Error ( err )
2017-01-03 11:20:28 +03:00
} else if verValue . IsValid ( ) && verValue . CanSet ( ) {
2018-12-12 04:01:41 +03:00
session . incrVersionFieldValue ( verValue )
2017-01-03 11:20:28 +03:00
}
}
if table . AutoIncrement == "" {
return res . RowsAffected ( )
}
var id int64
id , err = res . LastInsertId ( )
if err != nil || id <= 0 {
return res . RowsAffected ( )
}
aiValue , err := table . AutoIncrColumn ( ) . ValueOf ( bean )
if err != nil {
2017-08-22 14:39:52 +03:00
session . engine . logger . Error ( err )
2017-01-03 11:20:28 +03:00
}
if aiValue == nil || ! aiValue . IsValid ( ) || ! aiValue . CanSet ( ) {
return res . RowsAffected ( )
}
aiValue . Set ( int64ToIntValue ( id , aiValue . Type ( ) ) )
return res . RowsAffected ( )
}
}
// InsertOne insert only one struct into database as a record.
// The in parameter bean must a struct or a point to struct. The return
// parameter is inserted and error
func ( session * Session ) InsertOne ( bean interface { } ) ( int64 , error ) {
2017-08-22 14:39:52 +03:00
if session . isAutoClose {
2017-01-03 11:20:28 +03:00
defer session . Close ( )
}
return session . innerInsert ( bean )
}
2018-07-20 05:10:17 +03:00
func ( session * Session ) cacheInsert ( table string ) error {
if ! session . statement . UseCache {
return nil
2017-01-03 11:20:28 +03:00
}
2018-07-20 05:10:17 +03:00
cacher := session . engine . getCacher ( table )
if cacher == nil {
return nil
2017-01-03 11:20:28 +03:00
}
2018-07-20 05:10:17 +03:00
session . engine . logger . Debug ( "[cache] clear sql:" , table )
cacher . ClearIds ( table )
2017-01-03 11:20:28 +03:00
return nil
}
2018-07-20 05:10:17 +03:00
// genInsertColumns generates insert needed columns
func ( session * Session ) genInsertColumns ( bean interface { } ) ( [ ] string , [ ] interface { } , error ) {
table := session . statement . RefTable
colNames := make ( [ ] string , 0 , len ( table . ColumnsSeq ( ) ) )
args := make ( [ ] interface { } , 0 , len ( table . ColumnsSeq ( ) ) )
for _ , col := range table . Columns ( ) {
if col . MapType == core . ONLYFROMDB {
continue
}
if col . IsDeleted {
continue
}
if session . statement . omitColumnMap . contain ( col . Name ) {
continue
}
if len ( session . statement . columnMap ) > 0 && ! session . statement . columnMap . contain ( col . Name ) {
continue
}
2019-09-24 16:22:39 +03:00
if session . statement . incrColumns . isColExist ( col . Name ) {
2018-07-20 05:10:17 +03:00
continue
2019-09-24 16:22:39 +03:00
} else if session . statement . decrColumns . isColExist ( col . Name ) {
continue
} else if session . statement . exprColumns . isColExist ( col . Name ) {
2018-07-20 05:10:17 +03:00
continue
}
fieldValuePtr , err := col . ValueOf ( bean )
if err != nil {
return nil , nil , err
}
fieldValue := * fieldValuePtr
if col . IsAutoIncrement {
switch fieldValue . Type ( ) . Kind ( ) {
case reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int , reflect . Int64 :
if fieldValue . Int ( ) == 0 {
continue
}
case reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint , reflect . Uint64 :
if fieldValue . Uint ( ) == 0 {
continue
}
case reflect . String :
if len ( fieldValue . String ( ) ) == 0 {
continue
}
case reflect . Ptr :
if fieldValue . Pointer ( ) == 0 {
continue
}
}
}
// !evalphobia! set fieldValue as nil when column is nullable and zero-value
if _ , ok := getFlagForColumn ( session . statement . nullableMap , col ) ; ok {
if col . Nullable && isZero ( fieldValue . Interface ( ) ) {
var nilValue * int
fieldValue = reflect . ValueOf ( nilValue )
}
}
if ( col . IsCreated || col . IsUpdated ) && session . statement . UseAutoTime /*&& isZero(fieldValue.Interface())*/ {
// if time is non-empty, then set to auto time
val , t := session . engine . nowTime ( col )
args = append ( args , val )
var colName = col . Name
session . afterClosures = append ( session . afterClosures , func ( bean interface { } ) {
col := table . GetColumn ( colName )
setColumnTime ( bean , col , t )
} )
} else if col . IsVersion && session . statement . checkVersion {
args = append ( args , 1 )
} else {
arg , err := session . value2Interface ( col , fieldValue )
if err != nil {
return colNames , args , err
}
args = append ( args , arg )
}
colNames = append ( colNames , col . Name )
}
return colNames , args , nil
}
2019-06-23 18:22:43 +03:00
func ( session * Session ) insertMapInterface ( m map [ string ] interface { } ) ( int64 , error ) {
if len ( m ) == 0 {
return 0 , ErrParamsType
}
2019-08-27 05:17:23 +03:00
tableName := session . statement . TableName ( )
if len ( tableName ) <= 0 {
return 0 , ErrTableNotFound
}
2019-06-23 18:22:43 +03:00
var columns = make ( [ ] string , 0 , len ( m ) )
2019-09-24 16:22:39 +03:00
exprs := session . statement . exprColumns
2019-06-23 18:22:43 +03:00
for k := range m {
2019-09-24 16:22:39 +03:00
if ! exprs . isColExist ( k ) {
columns = append ( columns , k )
}
2019-06-23 18:22:43 +03:00
}
sort . Strings ( columns )
var args = make ( [ ] interface { } , 0 , len ( m ) )
for _ , colName := range columns {
args = append ( args , m [ colName ] )
}
2020-01-20 18:45:14 +03:00
return session . insertMap ( columns , args )
2019-06-23 18:22:43 +03:00
}
func ( session * Session ) insertMapString ( m map [ string ] string ) ( int64 , error ) {
if len ( m ) == 0 {
return 0 , ErrParamsType
}
2019-08-27 05:17:23 +03:00
tableName := session . statement . TableName ( )
if len ( tableName ) <= 0 {
return 0 , ErrTableNotFound
}
2019-06-23 18:22:43 +03:00
var columns = make ( [ ] string , 0 , len ( m ) )
2019-09-24 16:22:39 +03:00
exprs := session . statement . exprColumns
2019-06-23 18:22:43 +03:00
for k := range m {
2019-09-24 16:22:39 +03:00
if ! exprs . isColExist ( k ) {
columns = append ( columns , k )
}
2019-06-23 18:22:43 +03:00
}
2020-01-20 18:45:14 +03:00
2019-06-23 18:22:43 +03:00
sort . Strings ( columns )
2019-08-27 05:17:23 +03:00
var args = make ( [ ] interface { } , 0 , len ( m ) )
for _ , colName := range columns {
args = append ( args , m [ colName ] )
}
2020-01-20 18:45:14 +03:00
return session . insertMap ( columns , args )
}
func ( session * Session ) insertMap ( columns [ ] string , args [ ] interface { } ) ( int64 , error ) {
tableName := session . statement . TableName ( )
if len ( tableName ) <= 0 {
return 0 , ErrTableNotFound
}
exprs := session . statement . exprColumns
2019-09-24 16:22:39 +03:00
w := builder . NewWriter ( )
2020-01-20 18:45:14 +03:00
// if insert where
2019-09-24 16:22:39 +03:00
if session . statement . cond . IsValid ( ) {
if _ , err := w . WriteString ( fmt . Sprintf ( "INSERT INTO %s (" , session . engine . Quote ( tableName ) ) ) ; err != nil {
return 0 , err
}
2019-06-23 18:22:43 +03:00
2019-09-24 16:22:39 +03:00
if err := writeStrings ( w , append ( columns , exprs . colNames ... ) , "`" , "`" ) ; err != nil {
return 0 , err
}
2019-06-23 18:22:43 +03:00
2019-09-24 16:22:39 +03:00
if _ , err := w . WriteString ( ") SELECT " ) ; err != nil {
return 0 , err
}
2019-08-27 05:17:23 +03:00
2019-09-24 16:22:39 +03:00
if err := session . statement . writeArgs ( w , args ) ; err != nil {
return 0 , err
}
2019-08-27 05:17:23 +03:00
2019-09-24 16:22:39 +03:00
if len ( exprs . args ) > 0 {
if _ , err := w . WriteString ( "," ) ; err != nil {
return 0 , err
}
if err := exprs . writeArgs ( w ) ; err != nil {
return 0 , err
}
}
if _ , err := w . WriteString ( fmt . Sprintf ( " FROM %s WHERE " , session . engine . Quote ( tableName ) ) ) ; err != nil {
return 0 , err
}
if err := session . statement . cond . WriteTo ( w ) ; err != nil {
2019-08-27 05:17:23 +03:00
return 0 , err
}
} else {
2019-09-24 16:22:39 +03:00
qm := strings . Repeat ( "?," , len ( columns ) )
qm = qm [ : len ( qm ) - 1 ]
2020-01-20 18:45:14 +03:00
if _ , err := w . WriteString ( fmt . Sprintf ( "INSERT INTO %s (" , session . engine . Quote ( tableName ) ) ) ; err != nil {
2019-09-24 16:22:39 +03:00
return 0 , err
}
2020-01-20 18:45:14 +03:00
if err := writeStrings ( w , append ( columns , exprs . colNames ... ) , "`" , "`" ) ; err != nil {
return 0 , err
}
if _ , err := w . WriteString ( fmt . Sprintf ( ") VALUES (%s" , qm ) ) ; err != nil {
return 0 , err
}
2019-09-24 16:22:39 +03:00
w . Append ( args ... )
2020-01-20 18:45:14 +03:00
if len ( exprs . args ) > 0 {
if _ , err := w . WriteString ( "," ) ; err != nil {
return 0 , err
}
if err := exprs . writeArgs ( w ) ; err != nil {
return 0 , err
}
}
if _ , err := w . WriteString ( ")" ) ; err != nil {
return 0 , err
}
2019-06-23 18:22:43 +03:00
}
2019-09-24 16:22:39 +03:00
sql := w . String ( )
args = w . Args ( )
2019-06-23 18:22:43 +03:00
if err := session . cacheInsert ( tableName ) ; err != nil {
return 0 , err
}
res , err := session . exec ( sql , args ... )
if err != nil {
return 0 , err
}
affected , err := res . RowsAffected ( )
if err != nil {
return 0 , err
}
return affected , nil
}