2013-05-03 07:26:51 +00:00
package xorm
import (
2013-12-18 03:31:32 +00:00
"database/sql"
"encoding/json"
"errors"
"fmt"
2014-01-14 10:53:00 +00:00
"hash/crc32"
2013-12-18 03:31:32 +00:00
"reflect"
"strconv"
"strings"
"time"
2014-01-07 09:33:27 +00:00
2014-04-11 07:37:27 +00:00
"github.com/go-xorm/core"
2013-05-03 07:26:51 +00:00
)
2013-10-12 15:16:51 +00:00
// Struct Session keep a pointer to sql.DB and provides all execution of all
// kind of database operations.
2013-05-03 07:26:51 +00:00
type Session struct {
2014-02-11 06:59:04 +00:00
Db * core . DB
2013-12-18 03:31:32 +00:00
Engine * Engine
2014-02-11 06:59:04 +00:00
Tx * core . Tx
2013-12-18 03:31:32 +00:00
Statement Statement
IsAutoCommit bool
IsCommitedOrRollbacked bool
TransType string
IsAutoClose bool
2014-05-30 16:28:51 +00:00
// Automatically reset the statement after operations that execute a SQL
// query such as Count(), Find(), Get(), ...
AutoResetStatement bool
2013-12-18 03:31:32 +00:00
// !nashtsai! storing these beans due to yet committed tx
afterInsertBeans map [ interface { } ] * [ ] func ( interface { } )
afterUpdateBeans map [ interface { } ] * [ ] func ( interface { } )
afterDeleteBeans map [ interface { } ] * [ ] func ( interface { } )
// --
beforeClosures [ ] func ( interface { } )
afterClosures [ ] func ( interface { } )
2014-01-14 10:53:00 +00:00
2014-07-09 13:48:48 +00:00
stmtCache map [ uint32 ] * core . Stmt //key: hash.Hash32 of (queryStr, len(queryStr))
cascadeDeep int
2013-05-03 07:26:51 +00:00
}
2013-10-12 15:16:51 +00:00
// Method Init reset the session as the init status.
2013-05-03 07:26:51 +00:00
func ( session * Session ) Init ( ) {
2013-12-18 03:31:32 +00:00
session . Statement = Statement { Engine : session . Engine }
session . Statement . Init ( )
session . IsAutoCommit = true
session . IsCommitedOrRollbacked = false
session . IsAutoClose = false
2014-05-30 16:28:51 +00:00
session . AutoResetStatement = true
2013-12-02 14:54:55 +00:00
2013-12-18 03:31:32 +00:00
// !nashtsai! is lazy init better?
session . afterInsertBeans = make ( map [ interface { } ] * [ ] func ( interface { } ) , 0 )
session . afterUpdateBeans = make ( map [ interface { } ] * [ ] func ( interface { } ) , 0 )
session . afterDeleteBeans = make ( map [ interface { } ] * [ ] func ( interface { } ) , 0 )
session . beforeClosures = make ( [ ] func ( interface { } ) , 0 )
session . afterClosures = make ( [ ] func ( interface { } ) , 0 )
2013-05-03 07:26:51 +00:00
}
2013-10-12 15:16:51 +00:00
// Method Close release the connection from pool
2013-05-03 07:26:51 +00:00
func ( session * Session ) Close ( ) {
2014-01-14 10:53:00 +00:00
for _ , v := range session . stmtCache {
v . Close ( )
}
if session . Db != nil {
2014-04-15 03:39:29 +00:00
//session.Engine.Pool.ReleaseDB(session.Engine, session.Db)
2014-01-14 10:53:00 +00:00
session . Db = nil
session . Tx = nil
session . stmtCache = nil
session . Init ( )
}
2013-05-03 07:26:51 +00:00
}
2014-05-30 16:28:51 +00:00
func ( session * Session ) resetStatement ( ) {
if session . AutoResetStatement {
session . Statement . Init ( )
}
}
2013-10-12 15:16:51 +00:00
// Method Sql provides raw sql input parameter. When you have a complex SQL statement
// and cannot use Where, Id, In and etc. Methods to describe, you can use Sql.
2013-06-16 03:05:16 +00:00
func ( session * Session ) Sql ( querystring string , args ... interface { } ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Sql ( querystring , args ... )
return session
2013-06-16 03:05:16 +00:00
}
2013-10-12 15:16:51 +00:00
// Method Where provides custom query condition.
2013-05-06 08:01:17 +00:00
func ( session * Session ) Where ( querystring string , args ... interface { } ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Where ( querystring , args ... )
return session
2013-05-03 07:26:51 +00:00
}
2013-11-09 13:13:16 +00:00
// Method Where provides custom query condition.
func ( session * Session ) And ( querystring string , args ... interface { } ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . And ( querystring , args ... )
return session
2013-11-09 13:13:16 +00:00
}
// Method Where provides custom query condition.
func ( session * Session ) Or ( querystring string , args ... interface { } ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Or ( querystring , args ... )
return session
2013-11-09 13:13:16 +00:00
}
2013-10-12 15:16:51 +00:00
// Method Id provides converting id as a query condition
2013-12-17 01:38:20 +00:00
func ( session * Session ) Id ( id interface { } ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Id ( id )
return session
2013-05-09 01:56:58 +00:00
}
2013-12-02 06:23:04 +00:00
// Apply before Processor, affected bean is passed to closure arg
func ( session * Session ) Before ( closures func ( interface { } ) ) * Session {
2013-12-18 03:31:32 +00:00
if closures != nil {
session . beforeClosures = append ( session . beforeClosures , closures )
}
return session
2013-12-02 14:54:55 +00:00
}
2013-11-30 09:50:04 +00:00
2013-12-02 06:23:04 +00:00
// Apply after Processor, affected bean is passed to closure arg
func ( session * Session ) After ( closures func ( interface { } ) ) * Session {
2013-12-18 03:31:32 +00:00
if closures != nil {
session . afterClosures = append ( session . afterClosures , closures )
}
return session
2013-12-02 06:23:04 +00:00
}
2013-11-30 09:50:04 +00:00
2014-01-07 09:33:27 +00:00
// Method core.Table can input a string or pointer to struct for special a table to operate.
2013-08-29 05:18:02 +00:00
func ( session * Session ) Table ( tableNameOrBean interface { } ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Table ( tableNameOrBean )
return session
2013-05-19 05:25:52 +00:00
}
2014-11-18 16:41:03 +00:00
// set the table alias
func ( session * Session ) Alias ( alias string ) * Session {
session . Statement . Alias ( alias )
return session
}
2013-10-12 15:16:51 +00:00
// Method In provides a query string like "id in (1, 2, 3)"
2013-05-11 08:27:17 +00:00
func ( session * Session ) In ( column string , args ... interface { } ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . In ( column , args ... )
return session
2013-05-11 08:27:17 +00:00
}
2014-04-15 01:54:49 +00:00
// Method In provides a query string like "count = count + 1"
2014-04-15 04:14:18 +00:00
func ( session * Session ) Incr ( column string , arg ... interface { } ) * Session {
session . Statement . Incr ( column , arg ... )
2014-04-15 01:54:49 +00:00
return session
}
2014-07-15 15:32:20 +00:00
// Method Decr provides a query string like "count = count - 1"
2014-07-15 15:25:24 +00:00
func ( session * Session ) Decr ( column string , arg ... interface { } ) * Session {
session . Statement . Decr ( column , arg ... )
return session
}
2013-10-12 15:16:51 +00:00
// Method Cols provides some columns to special
2013-08-08 05:24:38 +00:00
func ( session * Session ) Cols ( columns ... string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Cols ( columns ... )
return session
2013-08-08 05:24:38 +00:00
}
2014-03-24 12:41:07 +00:00
func ( session * Session ) AllCols ( ) * Session {
session . Statement . AllCols ( )
return session
}
2014-04-06 04:58:16 +00:00
func ( session * Session ) MustCols ( columns ... string ) * Session {
session . Statement . MustCols ( columns ... )
return session
}
2013-12-19 14:32:00 +00:00
func ( session * Session ) NoCascade ( ) * Session {
session . Statement . UseCascade = false
return session
}
2013-11-22 06:11:07 +00:00
// Xorm automatically retrieve condition according struct, but
// if struct has bool field, it will ignore them. So use UseBool
// to tell system to do not ignore them.
// If no paramters, it will use all the bool field of struct, or
// it will use paramters's columns
2013-11-15 02:16:08 +00:00
func ( session * Session ) UseBool ( columns ... string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . UseBool ( columns ... )
return session
2013-11-15 02:16:08 +00:00
}
2013-11-22 06:11:07 +00:00
// use for distinct columns. Caution: when you are using cache,
// distinct will not be cached because cache system need id,
// but distinct will not provide id
2013-11-14 15:07:33 +00:00
func ( session * Session ) Distinct ( columns ... string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Distinct ( columns ... )
return session
2013-11-14 15:07:33 +00:00
}
2013-11-22 06:11:07 +00:00
// Only not use the paramters as select or update columns
2013-10-17 04:50:46 +00:00
func ( session * Session ) Omit ( columns ... string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Omit ( columns ... )
return session
2013-10-17 04:50:46 +00:00
}
2013-10-12 15:16:51 +00:00
// Method NoAutoTime means do not automatically give created field and updated field
// the current time on the current session temporarily
2013-09-17 09:36:34 +00:00
func ( session * Session ) NoAutoTime ( ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . UseAutoTime = false
return session
2013-08-08 05:24:38 +00:00
}
2013-10-12 15:16:51 +00:00
// Method Limit provide limit and offset query condition
2013-05-06 08:01:17 +00:00
func ( session * Session ) Limit ( limit int , start ... int ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Limit ( limit , start ... )
return session
2013-05-03 07:26:51 +00:00
}
2013-10-12 15:16:51 +00:00
// Method OrderBy provide order by query condition, the input parameter is the content
// after order by on a sql statement.
2013-05-03 07:26:51 +00:00
func ( session * Session ) OrderBy ( order string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . OrderBy ( order )
return session
2013-05-03 07:26:51 +00:00
}
2013-10-12 15:16:51 +00:00
// Method Desc provide desc order by query condition, the input parameters are columns.
2013-09-02 02:06:32 +00:00
func ( session * Session ) Desc ( colNames ... string ) * Session {
2014-08-18 13:20:18 +00:00
session . Statement . Desc ( colNames ... )
2013-12-18 03:31:32 +00:00
return session
2013-09-02 01:54:37 +00:00
}
2013-10-12 15:16:51 +00:00
// Method Asc provide asc order by query condition, the input parameters are columns.
2013-09-02 02:06:32 +00:00
func ( session * Session ) Asc ( colNames ... string ) * Session {
2014-11-03 03:05:49 +00:00
session . Statement . Asc ( colNames ... )
2013-12-18 03:31:32 +00:00
return session
2013-09-02 01:54:37 +00:00
}
2013-10-12 15:16:51 +00:00
// Method StoreEngine is only avialble mysql dialect currently
2013-07-27 04:24:38 +00:00
func ( session * Session ) StoreEngine ( storeEngine string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . StoreEngine = storeEngine
return session
2013-07-27 04:24:38 +00:00
}
2014-05-07 12:14:33 +00:00
// Method Charset is only avialble mysql dialect currently
2013-07-27 04:24:38 +00:00
func ( session * Session ) Charset ( charset string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Charset = charset
return session
2013-07-27 04:24:38 +00:00
}
2013-11-22 06:11:07 +00:00
// Method Cascade indicates if loading sub Struct
2013-06-04 08:56:59 +00:00
func ( session * Session ) Cascade ( trueOrFalse ... bool ) * Session {
2013-12-18 03:31:32 +00:00
if len ( trueOrFalse ) >= 1 {
session . Statement . UseCascade = trueOrFalse [ 0 ]
}
return session
2013-06-04 08:56:59 +00:00
}
2013-10-12 15:16:51 +00:00
// Method NoCache ask this session do not retrieve data from cache system and
// get data from database directly.
2013-09-17 09:36:34 +00:00
func ( session * Session ) NoCache ( ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . UseCache = false
return session
2013-09-17 09:36:34 +00:00
}
2013-05-03 07:26:51 +00:00
//The join_operator should be one of INNER, LEFT OUTER, CROSS etc - this will be prepended to JOIN
2014-11-18 16:41:03 +00:00
func ( session * Session ) Join ( join_operator string , tablename interface { } , condition string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Join ( join_operator , tablename , condition )
return session
2013-05-03 07:26:51 +00:00
}
2013-11-22 06:11:07 +00:00
// Generate Group By statement
2013-05-03 07:26:51 +00:00
func ( session * Session ) GroupBy ( keys string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . GroupBy ( keys )
return session
2013-05-03 07:26:51 +00:00
}
2013-11-22 06:11:07 +00:00
// Generate Having statement
2013-05-03 07:26:51 +00:00
func ( session * Session ) Having ( conditions string ) * Session {
2013-12-18 03:31:32 +00:00
session . Statement . Having ( conditions )
return session
2013-05-03 07:26:51 +00:00
}
2013-06-16 03:05:16 +00:00
func ( session * Session ) newDb ( ) error {
2013-12-18 03:31:32 +00:00
if session . Db == nil {
2014-04-15 03:39:29 +00:00
/ * db , err := session . Engine . Pool . RetrieveDB ( session . Engine )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
2014-04-15 03:39:29 +00:00
} * /
session . Db = session . Engine . db
2014-02-11 06:59:04 +00:00
session . stmtCache = make ( map [ uint32 ] * core . Stmt , 0 )
2013-12-18 03:31:32 +00:00
}
return nil
2013-06-16 03:05:16 +00:00
}
2013-11-22 06:11:07 +00:00
// Begin a transaction
2013-05-06 08:01:17 +00:00
func ( session * Session ) Begin ( ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
if session . IsAutoCommit {
tx , err := session . Db . Begin ( )
if err != nil {
return err
}
session . IsAutoCommit = false
session . IsCommitedOrRollbacked = false
session . Tx = tx
2014-02-11 17:35:26 +00:00
session . Engine . logSQL ( "BEGIN TRANSACTION" )
2013-12-18 03:31:32 +00:00
}
return nil
2013-05-03 07:26:51 +00:00
}
2013-11-22 06:11:07 +00:00
// When using transaction, you can rollback if any error
2013-05-06 08:01:17 +00:00
func ( session * Session ) Rollback ( ) error {
2013-12-18 03:31:32 +00:00
if ! session . IsAutoCommit && ! session . IsCommitedOrRollbacked {
2014-04-11 07:37:27 +00:00
session . Engine . logSQL ( session . Engine . dialect . RollBackStr ( ) )
2013-12-18 03:31:32 +00:00
session . IsCommitedOrRollbacked = true
return session . Tx . Rollback ( )
}
return nil
2013-05-03 07:26:51 +00:00
}
2013-11-22 06:11:07 +00:00
// When using transaction, Commit will commit all operations.
2013-05-06 08:01:17 +00:00
func ( session * Session ) Commit ( ) error {
2013-12-18 03:31:32 +00:00
if ! session . IsAutoCommit && ! session . IsCommitedOrRollbacked {
2014-02-11 17:35:26 +00:00
session . Engine . logSQL ( "COMMIT" )
2013-12-18 03:31:32 +00:00
session . IsCommitedOrRollbacked = true
var err error
if err = session . Tx . Commit ( ) ; err == nil {
// handle processors after tx committed
closureCallFunc := func ( closuresPtr * [ ] func ( interface { } ) , bean interface { } ) {
if closuresPtr != nil {
for _ , closure := range * closuresPtr {
closure ( bean )
}
}
}
for bean , closuresPtr := range session . afterInsertBeans {
closureCallFunc ( closuresPtr , bean )
if processor , ok := interface { } ( bean ) . ( AfterInsertProcessor ) ; ok {
processor . AfterInsert ( )
}
}
for bean , closuresPtr := range session . afterUpdateBeans {
closureCallFunc ( closuresPtr , bean )
if processor , ok := interface { } ( bean ) . ( AfterUpdateProcessor ) ; ok {
processor . AfterUpdate ( )
}
}
for bean , closuresPtr := range session . afterDeleteBeans {
closureCallFunc ( closuresPtr , bean )
if processor , ok := interface { } ( bean ) . ( AfterDeleteProcessor ) ; ok {
processor . AfterDelete ( )
}
}
cleanUpFunc := func ( slices * map [ interface { } ] * [ ] func ( interface { } ) ) {
if len ( * slices ) > 0 {
* slices = make ( map [ interface { } ] * [ ] func ( interface { } ) , 0 )
}
}
cleanUpFunc ( & session . afterInsertBeans )
cleanUpFunc ( & session . afterUpdateBeans )
cleanUpFunc ( & session . afterDeleteBeans )
}
return err
}
return nil
2013-05-03 07:26:51 +00:00
}
2013-12-03 01:09:21 +00:00
func cleanupProcessorsClosures ( slices * [ ] func ( interface { } ) ) {
2013-12-18 03:31:32 +00:00
if len ( * slices ) > 0 {
* slices = make ( [ ] func ( interface { } ) , 0 )
}
2013-12-03 01:09:21 +00:00
}
2013-05-03 07:26:51 +00:00
func ( session * Session ) scanMapIntoStruct ( obj interface { } , objMap map [ string ] [ ] byte ) error {
2014-04-08 08:46:23 +00:00
dataStruct := rValue ( obj )
2013-12-18 03:31:32 +00:00
if dataStruct . Kind ( ) != reflect . Struct {
return errors . New ( "Expected a pointer to a struct" )
}
2014-01-07 09:33:27 +00:00
var col * core . Column
2014-04-08 08:46:23 +00:00
table := session . Engine . autoMapType ( dataStruct )
2013-12-18 03:31:32 +00:00
for key , data := range objMap {
2014-01-07 09:33:27 +00:00
if col = table . GetColumn ( key ) ; col == nil {
2014-06-25 09:09:49 +00:00
session . Engine . LogWarn ( fmt . Sprintf ( "struct %v's has not field %v. %v" ,
table . Type . Name ( ) , key , table . ColumnsSeq ( ) ) )
2013-12-18 03:31:32 +00:00
continue
}
2013-12-25 09:41:01 +00:00
2013-12-18 03:31:32 +00:00
fieldName := col . FieldName
fieldPath := strings . Split ( fieldName , "." )
var fieldValue reflect . Value
if len ( fieldPath ) > 2 {
session . Engine . LogError ( "Unsupported mutliderive" , fieldName )
continue
} else if len ( fieldPath ) == 2 {
parentField := dataStruct . FieldByName ( fieldPath [ 0 ] )
if parentField . IsValid ( ) {
fieldValue = parentField . FieldByName ( fieldPath [ 1 ] )
}
} else {
fieldValue = dataStruct . FieldByName ( fieldName )
}
if ! fieldValue . IsValid ( ) || ! fieldValue . CanSet ( ) {
session . Engine . LogWarn ( "table %v's column %v is not valid or cannot set" ,
table . Name , key )
continue
}
err := session . bytes2Value ( col , & fieldValue , data )
if err != nil {
return err
}
}
return nil
2013-05-03 07:26:51 +00:00
}
2013-05-06 08:01:17 +00:00
//Execute sql
2013-12-27 18:42:50 +00:00
func ( session * Session ) innerExec ( sqlStr string , args ... interface { } ) ( sql . Result , error ) {
2014-01-14 10:53:00 +00:00
stmt , err := session . doPrepare ( sqlStr )
2013-12-18 03:31:32 +00:00
if err != nil {
return nil , err
}
2014-01-14 10:53:00 +00:00
//defer stmt.Close()
2013-05-06 08:01:17 +00:00
2014-01-14 10:53:00 +00:00
res , err := stmt . Exec ( args ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return nil , err
}
return res , nil
2013-05-06 08:01:17 +00:00
}
2013-12-27 18:42:50 +00:00
func ( session * Session ) exec ( sqlStr string , args ... interface { } ) ( sql . Result , error ) {
2014-04-15 03:39:29 +00:00
for _ , filter := range session . Engine . dialect . Filters ( ) {
2014-01-07 09:33:27 +00:00
sqlStr = filter . Do ( sqlStr , session . Engine . dialect , session . Statement . RefTable )
2013-12-18 03:31:32 +00:00
}
2013-08-08 05:24:38 +00:00
2014-05-16 15:42:47 +00:00
session . Engine . logSQL ( sqlStr , args ... )
2013-08-08 05:24:38 +00:00
2014-07-21 06:56:26 +00:00
return session . Engine . LogSQLExecutionTime ( sqlStr , args , func ( ) ( sql . Result , error ) {
if session . IsAutoCommit {
return session . innerExec ( sqlStr , args ... )
}
return session . Tx . Exec ( sqlStr , args ... )
} )
2013-05-08 13:42:22 +00:00
}
2013-11-22 06:11:07 +00:00
// Exec raw sql
2013-12-27 18:42:50 +00:00
func ( session * Session ) Exec ( sqlStr string , args ... interface { } ) ( sql . Result , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return nil , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-09-17 09:36:34 +00:00
2013-12-27 18:42:50 +00:00
return session . exec ( sqlStr , args ... )
2013-09-17 09:36:34 +00:00
}
2013-07-27 04:24:38 +00:00
// this function create a table according a bean
2013-05-19 05:25:52 +00:00
func ( session * Session ) CreateTable ( bean interface { } ) error {
2014-08-28 16:34:09 +00:00
session . Statement . RefTable = session . Engine . TableInfo ( bean )
2013-08-08 05:24:38 +00:00
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-08-08 05:24:38 +00:00
2013-12-18 03:31:32 +00:00
return session . createOneTable ( )
2013-08-08 05:24:38 +00:00
}
2013-11-22 06:11:07 +00:00
// create indexes
2013-09-28 15:14:42 +00:00
func ( session * Session ) CreateIndexes ( bean interface { } ) error {
2014-08-28 16:34:09 +00:00
session . Statement . RefTable = session . Engine . TableInfo ( bean )
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
sqls := session . Statement . genIndexSQL ( )
2013-12-27 18:42:50 +00:00
for _ , sqlStr := range sqls {
_ , err = session . exec ( sqlStr )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
}
}
return nil
2013-09-28 15:14:42 +00:00
}
2013-11-22 06:11:07 +00:00
// create uniques
2013-09-28 15:14:42 +00:00
func ( session * Session ) CreateUniques ( bean interface { } ) error {
2014-08-28 16:34:09 +00:00
session . Statement . RefTable = session . Engine . TableInfo ( bean )
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
sqls := session . Statement . genUniqueSQL ( )
2013-12-27 18:42:50 +00:00
for _ , sqlStr := range sqls {
_ , err = session . exec ( sqlStr )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
}
}
return nil
2013-09-28 15:14:42 +00:00
}
func ( session * Session ) createOneTable ( ) error {
2013-12-27 18:42:50 +00:00
sqlStr := session . Statement . genCreateTableSQL ( )
session . Engine . LogDebug ( "create table sql: [" , sqlStr , "]" )
_ , err := session . exec ( sqlStr )
2013-12-18 03:31:32 +00:00
return err
2013-05-19 05:25:52 +00:00
}
2013-11-22 06:11:07 +00:00
// to be deleted
func ( session * Session ) createAll ( ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
for _ , table := range session . Engine . Tables {
session . Statement . RefTable = table
err := session . createOneTable ( )
if err != nil {
return err
}
}
return nil
2013-08-08 05:24:38 +00:00
}
2013-11-22 06:11:07 +00:00
// drop indexes
2013-09-28 15:14:42 +00:00
func ( session * Session ) DropIndexes ( bean interface { } ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
sqls := session . Statement . genDelIndexSQL ( )
2013-12-27 18:42:50 +00:00
for _ , sqlStr := range sqls {
_ , err = session . exec ( sqlStr )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
}
}
return nil
2013-09-28 15:14:42 +00:00
}
2013-09-26 07:19:39 +00:00
// DropTable drop a table and all indexes of the table
2013-07-27 13:47:22 +00:00
func ( session * Session ) DropTable ( bean interface { } ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
t := reflect . Indirect ( reflect . ValueOf ( bean ) ) . Type ( )
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if t . Kind ( ) == reflect . String {
session . Statement . AltTableName = bean . ( string )
} else if t . Kind ( ) == reflect . Struct {
2014-08-28 16:34:09 +00:00
session . Statement . RefTable = session . Engine . TableInfo ( bean )
2013-12-18 03:31:32 +00:00
} else {
return errors . New ( "Unsupported type" )
}
2013-12-27 18:42:50 +00:00
sqlStr := session . Statement . genDropSQL ( )
_ , err = session . exec ( sqlStr )
2013-12-18 03:31:32 +00:00
return err
2013-07-27 13:47:22 +00:00
}
2014-11-07 09:56:33 +00:00
func ( statement * Statement ) JoinColumns ( cols [ ] * core . Column ) string {
var colnames = make ( [ ] string , len ( cols ) )
for i , col := range cols {
colnames [ i ] = statement . Engine . Quote ( statement . TableName ( ) ) +
"." + statement . Engine . Quote ( col . Name )
}
return strings . Join ( colnames , ", " )
}
2013-12-27 18:42:50 +00:00
func ( statement * Statement ) convertIdSql ( sqlStr string ) string {
2013-12-18 03:31:32 +00:00
if statement . RefTable != nil {
2014-11-07 09:56:33 +00:00
cols := statement . RefTable . PKColumns ( )
if len ( cols ) == 0 {
return ""
}
colstrs := statement . JoinColumns ( cols )
sqls := splitNNoCase ( sqlStr , "from" , 2 )
if len ( sqls ) != 2 {
return ""
}
return fmt . Sprintf ( "SELECT %s FROM %v" , colstrs , sqls [ 1 ] )
2013-12-18 03:31:32 +00:00
}
return ""
2013-09-17 09:36:34 +00:00
}
2013-12-27 18:42:50 +00:00
func ( session * Session ) cacheGet ( bean interface { } , sqlStr string , args ... interface { } ) ( has bool , err error ) {
2014-11-07 14:23:23 +00:00
// if has no reftable, then don't use cache currently
2014-11-08 03:12:37 +00:00
if session . Statement . RefTable == nil ||
session . Statement . JoinStr != "" ||
session . Statement . RawSQL != "" {
2014-11-07 14:23:23 +00:00
return false , ErrCacheFailed
}
// TODO: remove this after support multi pk cache
2014-11-08 03:12:37 +00:00
/ * if len ( session . Statement . RefTable . PrimaryKeys ) != 1 {
2013-12-18 03:31:32 +00:00
return false , ErrCacheFailed
2014-11-08 03:12:37 +00:00
} * /
2014-11-07 14:23:23 +00:00
2014-04-15 03:39:29 +00:00
for _ , filter := range session . Engine . dialect . Filters ( ) {
2014-01-07 09:33:27 +00:00
sqlStr = filter . Do ( sqlStr , session . Engine . dialect , session . Statement . RefTable )
2013-12-18 03:31:32 +00:00
}
2013-12-27 18:42:50 +00:00
newsql := session . Statement . convertIdSql ( sqlStr )
2013-12-18 03:31:32 +00:00
if newsql == "" {
return false , ErrCacheFailed
}
2014-04-11 07:37:27 +00:00
cacher := session . Engine . getCacher2 ( session . Statement . RefTable )
2013-12-18 03:31:32 +00:00
tableName := session . Statement . TableName ( )
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheGet] find sql:" , newsql , args )
2014-01-25 02:07:11 +00:00
ids , err := core . GetCacheSql ( cacher , tableName , newsql , args )
2014-11-07 14:23:23 +00:00
table := session . Statement . RefTable
2013-12-18 03:31:32 +00:00
if err != nil {
2014-11-07 14:23:23 +00:00
var res = make ( [ ] string , len ( table . PrimaryKeys ) )
2014-11-07 09:56:33 +00:00
rows , err := session . Db . Query ( newsql , args ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return false , err
}
2014-11-07 09:56:33 +00:00
defer rows . Close ( )
if rows . Next ( ) {
err = rows . ScanSlice ( & res )
if err != nil {
return false , err
2013-12-18 03:31:32 +00:00
}
2014-11-07 09:56:33 +00:00
} else {
return false , ErrCacheFailed
2013-12-18 03:31:32 +00:00
}
2014-11-07 09:56:33 +00:00
2014-11-07 14:23:23 +00:00
var pk core . PK = make ( [ ] interface { } , len ( table . PrimaryKeys ) )
for i , col := range table . PKColumns ( ) {
if col . SQLType . IsText ( ) {
pk [ i ] = res [ i ]
} else if col . SQLType . IsNumeric ( ) {
n , err := strconv . ParseInt ( res [ i ] , 10 , 64 )
if err != nil {
return false , err
}
pk [ i ] = n
} else {
return false , errors . New ( "unsupported" )
}
}
ids = [ ] core . PK { pk }
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheGet] cache ids:" , newsql , ids )
2014-01-25 02:07:11 +00:00
err = core . PutCacheSql ( cacher , ids , tableName , newsql , args )
2013-12-18 03:31:32 +00:00
if err != nil {
return false , err
}
} else {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheGet] cache hit sql:" , newsql )
2013-12-18 03:31:32 +00:00
}
if len ( ids ) > 0 {
structValue := reflect . Indirect ( reflect . ValueOf ( bean ) )
id := ids [ 0 ]
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheGet] get bean:" , tableName , id )
2014-01-25 02:07:11 +00:00
sid , err := id . ToString ( )
if err != nil {
return false , err
}
cacheBean := cacher . GetBean ( tableName , sid )
2013-12-18 03:31:32 +00:00
if cacheBean == nil {
newSession := session . Engine . NewSession ( )
defer newSession . Close ( )
cacheBean = reflect . New ( structValue . Type ( ) ) . Interface ( )
2013-12-19 14:32:00 +00:00
newSession . Id ( id ) . NoCache ( )
2013-12-18 03:31:32 +00:00
if session . Statement . AltTableName != "" {
2013-12-19 14:32:00 +00:00
newSession . Table ( session . Statement . AltTableName )
}
if ! session . Statement . UseCascade {
newSession . NoCascade ( )
2013-12-18 03:31:32 +00:00
}
2013-12-19 14:32:00 +00:00
has , err = newSession . Get ( cacheBean )
2013-12-18 03:31:32 +00:00
if err != nil || ! has {
return has , err
}
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheGet] cache bean:" , tableName , id , cacheBean )
2014-01-25 02:07:11 +00:00
cacher . PutBean ( tableName , sid , cacheBean )
2013-12-18 03:31:32 +00:00
} else {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheGet] cache hit bean:" , tableName , id , cacheBean )
2013-12-18 03:31:32 +00:00
has = true
}
structValue . Set ( reflect . Indirect ( reflect . ValueOf ( cacheBean ) ) )
return has , nil
}
return false , nil
2013-09-17 09:36:34 +00:00
}
2014-11-07 14:23:23 +00:00
func ( session * Session ) cacheFind ( t reflect . Type , sqlStr string , rowsSlicePtr interface { } , args ... interface { } ) ( err error ) {
2013-12-18 03:31:32 +00:00
if session . Statement . RefTable == nil ||
2013-12-27 18:42:50 +00:00
indexNoCase ( sqlStr , "having" ) != - 1 ||
indexNoCase ( sqlStr , "group by" ) != - 1 {
2013-12-18 03:31:32 +00:00
return ErrCacheFailed
}
2014-11-08 03:12:37 +00:00
// TODO: remove this after multi pk supported
/ * if len ( session . Statement . RefTable . PrimaryKeys ) != 1 {
return ErrCacheFailed
} * /
2014-04-15 03:39:29 +00:00
for _ , filter := range session . Engine . dialect . Filters ( ) {
2014-01-07 09:33:27 +00:00
sqlStr = filter . Do ( sqlStr , session . Engine . dialect , session . Statement . RefTable )
2013-12-18 03:31:32 +00:00
}
2013-12-27 18:42:50 +00:00
newsql := session . Statement . convertIdSql ( sqlStr )
2013-12-18 03:31:32 +00:00
if newsql == "" {
return ErrCacheFailed
}
table := session . Statement . RefTable
2014-04-11 07:37:27 +00:00
cacher := session . Engine . getCacher2 ( table )
2014-01-25 02:07:11 +00:00
ids , err := core . GetCacheSql ( cacher , session . Statement . TableName ( ) , newsql , args )
2013-12-18 03:31:32 +00:00
if err != nil {
2014-11-07 09:56:33 +00:00
rows , err := session . Db . Query ( newsql , args ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
}
2014-11-07 09:56:33 +00:00
defer rows . Close ( )
2013-12-18 03:31:32 +00:00
2014-11-07 09:56:33 +00:00
var i int
2014-01-25 02:07:11 +00:00
ids = make ( [ ] core . PK , 0 )
2014-11-07 09:56:33 +00:00
for rows . Next ( ) {
i ++
if i > 500 {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheFind] ids length > 500, no cache" )
2014-11-07 09:56:33 +00:00
return ErrCacheFailed
2013-12-18 03:31:32 +00:00
}
2014-11-07 14:23:23 +00:00
var res = make ( [ ] string , len ( table . PrimaryKeys ) )
2014-11-07 09:56:33 +00:00
err = rows . ScanSlice ( & res )
if err != nil {
return err
}
2014-11-07 14:23:23 +00:00
var pk core . PK = make ( [ ] interface { } , len ( table . PrimaryKeys ) )
for i , col := range table . PKColumns ( ) {
if col . SQLType . IsNumeric ( ) {
n , err := strconv . ParseInt ( res [ i ] , 10 , 64 )
if err != nil {
return err
}
pk [ i ] = n
} else if col . SQLType . IsText ( ) {
pk [ i ] = res [ i ]
} else {
return errors . New ( "not supported" )
}
}
ids = append ( ids , pk )
2013-12-18 03:31:32 +00:00
}
2014-11-07 09:56:33 +00:00
tableName := session . Statement . TableName ( )
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheFind] cache sql:" , ids , tableName , newsql , args )
2014-01-25 02:07:11 +00:00
err = core . PutCacheSql ( cacher , ids , tableName , newsql , args )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
}
} else {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheFind] cache hit sql:" , newsql , args )
2013-12-18 03:31:32 +00:00
}
sliceValue := reflect . Indirect ( reflect . ValueOf ( rowsSlicePtr ) )
2014-01-25 02:07:11 +00:00
ididxes := make ( map [ string ] int )
var ides [ ] core . PK = make ( [ ] core . PK , 0 )
2013-12-18 03:31:32 +00:00
var temps [ ] interface { } = make ( [ ] interface { } , len ( ids ) )
tableName := session . Statement . TableName ( )
for idx , id := range ids {
2014-01-25 02:07:11 +00:00
sid , err := id . ToString ( )
if err != nil {
return err
}
bean := cacher . GetBean ( tableName , sid )
2013-12-18 03:31:32 +00:00
if bean == nil {
ides = append ( ides , id )
2014-01-25 02:07:11 +00:00
ididxes [ sid ] = idx
2013-12-18 03:31:32 +00:00
} else {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheFind] cache hit bean:" , tableName , id , bean )
2013-12-18 03:31:32 +00:00
2014-01-25 02:07:11 +00:00
pk := session . Engine . IdOf ( bean )
xid , err := pk . ToString ( )
if err != nil {
return err
2013-12-24 10:18:48 +00:00
}
2014-01-25 02:07:11 +00:00
if sid != xid {
2014-11-08 03:12:37 +00:00
session . Engine . LogError ( "[cacheFind] error cache" , xid , sid , bean )
2013-12-18 03:31:32 +00:00
return ErrCacheFailed
}
temps [ idx ] = bean
}
}
if len ( ides ) > 0 {
newSession := session . Engine . NewSession ( )
defer newSession . Close ( )
slices := reflect . New ( reflect . SliceOf ( t ) )
beans := slices . Interface ( )
2014-11-08 03:12:37 +00:00
if len ( table . PrimaryKeys ) == 1 {
ff := make ( [ ] interface { } , 0 )
for _ , ie := range ides {
ff = append ( ff , ie [ 0 ] )
}
newSession . In ( table . PrimaryKeys [ 0 ] , ff ... )
} else {
var kn = make ( [ ] string , 0 )
for _ , name := range table . PrimaryKeys {
kn = append ( kn , name + " = ?" )
}
condi := "(" + strings . Join ( kn , " AND " ) + ")"
for _ , ie := range ides {
newSession . Or ( condi , ie ... )
2014-01-25 02:07:11 +00:00
}
}
2014-11-08 03:12:37 +00:00
2014-01-25 02:07:11 +00:00
err = newSession . NoCache ( ) . Find ( beans )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
}
vs := reflect . Indirect ( reflect . ValueOf ( beans ) )
for i := 0 ; i < vs . Len ( ) ; i ++ {
rv := vs . Index ( i )
if rv . Kind ( ) != reflect . Ptr {
rv = rv . Addr ( )
}
bean := rv . Interface ( )
2014-01-25 02:07:11 +00:00
id := session . Engine . IdOf ( bean )
sid , err := id . ToString ( )
if err != nil {
return err
}
2014-11-08 03:12:37 +00:00
2014-01-25 02:07:11 +00:00
temps [ ididxes [ sid ] ] = bean
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheFind] cache bean:" , tableName , id , bean )
2014-01-25 02:07:11 +00:00
cacher . PutBean ( tableName , sid , bean )
2013-12-18 03:31:32 +00:00
}
}
for j := 0 ; j < len ( temps ) ; j ++ {
bean := temps [ j ]
if bean == nil {
2014-11-08 03:12:37 +00:00
session . Engine . LogWarn ( "[cacheFind] cache no hit:" , tableName , ides [ j ] )
2014-08-18 08:02:24 +00:00
// return errors.New("cache error") // !nashtsai! no need to return error, but continue instead
continue
2013-12-18 03:31:32 +00:00
}
if sliceValue . Kind ( ) == reflect . Slice {
if t . Kind ( ) == reflect . Ptr {
sliceValue . Set ( reflect . Append ( sliceValue , reflect . ValueOf ( bean ) ) )
} else {
sliceValue . Set ( reflect . Append ( sliceValue , reflect . Indirect ( reflect . ValueOf ( bean ) ) ) )
}
} else if sliceValue . Kind ( ) == reflect . Map {
2014-01-25 02:07:11 +00:00
var key core . PK
2013-12-24 10:18:48 +00:00
if table . PrimaryKeys [ 0 ] != "" {
2013-12-18 03:31:32 +00:00
key = ids [ j ]
}
2014-01-25 02:07:11 +00:00
if len ( key ) == 1 {
ikey , err := strconv . ParseInt ( fmt . Sprintf ( "%v" , key [ 0 ] ) , 10 , 64 )
if err != nil {
return err
}
if t . Kind ( ) == reflect . Ptr {
sliceValue . SetMapIndex ( reflect . ValueOf ( ikey ) , reflect . ValueOf ( bean ) )
} else {
sliceValue . SetMapIndex ( reflect . ValueOf ( ikey ) , reflect . Indirect ( reflect . ValueOf ( bean ) ) )
}
2014-11-07 14:23:23 +00:00
} else {
return errors . New ( "table have multiple primary keys" )
2013-12-18 03:31:32 +00:00
}
}
/ * } else {
session . Engine . LogDebug ( "[xorm:cacheFind] cache delete:" , tableName , ides [ j ] )
cacher . DelBean ( tableName , ids [ j ] )
session . Engine . LogDebug ( "[xorm:cacheFind] cache clear:" , tableName )
cacher . ClearIds ( tableName )
} * /
}
return nil
2013-09-17 09:36:34 +00:00
}
2013-11-22 06:11:07 +00:00
// IterFunc only use by Iterate
2013-10-17 04:50:46 +00:00
type IterFunc func ( idx int , bean interface { } ) error
2013-12-26 06:53:20 +00:00
// Return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields
2013-12-25 07:39:56 +00:00
// are conditions.
2013-12-26 06:50:44 +00:00
func ( session * Session ) Rows ( bean interface { } ) ( * Rows , error ) {
return newRows ( session , bean )
2013-12-25 07:39:56 +00:00
}
2013-11-22 06:11:07 +00:00
// Iterate record by record handle records from table, condiBeans's non-empty fields
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct
2013-10-17 04:50:46 +00:00
func ( session * Session ) Iterate ( bean interface { } , fun IterFunc ) error {
2013-12-26 06:50:44 +00:00
rows , err := session . Rows ( bean )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
2014-07-09 13:48:48 +00:00
}
defer rows . Close ( )
//b := reflect.New(iterator.beanType).Interface()
i := 0
for rows . Next ( ) {
b := reflect . New ( rows . beanType ) . Interface ( )
err = rows . Scan ( b )
if err != nil {
return err
2013-12-18 03:31:32 +00:00
}
2014-07-09 13:48:48 +00:00
err = fun ( i , b )
if err != nil {
return err
}
i ++
2013-12-18 03:31:32 +00:00
}
2014-07-09 13:48:48 +00:00
return err
2013-10-17 04:50:46 +00:00
}
2014-02-11 06:59:04 +00:00
func ( session * Session ) doPrepare ( sqlStr string ) ( stmt * core . Stmt , err error ) {
2014-01-14 10:53:00 +00:00
crc := crc32 . ChecksumIEEE ( [ ] byte ( sqlStr ) )
// TODO try hash(sqlStr+len(sqlStr))
var has bool
stmt , has = session . stmtCache [ crc ]
if ! has {
stmt , err = session . Db . Prepare ( sqlStr )
if err != nil {
return nil , err
}
session . stmtCache [ crc ] = stmt
}
return
}
2013-11-22 06:11:07 +00:00
// get retrieve one record from database, bean's non-empty fields
// will be as conditions
2013-06-16 03:05:16 +00:00
func ( session * Session ) Get ( bean interface { } ) ( bool , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return false , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
session . Statement . Limit ( 1 )
2013-12-27 18:42:50 +00:00
var sqlStr string
2013-12-18 03:31:32 +00:00
var args [ ] interface { }
2014-04-11 07:37:27 +00:00
2014-05-23 06:18:45 +00:00
if session . Statement . RefTable == nil {
2014-08-28 16:34:09 +00:00
session . Statement . RefTable = session . Engine . TableInfo ( bean )
2014-05-23 06:18:45 +00:00
}
2013-12-18 03:31:32 +00:00
if session . Statement . RawSQL == "" {
2013-12-27 18:42:50 +00:00
sqlStr , args = session . Statement . genGetSql ( bean )
2013-12-18 03:31:32 +00:00
} else {
2013-12-27 18:42:50 +00:00
sqlStr = session . Statement . RawSQL
2013-12-18 03:31:32 +00:00
args = session . Statement . RawParams
}
2014-05-23 06:18:45 +00:00
if session . Statement . JoinStr == "" {
2014-11-18 08:17:44 +00:00
if cacher := session . Engine . getCacher2 ( session . Statement . RefTable ) ; cacher != nil &&
session . Statement . UseCache &&
! session . Statement . unscoped {
2014-05-23 06:18:45 +00:00
has , err := session . cacheGet ( bean , sqlStr , args ... )
if err != ErrCacheFailed {
return has , err
}
2013-12-18 03:31:32 +00:00
}
}
2014-02-11 06:59:04 +00:00
var rawRows * core . Rows
2013-12-29 18:32:26 +00:00
session . queryPreprocess ( & sqlStr , args ... )
2013-12-27 18:42:50 +00:00
if session . IsAutoCommit {
2014-01-14 10:53:00 +00:00
stmt , err := session . doPrepare ( sqlStr )
2013-12-27 18:42:50 +00:00
if err != nil {
return false , err
}
2014-01-14 10:53:00 +00:00
// defer stmt.Close() // !nashtsai! don't close due to stmt is cached and bounded to this session
2013-12-27 18:42:50 +00:00
rawRows , err = stmt . Query ( args ... )
} else {
rawRows , err = session . Tx . Query ( sqlStr , args ... )
}
2013-12-18 03:31:32 +00:00
if err != nil {
return false , err
}
2013-12-27 18:42:50 +00:00
defer rawRows . Close ( )
2013-12-18 03:31:32 +00:00
2013-12-27 18:42:50 +00:00
if rawRows . Next ( ) {
if fields , err := rawRows . Columns ( ) ; err == nil {
err = session . row2Bean ( rawRows , fields , len ( fields ) , bean )
}
2013-12-18 03:31:32 +00:00
return true , err
}
2014-08-28 14:48:01 +00:00
return false , nil
2013-05-03 07:26:51 +00:00
}
2013-11-22 06:11:07 +00:00
// Count counts the records. bean's non-empty fields
// are conditions.
2013-05-03 07:26:51 +00:00
func ( session * Session ) Count ( bean interface { } ) ( int64 , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return 0 , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-12-27 18:42:50 +00:00
var sqlStr string
2013-12-18 03:31:32 +00:00
var args [ ] interface { }
if session . Statement . RawSQL == "" {
2013-12-27 18:42:50 +00:00
sqlStr , args = session . Statement . genCountSql ( bean )
2013-12-18 03:31:32 +00:00
} else {
2013-12-27 18:42:50 +00:00
sqlStr = session . Statement . RawSQL
2013-12-18 03:31:32 +00:00
args = session . Statement . RawParams
}
2013-12-27 18:42:50 +00:00
resultsSlice , err := session . query ( sqlStr , args ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return 0 , err
}
var total int64 = 0
if len ( resultsSlice ) > 0 {
results := resultsSlice [ 0 ]
for _ , value := range results {
total , err = strconv . ParseInt ( string ( value ) , 10 , 64 )
break
}
}
return int64 ( total ) , err
2013-05-03 07:26:51 +00:00
}
2013-11-22 06:11:07 +00:00
// Find retrieve records from table, condiBeans's non-empty fields
// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
// map[int64]*Struct
2013-05-08 13:42:22 +00:00
func ( session * Session ) Find ( rowsSlicePtr interface { } , condiBean ... interface { } ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
sliceValue := reflect . Indirect ( reflect . ValueOf ( rowsSlicePtr ) )
if sliceValue . Kind ( ) != reflect . Slice && sliceValue . Kind ( ) != reflect . Map {
return errors . New ( "needs a pointer to a slice or a map" )
}
sliceElementType := sliceValue . Type ( ) . Elem ( )
2014-01-07 09:33:27 +00:00
var table * core . Table
2013-12-18 03:31:32 +00:00
if session . Statement . RefTable == nil {
if sliceElementType . Kind ( ) == reflect . Ptr {
if sliceElementType . Elem ( ) . Kind ( ) == reflect . Struct {
2014-11-07 14:23:23 +00:00
pv := reflect . New ( sliceElementType . Elem ( ) )
2014-04-08 08:46:23 +00:00
table = session . Engine . autoMapType ( pv . Elem ( ) )
2013-12-18 03:31:32 +00:00
} else {
return errors . New ( "slice type" )
}
} else if sliceElementType . Kind ( ) == reflect . Struct {
2014-11-07 14:23:23 +00:00
pv := reflect . New ( sliceElementType )
2014-04-08 08:46:23 +00:00
table = session . Engine . autoMapType ( pv . Elem ( ) )
2013-12-18 03:31:32 +00:00
} else {
return errors . New ( "slice type" )
}
session . Statement . RefTable = table
} else {
table = session . Statement . RefTable
}
if len ( condiBean ) > 0 {
colNames , args := buildConditions ( session . Engine , table , condiBean [ 0 ] , true , true ,
2014-03-24 12:41:07 +00:00
false , true , session . Statement . allUseBool , session . Statement . useAllCols ,
2014-11-05 07:04:53 +00:00
session . Statement . unscoped , session . Statement . mustColumnMap )
2013-12-18 03:31:32 +00:00
session . Statement . ConditionStr = strings . Join ( colNames , " AND " )
session . Statement . BeanArgs = args
2014-11-18 08:17:44 +00:00
} else {
// !oinume! Add "<col> IS NULL" to WHERE whatever condiBean is given.
// See https://github.com/go-xorm/xorm/issues/179
for _ , col := range table . Columns ( ) {
if col . IsDeleted && ! session . Statement . unscoped { // tag "deleted" is enabled
2014-11-23 09:59:10 +00:00
session . Statement . ConditionStr = fmt . Sprintf ( "(%v IS NULL or %v = '0001-01-01 00:00:00') " , session . Engine . Quote ( col . Name ) , session . Engine . Quote ( col . Name ) )
2014-11-18 08:17:44 +00:00
}
}
2013-12-18 03:31:32 +00:00
}
2013-12-27 18:42:50 +00:00
var sqlStr string
2013-12-18 03:31:32 +00:00
var args [ ] interface { }
if session . Statement . RawSQL == "" {
var columnStr string = session . Statement . ColumnStr
2014-05-23 06:18:45 +00:00
if session . Statement . JoinStr == "" {
if columnStr == "" {
columnStr = session . Statement . genColumnStr ( )
}
} else {
if columnStr == "" {
columnStr = "*"
}
2013-12-18 03:31:32 +00:00
}
session . Statement . attachInSql ( )
2013-12-27 18:42:50 +00:00
sqlStr = session . Statement . genSelectSql ( columnStr )
2013-12-18 03:31:32 +00:00
args = append ( session . Statement . Params , session . Statement . BeanArgs ... )
2014-08-05 07:45:54 +00:00
// for mssql and use limit
qs := strings . Count ( sqlStr , "?" )
if len ( args ) * 2 == qs {
args = append ( args , args ... )
}
2013-12-18 03:31:32 +00:00
} else {
2013-12-27 18:42:50 +00:00
sqlStr = session . Statement . RawSQL
2013-12-18 03:31:32 +00:00
args = session . Statement . RawParams
}
2014-05-23 06:18:45 +00:00
if session . Statement . JoinStr == "" {
if cacher := session . Engine . getCacher2 ( table ) ; cacher != nil &&
session . Statement . UseCache &&
2014-11-18 08:17:44 +00:00
! session . Statement . IsDistinct &&
! session . Statement . unscoped {
2014-11-07 14:23:23 +00:00
err = session . cacheFind ( sliceElementType , sqlStr , rowsSlicePtr , args ... )
2014-05-23 06:18:45 +00:00
if err != ErrCacheFailed {
return err
}
err = nil // !nashtsai! reset err to nil for ErrCacheFailed
session . Engine . LogWarn ( "Cache Find Failed" )
2013-12-18 03:31:32 +00:00
}
}
2013-12-27 18:42:50 +00:00
if sliceValue . Kind ( ) != reflect . Map {
2014-02-11 06:59:04 +00:00
var rawRows * core . Rows
var stmt * core . Stmt
2013-12-18 03:31:32 +00:00
2013-12-29 18:32:26 +00:00
session . queryPreprocess ( & sqlStr , args ... )
2013-12-27 18:42:50 +00:00
if session . IsAutoCommit {
2014-01-14 10:53:00 +00:00
stmt , err = session . doPrepare ( sqlStr )
2013-12-27 18:42:50 +00:00
if err != nil {
return err
}
rawRows , err = stmt . Query ( args ... )
2013-12-18 03:31:32 +00:00
} else {
2013-12-27 18:42:50 +00:00
rawRows , err = session . Tx . Query ( sqlStr , args ... )
2013-12-18 03:31:32 +00:00
}
if err != nil {
return err
}
2013-12-27 18:42:50 +00:00
defer rawRows . Close ( )
fields , err := rawRows . Columns ( )
if err != nil {
return err
}
fieldsCount := len ( fields )
2014-01-08 10:37:22 +00:00
var newElemFunc func ( ) reflect . Value
if sliceElementType . Kind ( ) == reflect . Ptr {
newElemFunc = func ( ) reflect . Value {
return reflect . New ( sliceElementType . Elem ( ) )
2013-12-27 18:42:50 +00:00
}
2014-01-08 10:37:22 +00:00
} else {
newElemFunc = func ( ) reflect . Value {
return reflect . New ( sliceElementType )
2013-12-27 18:42:50 +00:00
}
2014-01-08 10:37:22 +00:00
}
var sliceValueSetFunc func ( * reflect . Value )
if sliceValue . Kind ( ) == reflect . Slice {
if sliceElementType . Kind ( ) == reflect . Ptr {
sliceValueSetFunc = func ( newValue * reflect . Value ) {
2013-12-27 18:42:50 +00:00
sliceValue . Set ( reflect . Append ( sliceValue , reflect . ValueOf ( newValue . Interface ( ) ) ) )
2014-01-08 10:37:22 +00:00
}
} else {
sliceValueSetFunc = func ( newValue * reflect . Value ) {
2013-12-27 18:42:50 +00:00
sliceValue . Set ( reflect . Append ( sliceValue , reflect . Indirect ( reflect . ValueOf ( newValue . Interface ( ) ) ) ) )
}
}
}
2014-01-08 10:37:22 +00:00
2014-08-21 16:47:19 +00:00
var newValue reflect . Value = newElemFunc ( )
dataStruct := rValue ( newValue . Interface ( ) )
if dataStruct . Kind ( ) != reflect . Struct {
return errors . New ( "Expected a pointer to a struct" )
2014-01-08 10:37:22 +00:00
}
2014-08-21 16:47:19 +00:00
table := session . Engine . autoMapType ( dataStruct )
return session . rows2Beans ( rawRows , fields , fieldsCount , table , newElemFunc , sliceValueSetFunc )
2013-12-27 18:42:50 +00:00
} else {
resultsSlice , err := session . query ( sqlStr , args ... )
if err != nil {
return err
}
for i , results := range resultsSlice {
var newValue reflect . Value
if sliceElementType . Kind ( ) == reflect . Ptr {
newValue = reflect . New ( sliceElementType . Elem ( ) )
} else {
newValue = reflect . New ( sliceElementType )
}
err := session . scanMapIntoStruct ( newValue . Interface ( ) , results )
if err != nil {
return err
2013-12-18 03:31:32 +00:00
}
var key int64
2013-12-24 10:18:48 +00:00
// if there is only one pk, we can put the id as map key.
// TODO: should know if the column is ints
if len ( table . PrimaryKeys ) == 1 {
x , err := strconv . ParseInt ( string ( results [ table . PrimaryKeys [ 0 ] ] ) , 10 , 64 )
2013-12-18 03:31:32 +00:00
if err != nil {
2013-12-24 10:18:48 +00:00
return errors . New ( "pk " + table . PrimaryKeys [ 0 ] + " as int64: " + err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
key = x
} else {
key = int64 ( i )
}
if sliceElementType . Kind ( ) == reflect . Ptr {
sliceValue . SetMapIndex ( reflect . ValueOf ( key ) , reflect . ValueOf ( newValue . Interface ( ) ) )
} else {
sliceValue . SetMapIndex ( reflect . ValueOf ( key ) , reflect . Indirect ( reflect . ValueOf ( newValue . Interface ( ) ) ) )
}
}
}
return nil
2013-05-03 07:26:51 +00:00
}
2014-01-14 10:53:00 +00:00
// func (session *Session) queryRows(rawStmt **sql.Stmt, rawRows **sql.Rows, sqlStr string, args ...interface{}) error {
// var err error
// if session.IsAutoCommit {
// *rawStmt, err = session.doPrepare(sqlStr)
// if err != nil {
// return err
// }
// *rawRows, err = (*rawStmt).Query(args...)
// } else {
// *rawRows, err = session.Tx.Query(sqlStr, args...)
// }
// return err
// }
2013-12-27 18:42:50 +00:00
2013-11-22 06:11:07 +00:00
// Test if database is ok
2013-06-16 03:05:16 +00:00
func ( session * Session ) Ping ( ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-09-02 01:54:37 +00:00
2013-12-18 03:31:32 +00:00
return session . Db . Ping ( )
2013-06-16 03:05:16 +00:00
}
2014-04-23 06:01:04 +00:00
func ( session * Session ) isColumnExist ( tableName string , col * core . Column ) ( bool , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return false , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2014-04-23 06:01:04 +00:00
return session . Engine . dialect . IsColumnExist ( tableName , col )
//sqlStr, args := session.Engine.dialect.ColumnCheckSql(tableName, colName)
//results, err := session.query(sqlStr, args...)
//return len(results) > 0, err
2013-09-28 15:14:42 +00:00
}
func ( session * Session ) isTableExist ( tableName string ) ( bool , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return false , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-12-27 18:42:50 +00:00
sqlStr , args := session . Engine . dialect . TableCheckSql ( tableName )
results , err := session . query ( sqlStr , args ... )
2013-12-18 03:31:32 +00:00
return len ( results ) > 0 , err
2013-09-28 15:14:42 +00:00
}
func ( session * Session ) isIndexExist ( tableName , idxName string , unique bool ) ( bool , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return false , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
var idx string
if unique {
idx = uniqueName ( tableName , idxName )
} else {
idx = indexName ( tableName , idxName )
}
2013-12-27 18:42:50 +00:00
sqlStr , args := session . Engine . dialect . IndexCheckSql ( tableName , idx )
results , err := session . query ( sqlStr , args ... )
2013-12-18 03:31:32 +00:00
return len ( results ) > 0 , err
2013-09-28 15:14:42 +00:00
}
2013-10-28 03:16:22 +00:00
// find if index is exist according cols
func ( session * Session ) isIndexExist2 ( tableName string , cols [ ] string , unique bool ) ( bool , error ) {
2013-12-18 03:31:32 +00:00
indexes , err := session . Engine . dialect . GetIndexes ( tableName )
if err != nil {
return false , err
}
for _ , index := range indexes {
if sliceEq ( index . Cols , cols ) {
if unique {
2014-01-07 09:33:27 +00:00
return index . Type == core . UniqueType , nil
2013-12-18 03:31:32 +00:00
} else {
2014-01-07 09:33:27 +00:00
return index . Type == core . IndexType , nil
2013-12-18 03:31:32 +00:00
}
}
}
return false , nil
2013-10-28 03:16:22 +00:00
}
2013-09-28 15:14:42 +00:00
func ( session * Session ) addColumn ( colName string ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-12-31 03:44:11 +00:00
2014-01-07 09:33:27 +00:00
col := session . Statement . RefTable . GetColumn ( colName )
2013-12-18 03:31:32 +00:00
sql , args := session . Statement . genAddColumnStr ( col )
_ , err = session . exec ( sql , args ... )
return err
2013-09-28 15:14:42 +00:00
}
func ( session * Session ) addIndex ( tableName , idxName string ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2014-04-17 02:13:16 +00:00
index := session . Statement . RefTable . Indexes [ idxName ]
sqlStr := session . Engine . dialect . CreateIndexSql ( tableName , index )
//genAddIndexStr(indexName(tableName, idxName), cols)
_ , err = session . exec ( sqlStr )
2013-12-18 03:31:32 +00:00
return err
2013-09-28 15:14:42 +00:00
}
func ( session * Session ) addUnique ( tableName , uqeName string ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2014-04-17 02:13:16 +00:00
index := session . Statement . RefTable . Indexes [ uqeName ]
sqlStr := session . Engine . dialect . CreateIndexSql ( tableName , index )
_ , err = session . exec ( sqlStr )
2013-12-18 03:31:32 +00:00
return err
2013-09-28 15:14:42 +00:00
}
2013-11-22 06:11:07 +00:00
// To be deleted
func ( session * Session ) dropAll ( ) error {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
for _ , table := range session . Engine . Tables {
session . Statement . Init ( )
session . Statement . RefTable = table
2013-12-27 18:42:50 +00:00
sqlStr := session . Statement . genDropSQL ( )
_ , err := session . exec ( sqlStr )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
}
}
return nil
2013-06-16 03:05:16 +00:00
}
2014-08-30 14:17:59 +00:00
func row2mapStr ( rows * core . Rows , fields [ ] string ) ( resultsMap map [ string ] string , err error ) {
result := make ( map [ string ] string )
scanResultContainers := make ( [ ] interface { } , len ( fields ) )
for i := 0 ; i < len ( fields ) ; i ++ {
var scanResultContainer interface { }
scanResultContainers [ i ] = & scanResultContainer
}
if err := rows . Scan ( scanResultContainers ... ) ; err != nil {
return nil , err
}
for ii , key := range fields {
rawValue := reflect . Indirect ( reflect . ValueOf ( scanResultContainers [ ii ] ) )
//if row is null then ignore
if rawValue . Interface ( ) == nil {
//fmt.Println("ignore ...", key, rawValue)
continue
}
if data , err := value2String ( & rawValue ) ; err == nil {
result [ key ] = data
} else {
return nil , err // !nashtsai! REVIEW, should return err or just error log?
}
}
return result , nil
}
2014-02-11 06:59:04 +00:00
func row2map ( rows * core . Rows , fields [ ] string ) ( resultsMap map [ string ] [ ] byte , err error ) {
2013-12-18 03:31:32 +00:00
result := make ( map [ string ] [ ] byte )
2014-02-11 06:16:14 +00:00
scanResultContainers := make ( [ ] interface { } , len ( fields ) )
2013-12-18 03:31:32 +00:00
for i := 0 ; i < len ( fields ) ; i ++ {
var scanResultContainer interface { }
2014-02-11 06:16:14 +00:00
scanResultContainers [ i ] = & scanResultContainer
2013-12-18 03:31:32 +00:00
}
if err := rows . Scan ( scanResultContainers ... ) ; err != nil {
return nil , err
}
2013-12-19 18:25:27 +00:00
2013-12-18 03:31:32 +00:00
for ii , key := range fields {
rawValue := reflect . Indirect ( reflect . ValueOf ( scanResultContainers [ ii ] ) )
//if row is null then ignore
if rawValue . Interface ( ) == nil {
//fmt.Println("ignore ...", key, rawValue)
continue
}
2013-12-26 18:14:30 +00:00
if data , err := value2Bytes ( & rawValue ) ; err == nil {
result [ key ] = data
} else {
return nil , err // !nashtsai! REVIEW, should return err or just error log?
2013-12-18 03:31:32 +00:00
}
}
return result , nil
2013-10-17 04:50:46 +00:00
}
2014-05-23 06:18:45 +00:00
func ( session * Session ) getField ( dataStruct * reflect . Value , key string , table * core . Table , idx int ) * reflect . Value {
2014-01-07 09:33:27 +00:00
var col * core . Column
2014-05-23 06:18:45 +00:00
if col = table . GetColumnIdx ( key , idx ) ; col == nil {
2014-01-07 09:33:27 +00:00
session . Engine . LogWarn ( fmt . Sprintf ( "table %v's has not column %v. %v" , table . Name , key , table . Columns ( ) ) )
2013-12-26 18:14:30 +00:00
return nil
2013-12-18 03:31:32 +00:00
}
2014-01-07 09:33:27 +00:00
2014-01-09 06:44:41 +00:00
fieldValue , err := col . ValueOfV ( dataStruct )
if err != nil {
session . Engine . LogError ( err )
2013-12-26 18:14:30 +00:00
return nil
2013-12-18 03:31:32 +00:00
}
2014-01-09 06:44:41 +00:00
2013-12-26 18:14:30 +00:00
if ! fieldValue . IsValid ( ) || ! fieldValue . CanSet ( ) {
session . Engine . LogWarn ( "table %v's column %v is not valid or cannot set" ,
table . Name , key )
return nil
}
2014-01-09 06:44:41 +00:00
return fieldValue
2013-12-26 18:14:30 +00:00
}
2014-05-04 05:53:38 +00:00
type Cell * interface { }
2014-08-21 16:47:19 +00:00
func ( session * Session ) rows2Beans ( rows * core . Rows , fields [ ] string , fieldsCount int ,
table * core . Table , newElemFunc func ( ) reflect . Value ,
sliceValueSetFunc func ( * reflect . Value ) ) error {
for rows . Next ( ) {
var newValue reflect . Value = newElemFunc ( )
bean := newValue . Interface ( )
dataStruct := rValue ( bean )
err := session . _row2Bean ( rows , fields , fieldsCount , bean , & dataStruct , table )
if err != nil {
return err
}
sliceValueSetFunc ( & newValue )
}
return nil
}
2014-02-11 06:59:04 +00:00
func ( session * Session ) row2Bean ( rows * core . Rows , fields [ ] string , fieldsCount int , bean interface { } ) error {
2014-04-08 08:46:23 +00:00
dataStruct := rValue ( bean )
2013-12-26 18:14:30 +00:00
if dataStruct . Kind ( ) != reflect . Struct {
return errors . New ( "Expected a pointer to a struct" )
}
2014-04-08 08:46:23 +00:00
table := session . Engine . autoMapType ( dataStruct )
2014-08-21 16:47:19 +00:00
return session . _row2Bean ( rows , fields , fieldsCount , bean , & dataStruct , table )
}
func ( session * Session ) _row2Bean ( rows * core . Rows , fields [ ] string , fieldsCount int , bean interface { } , dataStruct * reflect . Value , table * core . Table ) error {
2013-12-26 18:14:30 +00:00
2014-08-21 16:47:19 +00:00
scanResults := make ( [ ] interface { } , fieldsCount )
2014-02-11 06:16:14 +00:00
for i := 0 ; i < len ( fields ) ; i ++ {
2014-05-04 05:53:38 +00:00
var cell interface { }
scanResults [ i ] = & cell
2013-12-26 18:14:30 +00:00
}
2014-05-04 05:53:38 +00:00
if err := rows . Scan ( scanResults ... ) ; err != nil {
2013-12-26 18:14:30 +00:00
return err
}
2014-05-12 15:27:15 +00:00
if b , hasBeforeSet := bean . ( BeforeSetProcessor ) ; hasBeforeSet {
for ii , key := range fields {
b . BeforeSet ( key , Cell ( scanResults [ ii ] . ( * interface { } ) ) )
2014-05-04 05:53:38 +00:00
}
2014-05-12 15:27:15 +00:00
}
2014-05-04 05:53:38 +00:00
2014-05-23 06:18:45 +00:00
var tempMap = make ( map [ string ] int )
2014-05-12 15:27:15 +00:00
for ii , key := range fields {
2014-05-23 06:18:45 +00:00
var idx int
var ok bool
if idx , ok = tempMap [ strings . ToLower ( key ) ] ; ! ok {
idx = 0
} else {
idx = idx + 1
}
tempMap [ strings . ToLower ( key ) ] = idx
2014-08-21 16:47:19 +00:00
if fieldValue := session . getField ( dataStruct , key , table , idx ) ; fieldValue != nil {
2014-05-04 05:53:38 +00:00
rawValue := reflect . Indirect ( reflect . ValueOf ( scanResults [ ii ] ) )
2013-12-26 18:14:30 +00:00
//if row is null then ignore
if rawValue . Interface ( ) == nil {
continue
}
2014-05-04 05:53:38 +00:00
if fieldValue . CanAddr ( ) {
if structConvert , ok := fieldValue . Addr ( ) . Interface ( ) . ( core . Conversion ) ; ok {
if data , err := value2Bytes ( & rawValue ) ; err == nil {
structConvert . FromDB ( data )
} else {
session . Engine . LogError ( err )
}
continue
}
}
2014-05-09 13:53:00 +00:00
if _ , ok := fieldValue . Interface ( ) . ( core . Conversion ) ; ok {
2013-12-26 18:14:30 +00:00
if data , err := value2Bytes ( & rawValue ) ; err == nil {
2014-05-09 13:53:00 +00:00
if fieldValue . Kind ( ) == reflect . Ptr && fieldValue . IsNil ( ) {
fieldValue . Set ( reflect . New ( fieldValue . Type ( ) . Elem ( ) ) )
}
fieldValue . Interface ( ) . ( core . Conversion ) . FromDB ( data )
2013-12-26 18:14:30 +00:00
} else {
session . Engine . LogError ( err )
}
continue
}
2013-12-27 18:42:50 +00:00
rawValueType := reflect . TypeOf ( rawValue . Interface ( ) )
2013-12-26 18:14:30 +00:00
vv := reflect . ValueOf ( rawValue . Interface ( ) )
fieldType := fieldValue . Type ( )
hasAssigned := false
switch fieldType . Kind ( ) {
case reflect . Complex64 , reflect . Complex128 :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . String {
2013-12-26 18:14:30 +00:00
hasAssigned = true
x := reflect . New ( fieldType )
err := json . Unmarshal ( [ ] byte ( vv . String ( ) ) , x . Interface ( ) )
if err != nil {
2014-02-11 17:35:26 +00:00
session . Engine . LogError ( err )
2013-12-26 18:14:30 +00:00
return err
}
fieldValue . Set ( x . Elem ( ) )
}
case reflect . Slice , reflect . Array :
2013-12-27 18:42:50 +00:00
switch rawValueType . Kind ( ) {
2013-12-26 18:14:30 +00:00
case reflect . Slice , reflect . Array :
2013-12-27 18:42:50 +00:00
switch rawValueType . Elem ( ) . Kind ( ) {
2013-12-26 18:14:30 +00:00
case reflect . Uint8 :
2013-12-27 18:42:50 +00:00
if fieldType . Elem ( ) . Kind ( ) == reflect . Uint8 {
hasAssigned = true
fieldValue . Set ( vv )
}
2013-12-26 18:14:30 +00:00
}
}
case reflect . String :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . String {
2013-12-26 18:14:30 +00:00
hasAssigned = true
fieldValue . SetString ( vv . String ( ) )
}
case reflect . Bool :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Bool {
2013-12-26 18:14:30 +00:00
hasAssigned = true
fieldValue . SetBool ( vv . Bool ( ) )
}
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
2013-12-27 18:42:50 +00:00
switch rawValueType . Kind ( ) {
2013-12-26 18:14:30 +00:00
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
hasAssigned = true
fieldValue . SetInt ( vv . Int ( ) )
}
case reflect . Float32 , reflect . Float64 :
2013-12-27 18:42:50 +00:00
switch rawValueType . Kind ( ) {
2013-12-26 18:14:30 +00:00
case reflect . Float32 , reflect . Float64 :
hasAssigned = true
fieldValue . SetFloat ( vv . Float ( ) )
}
case reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uint :
2013-12-27 18:42:50 +00:00
switch rawValueType . Kind ( ) {
2013-12-26 18:14:30 +00:00
case reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uint :
hasAssigned = true
fieldValue . SetUint ( vv . Uint ( ) )
2014-01-02 04:19:30 +00:00
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
hasAssigned = true
fieldValue . SetUint ( uint64 ( vv . Int ( ) ) )
2013-12-26 18:14:30 +00:00
}
case reflect . Struct :
2014-02-11 06:16:14 +00:00
if fieldType == core . TimeType {
if rawValueType == core . TimeType {
2013-12-26 18:14:30 +00:00
hasAssigned = true
2014-04-21 03:13:53 +00:00
2014-04-20 15:49:16 +00:00
t := vv . Interface ( ) . ( time . Time )
z , _ := t . Zone ( )
if len ( z ) == 0 || t . Year ( ) == 0 { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location
session . Engine . LogDebug ( "empty zone key[%v] : %v | zone: %v | location: %+v\n" , key , t , z , * t . Location ( ) )
tt := time . Date ( t . Year ( ) , t . Month ( ) , t . Day ( ) , t . Hour ( ) ,
t . Minute ( ) , t . Second ( ) , t . Nanosecond ( ) , time . Local )
vv = reflect . ValueOf ( tt )
2014-04-20 08:01:53 +00:00
}
2014-04-21 01:58:12 +00:00
// !nashtsai! convert to engine location
t = vv . Interface ( ) . ( time . Time ) . In ( session . Engine . TZLocation )
vv = reflect . ValueOf ( t )
2014-01-02 04:49:02 +00:00
fieldValue . Set ( vv )
2014-04-21 01:58:12 +00:00
2014-04-20 15:49:16 +00:00
// t = fieldValue.Interface().(time.Time)
// z, _ = t.Zone()
// session.Engine.LogDebug("fieldValue key[%v]: %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
2013-12-26 18:14:30 +00:00
}
2014-01-02 04:19:30 +00:00
} else if session . Statement . UseCascade {
2014-04-08 08:46:23 +00:00
table := session . Engine . autoMapType ( * fieldValue )
2014-01-02 04:19:30 +00:00
if table != nil {
var x int64
if rawValueType . Kind ( ) == reflect . Int64 {
x = vv . Int ( )
}
if x != 0 {
// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
// property to be fetched lazily
structInter := reflect . New ( fieldValue . Type ( ) )
newsession := session . Engine . NewSession ( )
defer newsession . Close ( )
has , err := newsession . Id ( x ) . Get ( structInter . Interface ( ) )
if err != nil {
return err
}
if has {
v := structInter . Elem ( ) . Interface ( )
fieldValue . Set ( reflect . ValueOf ( v ) )
} else {
return errors . New ( "cascade obj is not exist!" )
}
}
} else {
session . Engine . LogError ( "unsupported struct type in Scan: " , fieldValue . Type ( ) . String ( ) )
}
2013-12-26 18:14:30 +00:00
}
case reflect . Ptr :
// !nashtsai! TODO merge duplicated codes above
//typeStr := fieldType.String()
switch fieldType {
// following types case matching ptr's native type, therefore assign ptr directly
2014-02-11 06:16:14 +00:00
case core . PtrStringType :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . String {
x := vv . String ( )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrBoolType :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Bool {
x := vv . Bool ( )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrTimeType :
if rawValueType == core . PtrTimeType {
2013-12-27 18:42:50 +00:00
hasAssigned = true
2014-01-02 04:49:02 +00:00
var x time . Time = rawValue . Interface ( ) . ( time . Time )
fieldValue . Set ( reflect . ValueOf ( & x ) )
2013-12-27 18:42:50 +00:00
}
2014-02-11 06:16:14 +00:00
case core . PtrFloat64Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Float64 {
x := vv . Float ( )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrUint64Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x uint64 = uint64 ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrInt64Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
x := vv . Int ( )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrFloat32Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Float64 {
var x float32 = float32 ( vv . Float ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrIntType :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x int = int ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrInt32Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x int32 = int32 ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrInt8Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x int8 = int8 ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrInt16Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x int16 = int16 ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrUintType :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x uint = uint ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . PtrUint32Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x uint32 = uint32 ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . Uint8Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x uint8 = uint8 ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . Uint16Type :
2013-12-27 18:42:50 +00:00
if rawValueType . Kind ( ) == reflect . Int64 {
var x uint16 = uint16 ( vv . Int ( ) )
hasAssigned = true
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
2014-02-11 06:16:14 +00:00
case core . Complex64Type :
2013-12-26 18:14:30 +00:00
var x complex64
err := json . Unmarshal ( [ ] byte ( vv . String ( ) ) , & x )
if err != nil {
session . Engine . LogError ( err )
} else {
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
hasAssigned = true
2014-02-11 06:16:14 +00:00
case core . Complex128Type :
2013-12-26 18:14:30 +00:00
var x complex128
err := json . Unmarshal ( [ ] byte ( vv . String ( ) ) , & x )
if err != nil {
session . Engine . LogError ( err )
} else {
fieldValue . Set ( reflect . ValueOf ( & x ) )
}
hasAssigned = true
} // switch fieldType
// default:
// session.Engine.LogError("unsupported type in Scan: ", reflect.TypeOf(v).String())
} // switch fieldType.Kind()
// !nashtsai! for value can't be assigned directly fallback to convert to []byte then back to value
if ! hasAssigned {
data , err := value2Bytes ( & rawValue )
if err == nil {
2014-01-07 09:33:27 +00:00
session . bytes2Value ( table . GetColumn ( key ) , fieldValue , data )
2013-12-26 18:14:30 +00:00
} else {
session . Engine . LogError ( err . Error ( ) )
}
}
2013-12-18 03:31:32 +00:00
}
}
2013-12-26 18:14:30 +00:00
return nil
2013-10-17 04:50:46 +00:00
2013-10-12 15:16:51 +00:00
}
2013-12-29 18:32:26 +00:00
func ( session * Session ) queryPreprocess ( sqlStr * string , paramStr ... interface { } ) {
2014-04-15 03:39:29 +00:00
for _ , filter := range session . Engine . dialect . Filters ( ) {
2014-01-07 09:33:27 +00:00
* sqlStr = filter . Do ( * sqlStr , session . Engine . dialect , session . Statement . RefTable )
2013-12-18 03:31:32 +00:00
}
2013-12-02 14:54:55 +00:00
2014-05-16 15:42:47 +00:00
session . Engine . logSQL ( * sqlStr , paramStr ... )
2013-12-27 18:42:50 +00:00
}
func ( session * Session ) query ( sqlStr string , paramStr ... interface { } ) ( resultsSlice [ ] map [ string ] [ ] byte , err error ) {
2014-07-21 06:56:26 +00:00
2013-12-29 18:32:26 +00:00
session . queryPreprocess ( & sqlStr , paramStr ... )
2013-12-02 14:54:55 +00:00
2013-12-18 03:31:32 +00:00
if session . IsAutoCommit {
2014-07-21 06:56:26 +00:00
return session . innerQuery ( session . Db , sqlStr , paramStr ... )
2013-12-18 03:31:32 +00:00
}
2014-07-21 06:56:26 +00:00
return session . txQuery ( session . Tx , sqlStr , paramStr ... )
2013-12-02 14:54:55 +00:00
}
2014-07-21 06:56:26 +00:00
func ( session * Session ) txQuery ( tx * core . Tx , sqlStr string , params ... interface { } ) ( resultsSlice [ ] map [ string ] [ ] byte , err error ) {
2013-12-27 18:42:50 +00:00
rows , err := tx . Query ( sqlStr , params ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return nil , err
}
defer rows . Close ( )
2013-12-02 14:54:55 +00:00
2013-12-18 03:31:32 +00:00
return rows2maps ( rows )
2013-12-02 14:54:55 +00:00
}
2014-07-21 06:56:26 +00:00
func ( session * Session ) innerQuery ( db * core . DB , sqlStr string , params ... interface { } ) ( resultsSlice [ ] map [ string ] [ ] byte , err error ) {
stmt , rows , err := session . Engine . LogSQLQueryTime ( sqlStr , params , func ( ) ( * core . Stmt , * core . Rows , error ) {
stmt , err := db . Prepare ( sqlStr )
if err != nil {
return stmt , nil , err
}
rows , err := stmt . Query ( params ... )
return stmt , rows , err
} )
if rows != nil {
defer rows . Close ( )
}
if stmt != nil {
defer stmt . Close ( )
2013-12-18 03:31:32 +00:00
}
if err != nil {
return nil , err
}
return rows2maps ( rows )
2013-10-17 04:50:46 +00:00
}
2013-11-22 06:11:07 +00:00
// Exec a raw sql and return records as []map[string][]byte
2013-12-27 18:42:50 +00:00
func ( session * Session ) Query ( sqlStr string , paramStr ... interface { } ) ( resultsSlice [ ] map [ string ] [ ] byte , err error ) {
2013-12-18 03:31:32 +00:00
err = session . newDb ( )
if err != nil {
return nil , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-09-17 09:36:34 +00:00
2013-12-27 18:42:50 +00:00
return session . query ( sqlStr , paramStr ... )
2013-09-17 09:36:34 +00:00
}
2014-08-30 14:17:59 +00:00
// =============================
// for string
// =============================
func ( session * Session ) query2 ( sqlStr string , paramStr ... interface { } ) ( resultsSlice [ ] map [ string ] string , err error ) {
session . queryPreprocess ( & sqlStr , paramStr ... )
if session . IsAutoCommit {
return query2 ( session . Db , sqlStr , paramStr ... )
}
return txQuery2 ( session . Tx , sqlStr , paramStr ... )
}
func txQuery2 ( tx * core . Tx , sqlStr string , params ... interface { } ) ( resultsSlice [ ] map [ string ] string , err error ) {
rows , err := tx . Query ( sqlStr , params ... )
if err != nil {
return nil , err
}
defer rows . Close ( )
return rows2Strings ( rows )
}
func query2 ( db * core . DB , sqlStr string , params ... interface { } ) ( resultsSlice [ ] map [ string ] string , err error ) {
s , err := db . Prepare ( sqlStr )
if err != nil {
return nil , err
}
defer s . Close ( )
rows , err := s . Query ( params ... )
if err != nil {
return nil , err
}
defer rows . Close ( )
return rows2Strings ( rows )
}
// Exec a raw sql and return records as []map[string]string
func ( session * Session ) Q ( sqlStr string , paramStr ... interface { } ) ( resultsSlice [ ] map [ string ] string , err error ) {
err = session . newDb ( )
if err != nil {
return nil , err
}
defer session . resetStatement ( )
if session . IsAutoClose {
defer session . Close ( )
}
return session . query2 ( sqlStr , paramStr ... )
}
2013-09-17 09:36:34 +00:00
// insert one or more beans
2013-05-03 07:26:51 +00:00
func ( session * Session ) Insert ( beans ... interface { } ) ( int64 , error ) {
2013-12-18 03:31:32 +00:00
var affected int64 = 0
var err error = nil
err = session . newDb ( )
if err != nil {
return 0 , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
for _ , bean := range beans {
sliceValue := reflect . Indirect ( reflect . ValueOf ( bean ) )
if sliceValue . Kind ( ) == reflect . Slice {
if session . Engine . SupportInsertMany ( ) {
cnt , err := session . innerInsertMulti ( bean )
if err != nil {
return affected , err
}
affected += cnt
} else {
size := sliceValue . Len ( )
for i := 0 ; i < size ; i ++ {
cnt , err := session . innerInsert ( sliceValue . Index ( i ) . Interface ( ) )
if err != nil {
return affected , err
}
affected += cnt
}
}
} else {
cnt , err := session . innerInsert ( bean )
if err != nil {
return affected , err
}
affected += cnt
}
}
return affected , err
2013-05-12 13:37:10 +00:00
}
2013-08-08 05:24:38 +00:00
func ( session * Session ) innerInsertMulti ( rowsSlicePtr interface { } ) ( int64 , error ) {
2013-12-18 03:31:32 +00:00
sliceValue := reflect . Indirect ( reflect . ValueOf ( rowsSlicePtr ) )
if sliceValue . Kind ( ) != reflect . Slice {
return 0 , errors . New ( "needs a pointer to a slice" )
}
bean := sliceValue . Index ( 0 ) . Interface ( )
2014-04-08 08:46:23 +00:00
elementValue := rValue ( bean )
//sliceElementType := elementValue.Type()
2013-12-18 03:31:32 +00:00
2014-04-08 08:46:23 +00:00
table := session . Engine . autoMapType ( elementValue )
2013-12-18 03:31:32 +00:00
session . Statement . RefTable = table
size := sliceValue . Len ( )
colNames := make ( [ ] string , 0 )
colMultiPlaces := make ( [ ] string , 0 )
var args = make ( [ ] interface { } , 0 )
2014-01-07 09:33:27 +00:00
cols := make ( [ ] * core . Column , 0 )
2013-12-18 03:31:32 +00:00
for i := 0 ; i < size ; i ++ {
elemValue := sliceValue . Index ( i ) . Interface ( )
colPlaces := make ( [ ] string , 0 )
// 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 {
2014-01-07 09:33:27 +00:00
for _ , col := range table . Columns ( ) {
2013-12-18 03:31:32 +00:00
fieldValue := reflect . Indirect ( reflect . ValueOf ( elemValue ) ) . FieldByName ( col . FieldName )
if col . IsAutoIncrement && fieldValue . Int ( ) == 0 {
continue
}
2014-01-09 06:44:41 +00:00
if col . MapType == core . ONLYFROMDB {
2013-12-18 03:31:32 +00:00
continue
}
if session . Statement . ColumnStr != "" {
if _ , ok := session . Statement . columnMap [ col . Name ] ; ! ok {
continue
}
}
if ( col . IsCreated || col . IsUpdated ) && session . Statement . UseAutoTime {
2014-04-15 15:27:08 +00:00
args = append ( args , session . Engine . NowTime ( col . SQLType . Name ) )
2013-12-18 03:31:32 +00:00
} 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 {
fieldValue := reflect . Indirect ( reflect . ValueOf ( elemValue ) ) . FieldByName ( col . FieldName )
if col . IsAutoIncrement && fieldValue . Int ( ) == 0 {
continue
}
2014-01-09 06:44:41 +00:00
if col . MapType == core . ONLYFROMDB {
2013-12-18 03:31:32 +00:00
continue
}
if session . Statement . ColumnStr != "" {
if _ , ok := session . Statement . columnMap [ col . Name ] ; ! ok {
continue
}
}
if ( col . IsCreated || col . IsUpdated ) && session . Statement . UseAutoTime {
2014-04-15 15:27:08 +00:00
args = append ( args , session . Engine . NowTime ( col . SQLType . Name ) )
2013-12-18 03:31:32 +00:00
} 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 )
statement := fmt . Sprintf ( "INSERT INTO %v%v%v (%v%v%v) VALUES (%v)" ,
session . Engine . QuoteStr ( ) ,
session . Statement . TableName ( ) ,
session . Engine . QuoteStr ( ) ,
session . Engine . QuoteStr ( ) ,
strings . Join ( colNames , session . Engine . QuoteStr ( ) + ", " + session . Engine . QuoteStr ( ) ) ,
session . Engine . QuoteStr ( ) ,
strings . Join ( colMultiPlaces , "),(" ) )
res , err := session . exec ( statement , args ... )
if err != nil {
return 0 , err
}
2014-04-11 07:37:27 +00:00
if cacher := session . Engine . getCacher2 ( table ) ; cacher != nil && session . Statement . UseCache {
2013-12-18 03:31:32 +00:00
session . cacheInsert ( session . Statement . TableName ( ) )
}
lenAfterClosures := len ( session . afterClosures )
for i := 0 ; i < size ; i ++ {
elemValue := sliceValue . Index ( i ) . Interface ( )
// handle AfterInsertProcessor
if session . IsAutoCommit {
// !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 ( )
2013-05-03 07:26:51 +00:00
}
2013-11-22 06:11:07 +00:00
// Insert multiple records
2013-08-08 05:24:38 +00:00
func ( session * Session ) InsertMulti ( rowsSlicePtr interface { } ) ( int64 , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return 0 , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-08-08 05:24:38 +00:00
2013-12-18 03:31:32 +00:00
return session . innerInsertMulti ( rowsSlicePtr )
2013-08-08 05:24:38 +00:00
}
2014-01-07 09:33:27 +00:00
func ( session * Session ) byte2Time ( col * core . Column , data [ ] byte ) ( outTime time . Time , outErr error ) {
2013-12-19 18:25:27 +00:00
sdata := strings . TrimSpace ( string ( data ) )
var x time . Time
var err error
if sdata == "0000-00-00 00:00:00" ||
sdata == "0001-01-01 00:00:00" {
2014-04-20 15:49:16 +00:00
} else if ! strings . ContainsAny ( sdata , "- :" ) { // !nashtsai! has only found that mymysql driver is using this for time type column
2013-12-19 18:25:27 +00:00
// time stamp
sd , err := strconv . ParseInt ( sdata , 10 , 64 )
if err == nil {
x = time . Unix ( 0 , sd )
2014-04-20 15:49:16 +00:00
// !nashtsai! HACK mymysql driver is casuing Local location being change to CHAT and cause wrong time conversion
x = x . In ( time . UTC )
x = time . Date ( x . Year ( ) , x . Month ( ) , x . Day ( ) , x . Hour ( ) ,
x . Minute ( ) , x . Second ( ) , x . Nanosecond ( ) , session . Engine . TZLocation )
2014-11-07 09:56:33 +00:00
session . Engine . LogDebugf ( "time(0) key[%v]: %+v | sdata: [%v]\n" , col . FieldName , x , sdata )
2014-04-20 15:49:16 +00:00
} else {
2014-11-07 09:56:33 +00:00
session . Engine . LogDebugf ( "time(0) err key[%v]: %+v | sdata: [%v]\n" , col . FieldName , x , sdata )
2013-12-19 18:25:27 +00:00
}
} else if len ( sdata ) > 19 {
2014-04-20 15:49:16 +00:00
2014-04-17 15:21:42 +00:00
x , err = time . ParseInLocation ( time . RFC3339Nano , sdata , session . Engine . TZLocation )
2014-11-07 09:56:33 +00:00
session . Engine . LogDebugf ( "time(1) key[%v]: %+v | sdata: [%v]\n" , col . FieldName , x , sdata )
2013-12-19 18:25:27 +00:00
if err != nil {
2014-04-17 15:21:42 +00:00
x , err = time . ParseInLocation ( "2006-01-02 15:04:05.999999999" , sdata , session . Engine . TZLocation )
2014-11-07 09:56:33 +00:00
session . Engine . LogDebugf ( "time(2) key[%v]: %+v | sdata: [%v]\n" , col . FieldName , x , sdata )
2013-12-19 18:25:27 +00:00
}
2013-12-20 07:11:56 +00:00
if err != nil {
2014-04-17 15:21:42 +00:00
x , err = time . ParseInLocation ( "2006-01-02 15:04:05.9999999 Z07:00" , sdata , session . Engine . TZLocation )
2014-11-07 09:56:33 +00:00
session . Engine . LogDebugf ( "time(3) key[%v]: %+v | sdata: [%v]\n" , col . FieldName , x , sdata )
2013-12-20 07:11:56 +00:00
}
2014-04-20 15:49:16 +00:00
2013-12-19 18:25:27 +00:00
} else if len ( sdata ) == 19 {
2014-04-17 15:21:42 +00:00
x , err = time . ParseInLocation ( "2006-01-02 15:04:05" , sdata , session . Engine . TZLocation )
2014-11-07 09:56:33 +00:00
session . Engine . LogDebugf ( "time(4) key[%v]: %+v | sdata: [%v]\n" , col . FieldName , x , sdata )
2013-12-19 18:25:27 +00:00
} else if len ( sdata ) == 10 && sdata [ 4 ] == '-' && sdata [ 7 ] == '-' {
2014-04-17 15:21:42 +00:00
x , err = time . ParseInLocation ( "2006-01-02" , sdata , session . Engine . TZLocation )
2014-11-07 09:56:33 +00:00
session . Engine . LogDebugf ( "time(5) key[%v]: %+v | sdata: [%v]\n" , col . FieldName , x , sdata )
2014-01-07 09:33:27 +00:00
} else if col . SQLType . Name == core . Time {
2013-12-20 07:11:56 +00:00
if strings . Contains ( sdata , " " ) {
ssd := strings . Split ( sdata , " " )
sdata = ssd [ 1 ]
2013-12-19 18:25:27 +00:00
}
2013-12-24 10:18:48 +00:00
sdata = strings . TrimSpace ( sdata )
2014-01-07 09:33:27 +00:00
if session . Engine . dialect . DBType ( ) == core . MYSQL && len ( sdata ) > 8 {
2013-12-20 07:11:56 +00:00
sdata = sdata [ len ( sdata ) - 8 : ]
2013-12-20 07:55:34 +00:00
}
2013-12-24 10:18:48 +00:00
2013-12-19 18:25:27 +00:00
st := fmt . Sprintf ( "2006-01-02 %v" , sdata )
2014-04-17 15:21:42 +00:00
x , err = time . ParseInLocation ( "2006-01-02 15:04:05" , st , session . Engine . TZLocation )
2014-11-07 09:56:33 +00:00
session . Engine . LogDebugf ( "time(6) key[%v]: %+v | sdata: [%v]\n" , col . FieldName , x , sdata )
2013-12-19 18:25:27 +00:00
} else {
outErr = errors . New ( fmt . Sprintf ( "unsupported time format %v" , sdata ) )
return
}
if err != nil {
outErr = errors . New ( fmt . Sprintf ( "unsupported time format %v: %v" , sdata , err ) )
return
}
2014-04-16 12:56:04 +00:00
outTime = x
2013-12-19 18:25:27 +00:00
return
}
2013-09-05 15:20:52 +00:00
// convert a db data([]byte) to a field value
2014-01-07 09:33:27 +00:00
func ( session * Session ) bytes2Value ( col * core . Column , fieldValue * reflect . Value , data [ ] byte ) error {
if structConvert , ok := fieldValue . Addr ( ) . Interface ( ) . ( core . Conversion ) ; ok {
2013-12-18 03:31:32 +00:00
return structConvert . FromDB ( data )
}
2014-05-12 15:27:15 +00:00
if structConvert , ok := fieldValue . Interface ( ) . ( core . Conversion ) ; ok {
return structConvert . FromDB ( data )
}
2013-12-18 03:31:32 +00:00
var v interface { }
key := col . Name
fieldType := fieldValue . Type ( )
switch fieldType . Kind ( ) {
case reflect . Complex64 , reflect . Complex128 :
x := reflect . New ( fieldType )
err := json . Unmarshal ( data , x . Interface ( ) )
if err != nil {
2013-12-26 18:14:30 +00:00
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
return err
}
fieldValue . Set ( x . Elem ( ) )
case reflect . Slice , reflect . Array , reflect . Map :
v = data
t := fieldType . Elem ( )
k := t . Kind ( )
if col . SQLType . IsText ( ) {
x := reflect . New ( fieldType )
err := json . Unmarshal ( data , x . Interface ( ) )
if err != nil {
2013-12-26 18:14:30 +00:00
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
return err
}
fieldValue . Set ( x . Elem ( ) )
} else if col . SQLType . IsBlob ( ) {
if k == reflect . Uint8 {
fieldValue . Set ( reflect . ValueOf ( v ) )
} else {
x := reflect . New ( fieldType )
err := json . Unmarshal ( data , x . Interface ( ) )
if err != nil {
2013-12-26 18:14:30 +00:00
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
return err
}
fieldValue . Set ( x . Elem ( ) )
}
} else {
return ErrUnSupportedType
}
case reflect . String :
fieldValue . SetString ( string ( data ) )
case reflect . Bool :
d := string ( data )
v , err := strconv . ParseBool ( d )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as bool: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( v ) )
case reflect . Int , reflect . Int8 , reflect . Int16 , reflect . Int32 , reflect . Int64 :
sdata := string ( data )
var x int64
var err error
// for mysql, when use bit, it returned \x01
2014-01-07 09:33:27 +00:00
if col . SQLType . Name == core . Bit &&
2014-04-20 15:49:16 +00:00
session . Engine . dialect . DBType ( ) == core . MYSQL { // !nashtsai! TODO dialect needs to provide conversion interface API
2013-12-18 03:31:32 +00:00
if len ( data ) == 1 {
x = int64 ( data [ 0 ] )
} else {
x = 0
}
//fmt.Println("######", x, data)
} else if strings . HasPrefix ( sdata , "0x" ) {
x , err = strconv . ParseInt ( sdata , 16 , 64 )
} else if strings . HasPrefix ( sdata , "0" ) {
x , err = strconv . ParseInt ( sdata , 8 , 64 )
2013-12-20 06:53:40 +00:00
} else if strings . ToLower ( sdata ) == "true" {
x = 1
} else if strings . ToLower ( sdata ) == "false" {
x = 0
2013-12-18 03:31:32 +00:00
} else {
x , err = strconv . ParseInt ( sdata , 10 , 64 )
}
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . SetInt ( x )
case reflect . Float32 , reflect . Float64 :
x , err := strconv . ParseFloat ( string ( data ) , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as float64: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . SetFloat ( x )
case reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uint :
x , err := strconv . ParseUint ( string ( data ) , 10 , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . SetUint ( x )
2013-12-20 07:11:56 +00:00
//Currently only support Time type
2013-12-18 03:31:32 +00:00
case reflect . Struct :
2014-02-11 06:16:14 +00:00
if fieldType == core . TimeType {
2013-12-19 18:25:27 +00:00
x , err := session . byte2Time ( col , data )
2013-12-18 03:31:32 +00:00
if err != nil {
2013-12-19 18:25:27 +00:00
return err
2013-12-18 03:31:32 +00:00
}
v = x
fieldValue . Set ( reflect . ValueOf ( v ) )
} else if session . Statement . UseCascade {
2014-04-08 08:46:23 +00:00
table := session . Engine . autoMapType ( * fieldValue )
2013-12-18 03:31:32 +00:00
if table != nil {
x , err := strconv . ParseInt ( string ( data ) , 10 , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
if x != 0 {
2013-12-25 15:50:37 +00:00
// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
// property to be fetched lazily
2013-12-18 03:31:32 +00:00
structInter := reflect . New ( fieldValue . Type ( ) )
newsession := session . Engine . NewSession ( )
defer newsession . Close ( )
has , err := newsession . Id ( x ) . Get ( structInter . Interface ( ) )
if err != nil {
return err
}
if has {
v = structInter . Elem ( ) . Interface ( )
fieldValue . Set ( reflect . ValueOf ( v ) )
} else {
return errors . New ( "cascade obj is not exist!" )
}
}
} else {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "unsupported struct type in Scan: %s" , fieldValue . Type ( ) . String ( ) )
2013-12-18 03:31:32 +00:00
}
}
case reflect . Ptr :
// !nashtsai! TODO merge duplicated codes above
//typeStr := fieldType.String()
switch fieldType {
// case "*string":
2014-02-11 06:16:14 +00:00
case core . PtrStringType :
2013-12-18 03:31:32 +00:00
x := string ( data )
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*bool":
2014-02-11 06:16:14 +00:00
case core . PtrBoolType :
2013-12-18 03:31:32 +00:00
d := string ( data )
v , err := strconv . ParseBool ( d )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as bool: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( & v ) )
// case "*complex64":
2014-02-11 06:16:14 +00:00
case core . PtrComplex64Type :
2013-12-18 03:31:32 +00:00
var x complex64
err := json . Unmarshal ( data , & x )
if err != nil {
2013-12-26 18:14:30 +00:00
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
return err
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*complex128":
2014-02-11 06:16:14 +00:00
case core . PtrComplex128Type :
2013-12-18 03:31:32 +00:00
var x complex128
err := json . Unmarshal ( data , & x )
if err != nil {
2013-12-26 18:14:30 +00:00
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
return err
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*float64":
2014-02-11 06:16:14 +00:00
case core . PtrFloat64Type :
2013-12-18 03:31:32 +00:00
x , err := strconv . ParseFloat ( string ( data ) , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as float64: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*float32":
2014-02-11 06:16:14 +00:00
case core . PtrFloat32Type :
2013-12-18 03:31:32 +00:00
var x float32
x1 , err := strconv . ParseFloat ( string ( data ) , 32 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as float32: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
x = float32 ( x1 )
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*time.Time":
2014-02-11 06:16:14 +00:00
case core . PtrTimeType :
2013-12-19 18:25:27 +00:00
x , err := session . byte2Time ( col , data )
2013-12-18 03:31:32 +00:00
if err != nil {
2013-12-19 18:25:27 +00:00
return err
2013-12-18 03:31:32 +00:00
}
v = x
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*uint64":
2014-02-11 06:16:14 +00:00
case core . PtrUint64Type :
2013-12-18 03:31:32 +00:00
var x uint64
x , err := strconv . ParseUint ( string ( data ) , 10 , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*uint":
2014-02-11 06:16:14 +00:00
case core . PtrUintType :
2013-12-18 03:31:32 +00:00
var x uint
x1 , err := strconv . ParseUint ( string ( data ) , 10 , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
x = uint ( x1 )
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*uint32":
2014-02-11 06:16:14 +00:00
case core . PtrUint32Type :
2013-12-18 03:31:32 +00:00
var x uint32
x1 , err := strconv . ParseUint ( string ( data ) , 10 , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
x = uint32 ( x1 )
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*uint8":
2014-02-11 06:16:14 +00:00
case core . PtrUint8Type :
2013-12-18 03:31:32 +00:00
var x uint8
x1 , err := strconv . ParseUint ( string ( data ) , 10 , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
x = uint8 ( x1 )
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*uint16":
2014-02-11 06:16:14 +00:00
case core . PtrUint16Type :
2013-12-18 03:31:32 +00:00
var x uint16
x1 , err := strconv . ParseUint ( string ( data ) , 10 , 64 )
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
x = uint16 ( x1 )
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*int64":
2014-02-11 06:16:14 +00:00
case core . PtrInt64Type :
2013-12-18 03:31:32 +00:00
sdata := string ( data )
var x int64
var err error
// for mysql, when use bit, it returned \x01
2014-01-07 09:33:27 +00:00
if col . SQLType . Name == core . Bit &&
2014-04-15 03:39:29 +00:00
strings . Contains ( session . Engine . DriverName ( ) , "mysql" ) {
2013-12-18 03:31:32 +00:00
if len ( data ) == 1 {
x = int64 ( data [ 0 ] )
} else {
x = 0
}
//fmt.Println("######", x, data)
} else if strings . HasPrefix ( sdata , "0x" ) {
x , err = strconv . ParseInt ( sdata , 16 , 64 )
} else if strings . HasPrefix ( sdata , "0" ) {
x , err = strconv . ParseInt ( sdata , 8 , 64 )
} else {
x , err = strconv . ParseInt ( sdata , 10 , 64 )
}
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*int":
2014-02-11 06:16:14 +00:00
case core . PtrIntType :
2013-12-18 03:31:32 +00:00
sdata := string ( data )
var x int
var x1 int64
var err error
// for mysql, when use bit, it returned \x01
2014-01-07 09:33:27 +00:00
if col . SQLType . Name == core . Bit &&
2014-04-15 03:39:29 +00:00
strings . Contains ( session . Engine . DriverName ( ) , "mysql" ) {
2013-12-18 03:31:32 +00:00
if len ( data ) == 1 {
x = int ( data [ 0 ] )
} else {
x = 0
}
//fmt.Println("######", x, data)
} else if strings . HasPrefix ( sdata , "0x" ) {
x1 , err = strconv . ParseInt ( sdata , 16 , 64 )
x = int ( x1 )
} else if strings . HasPrefix ( sdata , "0" ) {
x1 , err = strconv . ParseInt ( sdata , 8 , 64 )
x = int ( x1 )
} else {
x1 , err = strconv . ParseInt ( sdata , 10 , 64 )
x = int ( x1 )
}
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*int32":
2014-02-11 06:16:14 +00:00
case core . PtrInt32Type :
2013-12-18 03:31:32 +00:00
sdata := string ( data )
var x int32
var x1 int64
var err error
// for mysql, when use bit, it returned \x01
2014-01-07 09:33:27 +00:00
if col . SQLType . Name == core . Bit &&
2014-02-11 06:16:14 +00:00
session . Engine . dialect . DBType ( ) == core . MYSQL {
2013-12-18 03:31:32 +00:00
if len ( data ) == 1 {
x = int32 ( data [ 0 ] )
} else {
x = 0
}
//fmt.Println("######", x, data)
} else if strings . HasPrefix ( sdata , "0x" ) {
x1 , err = strconv . ParseInt ( sdata , 16 , 64 )
x = int32 ( x1 )
} else if strings . HasPrefix ( sdata , "0" ) {
x1 , err = strconv . ParseInt ( sdata , 8 , 64 )
x = int32 ( x1 )
} else {
x1 , err = strconv . ParseInt ( sdata , 10 , 64 )
x = int32 ( x1 )
}
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*int8":
2014-02-11 06:16:14 +00:00
case core . PtrInt8Type :
2013-12-18 03:31:32 +00:00
sdata := string ( data )
var x int8
var x1 int64
var err error
// for mysql, when use bit, it returned \x01
2014-01-07 09:33:27 +00:00
if col . SQLType . Name == core . Bit &&
2014-04-15 03:39:29 +00:00
strings . Contains ( session . Engine . DriverName ( ) , "mysql" ) {
2013-12-18 03:31:32 +00:00
if len ( data ) == 1 {
x = int8 ( data [ 0 ] )
} else {
x = 0
}
//fmt.Println("######", x, data)
} else if strings . HasPrefix ( sdata , "0x" ) {
x1 , err = strconv . ParseInt ( sdata , 16 , 64 )
x = int8 ( x1 )
} else if strings . HasPrefix ( sdata , "0" ) {
x1 , err = strconv . ParseInt ( sdata , 8 , 64 )
x = int8 ( x1 )
} else {
x1 , err = strconv . ParseInt ( sdata , 10 , 64 )
x = int8 ( x1 )
}
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
// case "*int16":
2014-02-11 06:16:14 +00:00
case core . PtrInt16Type :
2013-12-18 03:31:32 +00:00
sdata := string ( data )
var x int16
var x1 int64
var err error
// for mysql, when use bit, it returned \x01
2014-01-07 09:33:27 +00:00
if col . SQLType . Name == core . Bit &&
2014-04-15 03:39:29 +00:00
strings . Contains ( session . Engine . DriverName ( ) , "mysql" ) {
2013-12-18 03:31:32 +00:00
if len ( data ) == 1 {
x = int16 ( data [ 0 ] )
} else {
x = 0
}
//fmt.Println("######", x, data)
} else if strings . HasPrefix ( sdata , "0x" ) {
x1 , err = strconv . ParseInt ( sdata , 16 , 64 )
x = int16 ( x1 )
} else if strings . HasPrefix ( sdata , "0" ) {
x1 , err = strconv . ParseInt ( sdata , 8 , 64 )
x = int16 ( x1 )
} else {
x1 , err = strconv . ParseInt ( sdata , 10 , 64 )
x = int16 ( x1 )
}
if err != nil {
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
2013-12-18 03:31:32 +00:00
}
fieldValue . Set ( reflect . ValueOf ( & x ) )
default :
2014-07-09 13:48:48 +00:00
if fieldType . Elem ( ) . Kind ( ) == reflect . Struct {
if session . Statement . UseCascade {
structInter := reflect . New ( fieldType . Elem ( ) )
table := session . Engine . autoMapType ( structInter . Elem ( ) )
if table != nil {
x , err := strconv . ParseInt ( string ( data ) , 10 , 64 )
if err != nil {
return fmt . Errorf ( "arg %v as int: %s" , key , err . Error ( ) )
}
if x != 0 {
// !nashtsai! TODO for hasOne relationship, it's preferred to use join query for eager fetch
// however, also need to consider adding a 'lazy' attribute to xorm tag which allow hasOne
// property to be fetched lazily
newsession := session . Engine . NewSession ( )
defer newsession . Close ( )
has , err := newsession . Id ( x ) . Get ( structInter . Interface ( ) )
if err != nil {
return err
}
if has {
v = structInter . Interface ( )
fieldValue . Set ( reflect . ValueOf ( v ) )
} else {
return errors . New ( "cascade obj is not exist!" )
}
}
}
} else {
return fmt . Errorf ( "unsupported struct type in Scan: %s" , fieldValue . Type ( ) . String ( ) )
}
}
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "unsupported type in Scan: %s" , reflect . TypeOf ( v ) . String ( ) )
2013-12-18 03:31:32 +00:00
}
default :
2013-12-26 18:14:30 +00:00
return fmt . Errorf ( "unsupported type in Scan: %s" , reflect . TypeOf ( v ) . String ( ) )
2013-12-18 03:31:32 +00:00
}
return nil
2013-09-05 15:20:52 +00:00
}
// convert a field value of a struct to interface for put into db
2014-01-07 09:33:27 +00:00
func ( session * Session ) value2Interface ( col * core . Column , fieldValue reflect . Value ) ( interface { } , error ) {
2013-12-18 03:31:32 +00:00
if fieldValue . CanAddr ( ) {
2014-01-07 09:33:27 +00:00
if fieldConvert , ok := fieldValue . Addr ( ) . Interface ( ) . ( core . Conversion ) ; ok {
2013-12-18 03:31:32 +00:00
data , err := fieldConvert . ToDB ( )
if err != nil {
return 0 , err
} else {
return string ( data ) , nil
}
}
}
2014-05-04 05:53:38 +00:00
if fieldConvert , ok := fieldValue . Interface ( ) . ( core . Conversion ) ; ok {
data , err := fieldConvert . ToDB ( )
if err != nil {
return 0 , err
} else {
return string ( data ) , nil
}
}
2013-12-18 03:31:32 +00:00
fieldType := fieldValue . Type ( )
k := fieldType . Kind ( )
if k == reflect . Ptr {
if fieldValue . IsNil ( ) {
return nil , nil
} else if ! fieldValue . IsValid ( ) {
session . Engine . LogWarn ( "the field[" , col . FieldName , "] is invalid" )
return nil , nil
} else {
// !nashtsai! deference pointer type to instance type
fieldValue = fieldValue . Elem ( )
fieldType = fieldValue . Type ( )
k = fieldType . Kind ( )
}
}
switch k {
case reflect . Bool :
2014-04-18 10:39:07 +00:00
return fieldValue . Bool ( ) , nil
2013-12-18 03:31:32 +00:00
case reflect . String :
return fieldValue . String ( ) , nil
case reflect . Struct :
2014-02-11 06:16:14 +00:00
if fieldType == core . TimeType {
2014-04-15 15:27:08 +00:00
switch fieldValue . Interface ( ) . ( type ) {
2014-04-16 12:08:53 +00:00
case time . Time :
2014-05-05 14:26:17 +00:00
t := fieldValue . Interface ( ) . ( time . Time )
if session . Engine . dialect . DBType ( ) == core . MSSQL {
if t . IsZero ( ) {
return nil , nil
}
}
tf := session . Engine . FormatTime ( col . SQLType . Name , t )
2014-04-15 15:27:08 +00:00
return tf , nil
2014-04-16 12:08:53 +00:00
default :
2014-04-15 15:27:08 +00:00
return fieldValue . Interface ( ) , nil
2013-12-18 03:31:32 +00:00
}
}
if fieldTable , ok := session . Engine . Tables [ fieldValue . Type ( ) ] ; ok {
2013-12-24 10:18:48 +00:00
if len ( fieldTable . PrimaryKeys ) == 1 {
pkField := reflect . Indirect ( fieldValue ) . FieldByName ( fieldTable . PKColumns ( ) [ 0 ] . FieldName )
2013-12-18 03:31:32 +00:00
return pkField . Interface ( ) , nil
} else {
2013-12-24 10:18:48 +00:00
return 0 , fmt . Errorf ( "no primary key for col %v" , col . Name )
2013-12-18 03:31:32 +00:00
}
} else {
2013-12-24 10:18:48 +00:00
return 0 , fmt . Errorf ( "Unsupported type %v\n" , fieldValue . Type ( ) )
2013-12-18 03:31:32 +00:00
}
case reflect . Complex64 , reflect . Complex128 :
bytes , err := json . Marshal ( fieldValue . Interface ( ) )
if err != nil {
2014-02-11 17:35:26 +00:00
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
return 0 , err
}
return string ( bytes ) , nil
case reflect . Array , reflect . Slice , reflect . Map :
if ! fieldValue . IsValid ( ) {
return fieldValue . Interface ( ) , nil
}
if col . SQLType . IsText ( ) {
bytes , err := json . Marshal ( fieldValue . Interface ( ) )
if err != nil {
2014-02-11 17:35:26 +00:00
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
return 0 , err
}
return string ( bytes ) , nil
} else if col . SQLType . IsBlob ( ) {
var bytes [ ] byte
var err error
if ( k == reflect . Array || k == reflect . Slice ) &&
( fieldValue . Type ( ) . Elem ( ) . Kind ( ) == reflect . Uint8 ) {
bytes = fieldValue . Bytes ( )
} else {
bytes , err = json . Marshal ( fieldValue . Interface ( ) )
if err != nil {
2014-02-11 17:35:26 +00:00
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
return 0 , err
}
}
return bytes , nil
} else {
return nil , ErrUnSupportedType
}
2014-07-11 17:40:41 +00:00
case reflect . Uint8 , reflect . Uint16 , reflect . Uint32 , reflect . Uint64 , reflect . Uint :
return int64 ( fieldValue . Uint ( ) ) , nil
2013-12-18 03:31:32 +00:00
default :
return fieldValue . Interface ( ) , nil
}
2013-08-08 05:24:38 +00:00
}
func ( session * Session ) innerInsert ( bean interface { } ) ( int64 , error ) {
2014-08-28 16:34:09 +00:00
table := session . Engine . TableInfo ( bean )
2013-12-18 03:31:32 +00:00
session . Statement . RefTable = table
// handle BeforeInsertProcessor
for _ , closure := range session . beforeClosures {
closure ( bean )
}
cleanupProcessorsClosures ( & session . beforeClosures ) // cleanup after used
if processor , ok := interface { } ( bean ) . ( BeforeInsertProcessor ) ; ok {
processor . BeforeInsert ( )
}
// --
2014-01-07 09:33:27 +00:00
colNames , args , err := genCols ( table , session , bean , false , false )
2013-12-18 03:31:32 +00:00
if err != nil {
return 0 , err
}
colPlaces := strings . Repeat ( "?, " , len ( colNames ) )
colPlaces = colPlaces [ 0 : len ( colPlaces ) - 2 ]
2013-12-27 18:42:50 +00:00
sqlStr := fmt . Sprintf ( "INSERT INTO %v%v%v (%v%v%v) VALUES (%v)" ,
2013-12-18 03:31:32 +00:00
session . Engine . QuoteStr ( ) ,
session . Statement . TableName ( ) ,
session . Engine . QuoteStr ( ) ,
session . Engine . QuoteStr ( ) ,
strings . Join ( colNames , session . Engine . Quote ( ", " ) ) ,
session . Engine . QuoteStr ( ) ,
colPlaces )
handleAfterInsertProcessorFunc := func ( bean interface { } ) {
if session . IsAutoCommit {
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.
2013-12-31 03:44:11 +00:00
2014-04-15 03:39:29 +00:00
if session . Engine . DriverName ( ) != core . POSTGRES || table . AutoIncrement == "" {
2013-12-27 18:42:50 +00:00
res , err := session . exec ( sqlStr , args ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return 0 , err
} else {
handleAfterInsertProcessorFunc ( bean )
}
2014-04-11 07:37:27 +00:00
if cacher := session . Engine . getCacher2 ( table ) ; cacher != nil && session . Statement . UseCache {
2013-12-18 03:31:32 +00:00
session . cacheInsert ( session . Statement . TableName ( ) )
}
if table . Version != "" && session . Statement . checkVersion {
2014-01-09 06:44:41 +00:00
verValue , err := table . VersionColumn ( ) . ValueOf ( bean )
if err != nil {
session . Engine . LogError ( err )
} else if verValue . IsValid ( ) && verValue . CanSet ( ) {
2013-12-18 03:31:32 +00:00
verValue . SetInt ( 1 )
}
}
2013-12-24 10:18:48 +00:00
if table . AutoIncrement == "" {
2013-12-18 03:31:32 +00:00
return res . RowsAffected ( )
}
var id int64 = 0
id , err = res . LastInsertId ( )
if err != nil || id <= 0 {
return res . RowsAffected ( )
}
2014-01-09 06:44:41 +00:00
aiValue , err := table . AutoIncrColumn ( ) . ValueOf ( bean )
if err != nil {
session . Engine . LogError ( err )
}
if aiValue == nil || ! aiValue . IsValid ( ) /*|| aiValue.Int() != 0*/ || ! aiValue . CanSet ( ) {
2013-12-18 03:31:32 +00:00
return res . RowsAffected ( )
}
var v interface { } = id
2013-12-24 10:18:48 +00:00
switch aiValue . Type ( ) . Kind ( ) {
case reflect . Int32 :
v = int32 ( id )
case reflect . Int :
2013-12-18 03:31:32 +00:00
v = int ( id )
2013-12-24 10:18:48 +00:00
case reflect . Uint32 :
v = uint32 ( id )
case reflect . Uint64 :
v = uint64 ( id )
case reflect . Uint :
2013-12-18 03:31:32 +00:00
v = uint ( id )
}
2013-12-24 10:18:48 +00:00
aiValue . Set ( reflect . ValueOf ( v ) )
2013-12-18 03:31:32 +00:00
return res . RowsAffected ( )
} else {
2013-12-24 10:18:48 +00:00
//assert table.AutoIncrement != ""
2013-12-31 03:44:11 +00:00
sqlStr = sqlStr + " RETURNING " + session . Engine . Quote ( table . AutoIncrement )
2013-12-27 18:42:50 +00:00
res , err := session . query ( sqlStr , args ... )
2013-12-31 03:44:11 +00:00
2013-12-18 03:31:32 +00:00
if err != nil {
return 0 , err
} else {
handleAfterInsertProcessorFunc ( bean )
}
2014-04-11 07:37:27 +00:00
if cacher := session . Engine . getCacher2 ( table ) ; cacher != nil && session . Statement . UseCache {
2013-12-18 03:31:32 +00:00
session . cacheInsert ( session . Statement . TableName ( ) )
}
if table . Version != "" && session . Statement . checkVersion {
2014-01-09 06:44:41 +00:00
verValue , err := table . VersionColumn ( ) . ValueOf ( bean )
if err != nil {
session . Engine . LogError ( err )
} else if verValue . IsValid ( ) && verValue . CanSet ( ) {
2013-12-18 03:31:32 +00:00
verValue . SetInt ( 1 )
}
}
if len ( res ) < 1 {
return 0 , errors . New ( "insert no error but not returned id" )
}
2013-12-24 10:18:48 +00:00
idByte := res [ 0 ] [ table . AutoIncrement ]
2013-12-18 03:31:32 +00:00
id , err := strconv . ParseInt ( string ( idByte ) , 10 , 64 )
if err != nil {
return 1 , err
}
2014-01-09 06:44:41 +00:00
aiValue , err := table . AutoIncrColumn ( ) . ValueOf ( bean )
if err != nil {
session . Engine . LogError ( err )
}
if aiValue == nil || ! aiValue . IsValid ( ) /*|| aiValue. != 0*/ || ! aiValue . CanSet ( ) {
2013-12-18 03:31:32 +00:00
return 1 , nil
}
var v interface { } = id
2013-12-24 10:18:48 +00:00
switch aiValue . Type ( ) . Kind ( ) {
case reflect . Int32 :
v = int32 ( id )
case reflect . Int :
2013-12-18 03:31:32 +00:00
v = int ( id )
2013-12-24 10:18:48 +00:00
case reflect . Uint32 :
v = uint32 ( id )
case reflect . Uint64 :
v = uint64 ( id )
case reflect . Uint :
2013-12-18 03:31:32 +00:00
v = uint ( id )
}
2013-12-24 10:18:48 +00:00
aiValue . Set ( reflect . ValueOf ( v ) )
2013-12-18 03:31:32 +00:00
return 1 , nil
}
2013-05-03 07:26:51 +00:00
}
2013-10-12 15:16:51 +00:00
// Method InsertOne insert only one struct into database as a record.
// The in parameter bean must a struct or a point to struct. The return
2014-02-18 06:10:51 +00:00
// parameter is inserted and error
2013-08-08 05:24:38 +00:00
func ( session * Session ) InsertOne ( bean interface { } ) ( int64 , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return 0 , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
2013-09-17 09:36:34 +00:00
2013-12-18 03:31:32 +00:00
return session . innerInsert ( bean )
2013-09-17 09:36:34 +00:00
}
2013-12-27 18:42:50 +00:00
func ( statement * Statement ) convertUpdateSql ( sqlStr string ) ( string , string ) {
2013-12-24 10:18:48 +00:00
if statement . RefTable == nil || len ( statement . RefTable . PrimaryKeys ) != 1 {
2013-12-18 03:31:32 +00:00
return "" , ""
}
2014-11-07 09:56:33 +00:00
colstrs := statement . JoinColumns ( statement . RefTable . PKColumns ( ) )
2013-12-27 18:42:50 +00:00
sqls := splitNNoCase ( sqlStr , "where" , 2 )
2013-12-18 03:31:32 +00:00
if len ( sqls ) != 2 {
if len ( sqls ) == 1 {
return sqls [ 0 ] , fmt . Sprintf ( "SELECT %v FROM %v" ,
2014-11-07 09:56:33 +00:00
colstrs , statement . Engine . Quote ( statement . TableName ( ) ) )
2013-12-18 03:31:32 +00:00
}
return "" , ""
}
var whereStr = sqls [ 1 ]
//TODO: for postgres only, if any other database?
2013-12-24 10:18:48 +00:00
var paraStr string
2014-01-07 09:33:27 +00:00
if statement . Engine . dialect . DBType ( ) == core . POSTGRES {
2013-12-24 10:18:48 +00:00
paraStr = "$"
2014-01-07 09:33:27 +00:00
} else if statement . Engine . dialect . DBType ( ) == core . MSSQL {
2013-12-24 10:18:48 +00:00
paraStr = ":"
}
if paraStr != "" {
if strings . Contains ( sqls [ 1 ] , paraStr ) {
dollers := strings . Split ( sqls [ 1 ] , paraStr )
whereStr = dollers [ 0 ]
for i , c := range dollers [ 1 : ] {
ccs := strings . SplitN ( c , " " , 2 )
whereStr += fmt . Sprintf ( paraStr + "%v %v" , i + 1 , ccs [ 1 ] )
}
2013-12-18 03:31:32 +00:00
}
}
return sqls [ 0 ] , fmt . Sprintf ( "SELECT %v FROM %v WHERE %v" ,
2014-11-07 09:56:33 +00:00
colstrs , statement . Engine . Quote ( statement . TableName ( ) ) ,
2013-12-18 03:31:32 +00:00
whereStr )
2013-09-17 09:36:34 +00:00
}
2013-09-23 14:31:51 +00:00
func ( session * Session ) cacheInsert ( tables ... string ) error {
2014-11-08 03:12:37 +00:00
if session . Statement . RefTable == nil {
2013-12-18 03:31:32 +00:00
return ErrCacheFailed
}
2013-09-23 14:31:51 +00:00
2013-12-18 03:31:32 +00:00
table := session . Statement . RefTable
2014-04-11 07:37:27 +00:00
cacher := session . Engine . getCacher2 ( table )
2013-09-23 14:31:51 +00:00
2013-12-18 03:31:32 +00:00
for _ , t := range tables {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cache] clear sql:" , t )
2013-12-18 03:31:32 +00:00
cacher . ClearIds ( t )
}
2013-09-23 14:31:51 +00:00
2013-12-18 03:31:32 +00:00
return nil
2013-09-23 14:31:51 +00:00
}
2014-11-07 14:23:23 +00:00
func ( session * Session ) cacheUpdate ( sqlStr string , args ... interface { } ) error {
2013-12-24 10:18:48 +00:00
if session . Statement . RefTable == nil || len ( session . Statement . RefTable . PrimaryKeys ) != 1 {
2013-12-18 03:31:32 +00:00
return ErrCacheFailed
}
2013-12-27 18:42:50 +00:00
oldhead , newsql := session . Statement . convertUpdateSql ( sqlStr )
2013-12-18 03:31:32 +00:00
if newsql == "" {
return ErrCacheFailed
}
2014-04-15 03:39:29 +00:00
for _ , filter := range session . Engine . dialect . Filters ( ) {
2014-01-07 09:33:27 +00:00
newsql = filter . Do ( newsql , session . Engine . dialect , session . Statement . RefTable )
2013-12-18 03:31:32 +00:00
}
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheUpdate] new sql" , oldhead , newsql )
2013-12-18 03:31:32 +00:00
var nStart int
if len ( args ) > 0 {
2013-12-27 18:42:50 +00:00
if strings . Index ( sqlStr , "?" ) > - 1 {
2013-12-18 03:31:32 +00:00
nStart = strings . Count ( oldhead , "?" )
} else {
// only for pq, TODO: if any other databse?
nStart = strings . Count ( oldhead , "$" )
}
}
table := session . Statement . RefTable
2014-04-11 07:37:27 +00:00
cacher := session . Engine . getCacher2 ( table )
2013-12-18 03:31:32 +00:00
tableName := session . Statement . TableName ( )
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheUpdate] get cache sql" , newsql , args [ nStart : ] )
2014-01-25 02:07:11 +00:00
ids , err := core . GetCacheSql ( cacher , tableName , newsql , args [ nStart : ] )
2013-12-18 03:31:32 +00:00
if err != nil {
2014-11-07 09:56:33 +00:00
rows , err := session . Db . Query ( newsql , args [ nStart : ] ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return err
}
2014-11-07 09:56:33 +00:00
defer rows . Close ( )
2013-12-18 03:31:32 +00:00
2014-01-25 02:07:11 +00:00
ids = make ( [ ] core . PK , 0 )
2014-11-07 09:56:33 +00:00
for rows . Next ( ) {
2014-11-07 14:23:23 +00:00
var res = make ( [ ] string , len ( table . PrimaryKeys ) )
2014-11-07 09:56:33 +00:00
err = rows . ScanSlice ( & res )
if err != nil {
return err
2013-12-18 03:31:32 +00:00
}
2014-11-07 14:23:23 +00:00
var pk core . PK = make ( [ ] interface { } , len ( table . PrimaryKeys ) )
for i , col := range table . PKColumns ( ) {
if col . SQLType . IsNumeric ( ) {
n , err := strconv . ParseInt ( res [ i ] , 10 , 64 )
if err != nil {
return err
}
pk [ i ] = n
} else if col . SQLType . IsText ( ) {
pk [ i ] = res [ i ]
} else {
return errors . New ( "not supported" )
}
}
ids = append ( ids , pk )
2013-12-18 03:31:32 +00:00
}
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheUpdate] find updated id" , ids )
2013-12-18 03:31:32 +00:00
} / * else {
session . Engine . LogDebug ( "[xorm:cacheUpdate] del cached sql:" , tableName , newsql , args )
cacher . DelIds ( tableName , genSqlKey ( newsql , args ) )
} * /
for _ , id := range ids {
2014-01-25 02:07:11 +00:00
sid , err := id . ToString ( )
if err != nil {
return err
}
if bean := cacher . GetBean ( tableName , sid ) ; bean != nil {
2013-12-27 18:42:50 +00:00
sqls := splitNNoCase ( sqlStr , "where" , 2 )
2013-12-18 03:31:32 +00:00
if len ( sqls ) == 0 || len ( sqls ) > 2 {
return ErrCacheFailed
}
sqls = splitNNoCase ( sqls [ 0 ] , "set" , 2 )
if len ( sqls ) != 2 {
return ErrCacheFailed
}
kvs := strings . Split ( strings . TrimSpace ( sqls [ 1 ] ) , "," )
for idx , kv := range kvs {
sps := strings . SplitN ( kv , "=" , 2 )
sps2 := strings . Split ( sps [ 0 ] , "." )
colName := sps2 [ len ( sps2 ) - 1 ]
if strings . Contains ( colName , "`" ) {
colName = strings . TrimSpace ( strings . Replace ( colName , "`" , "" , - 1 ) )
} else if strings . Contains ( colName , session . Engine . QuoteStr ( ) ) {
colName = strings . TrimSpace ( strings . Replace ( colName , session . Engine . QuoteStr ( ) , "" , - 1 ) )
} else {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheUpdate] cannot find column" , tableName , colName )
2013-12-18 03:31:32 +00:00
return ErrCacheFailed
}
2014-01-07 09:33:27 +00:00
if col := table . GetColumn ( colName ) ; col != nil {
2014-01-09 06:44:41 +00:00
fieldValue , err := col . ValueOf ( bean )
if err != nil {
session . Engine . LogError ( err )
2013-12-18 03:31:32 +00:00
} else {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheUpdate] set bean field" , bean , colName , fieldValue . Interface ( ) )
2014-01-09 06:44:41 +00:00
if col . IsVersion && session . Statement . checkVersion {
fieldValue . SetInt ( fieldValue . Int ( ) + 1 )
} else {
fieldValue . Set ( reflect . ValueOf ( args [ idx ] ) )
}
2013-12-18 03:31:32 +00:00
}
} else {
2014-11-08 03:12:37 +00:00
session . Engine . LogErrorf ( "[cacheUpdate] ERROR: column %v is not table %v's" ,
2013-12-18 03:31:32 +00:00
colName , table . Name )
}
}
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheUpdate] update cache" , tableName , id , bean )
2014-01-25 02:07:11 +00:00
cacher . PutBean ( tableName , sid , bean )
2013-12-18 03:31:32 +00:00
}
}
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheUpdate] clear cached table sql:" , tableName )
2013-12-18 03:31:32 +00:00
cacher . ClearIds ( tableName )
return nil
2013-08-08 05:24:38 +00:00
}
2013-11-22 06:11:07 +00:00
// Update records, bean's non-empty fields are updated contents,
// condiBean' non-empty filds are conditions
// CAUTION:
2013-12-09 02:29:23 +00:00
// 1.bool will defaultly be updated content nor conditions
// You should call UseBool if you have bool to use.
// 2.float32 & float64 may be not inexact as conditions
2013-05-08 13:42:22 +00:00
func ( session * Session ) Update ( bean interface { } , condiBean ... interface { } ) ( int64 , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return 0 , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
t := rType ( bean )
var colNames [ ] string
var args [ ] interface { }
2014-01-07 09:33:27 +00:00
var table * core . Table
2013-12-18 03:31:32 +00:00
// handle before update processors
for _ , closure := range session . beforeClosures {
closure ( bean )
}
cleanupProcessorsClosures ( & session . beforeClosures ) // cleanup after used
if processor , ok := interface { } ( bean ) . ( BeforeUpdateProcessor ) ; ok {
processor . BeforeUpdate ( )
}
// --
if t . Kind ( ) == reflect . Struct {
2014-08-28 16:34:09 +00:00
table = session . Engine . TableInfo ( bean )
2013-12-18 03:31:32 +00:00
session . Statement . RefTable = table
if session . Statement . ColumnStr == "" {
2014-05-02 00:48:51 +00:00
colNames , args = buildUpdates ( session . Engine , table , bean , false , false ,
2014-03-24 12:41:07 +00:00
false , false , session . Statement . allUseBool , session . Statement . useAllCols ,
2014-04-29 06:16:53 +00:00
session . Statement . mustColumnMap , true )
2013-12-18 03:31:32 +00:00
} else {
2014-01-07 09:33:27 +00:00
colNames , args , err = genCols ( table , session , bean , true , true )
2013-12-18 03:31:32 +00:00
if err != nil {
return 0 , err
}
}
} else if t . Kind ( ) == reflect . Map {
if session . Statement . RefTable == nil {
return 0 , ErrTableNotFound
}
table = session . Statement . RefTable
colNames = make ( [ ] string , 0 )
args = make ( [ ] interface { } , 0 )
bValue := reflect . Indirect ( reflect . ValueOf ( bean ) )
for _ , v := range bValue . MapKeys ( ) {
colNames = append ( colNames , session . Engine . Quote ( v . String ( ) ) + " = ?" )
args = append ( args , bValue . MapIndex ( v ) . Interface ( ) )
}
} else {
return 0 , ErrParamsType
}
if session . Statement . UseAutoTime && table . Updated != "" {
colNames = append ( colNames , session . Engine . Quote ( table . Updated ) + " = ?" )
2014-05-23 06:18:45 +00:00
args = append ( args , session . Engine . NowTime ( table . UpdatedColumn ( ) . SQLType . Name ) )
2013-12-18 03:31:32 +00:00
}
2014-04-15 01:54:49 +00:00
//for update action to like "column = column + ?"
incColumns := session . Statement . getInc ( )
2014-04-18 14:14:15 +00:00
for _ , v := range incColumns {
colNames = append ( colNames , session . Engine . Quote ( v . colName ) + " = " + session . Engine . Quote ( v . colName ) + " + ?" )
args = append ( args , v . arg )
2014-04-15 01:54:49 +00:00
}
2014-07-15 15:25:24 +00:00
//for update action to like "column = column - ?"
decColumns := session . Statement . getDec ( )
for _ , v := range decColumns {
colNames = append ( colNames , session . Engine . Quote ( v . colName ) + " = " + session . Engine . Quote ( v . colName ) + " - ?" )
args = append ( args , v . arg )
}
2013-12-18 03:31:32 +00:00
var condiColNames [ ] string
var condiArgs [ ] interface { }
if len ( condiBean ) > 0 {
condiColNames , condiArgs = buildConditions ( session . Engine , session . Statement . RefTable , condiBean [ 0 ] , true , true ,
2014-03-24 12:41:07 +00:00
false , true , session . Statement . allUseBool , session . Statement . useAllCols ,
2014-11-05 07:04:53 +00:00
session . Statement . unscoped , session . Statement . mustColumnMap )
2013-12-18 03:31:32 +00:00
}
var condition = ""
session . Statement . processIdParam ( )
st := session . Statement
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if st . WhereStr != "" {
condition = fmt . Sprintf ( "%v" , st . WhereStr )
}
if condition == "" {
if len ( condiColNames ) > 0 {
2014-04-29 06:16:53 +00:00
condition = fmt . Sprintf ( "%v" , strings . Join ( condiColNames , " " + session . Engine . Dialect ( ) . AndStr ( ) + " " ) )
2013-12-18 03:31:32 +00:00
}
} else {
if len ( condiColNames ) > 0 {
2014-04-29 06:16:53 +00:00
condition = fmt . Sprintf ( "(%v) %v (%v)" , condition ,
session . Engine . Dialect ( ) . AndStr ( ) , strings . Join ( condiColNames , " " + session . Engine . Dialect ( ) . AndStr ( ) + " " ) )
2013-12-18 03:31:32 +00:00
}
}
2013-12-27 18:42:50 +00:00
var sqlStr , inSql string
2013-12-18 03:31:32 +00:00
var inArgs [ ] interface { }
2014-04-11 13:31:44 +00:00
doIncVer := false
2014-04-11 13:31:44 +00:00
var verValue * reflect . Value
2013-12-18 03:31:32 +00:00
if table . Version != "" && session . Statement . checkVersion {
if condition != "" {
2014-04-29 06:16:53 +00:00
condition = fmt . Sprintf ( "WHERE (%v) %v %v = ?" , condition , session . Engine . Dialect ( ) . AndStr ( ) ,
2013-12-18 03:31:32 +00:00
session . Engine . Quote ( table . Version ) )
} else {
condition = fmt . Sprintf ( "WHERE %v = ?" , session . Engine . Quote ( table . Version ) )
}
inSql , inArgs = session . Statement . genInSql ( )
if len ( inSql ) > 0 {
if condition != "" {
2014-04-29 06:16:53 +00:00
condition += " " + session . Engine . Dialect ( ) . AndStr ( ) + " " + inSql
2013-12-18 03:31:32 +00:00
} else {
condition = "WHERE " + inSql
}
}
2013-12-27 18:42:50 +00:00
sqlStr = fmt . Sprintf ( "UPDATE %v SET %v, %v %v" ,
2013-12-18 03:31:32 +00:00
session . Engine . Quote ( session . Statement . TableName ( ) ) ,
strings . Join ( colNames , ", " ) ,
session . Engine . Quote ( table . Version ) + " = " + session . Engine . Quote ( table . Version ) + " + 1" ,
condition )
2014-04-11 13:31:44 +00:00
verValue , err = table . VersionColumn ( ) . ValueOf ( bean )
2014-01-09 06:44:41 +00:00
if err != nil {
return 0 , err
}
2014-04-11 13:31:44 +00:00
condiArgs = append ( condiArgs , verValue . Interface ( ) )
doIncVer = true
2013-12-18 03:31:32 +00:00
} else {
if condition != "" {
condition = "WHERE " + condition
}
inSql , inArgs = session . Statement . genInSql ( )
if len ( inSql ) > 0 {
if condition != "" {
2014-04-29 06:16:53 +00:00
condition += " " + session . Engine . Dialect ( ) . AndStr ( ) + " " + inSql
2013-12-18 03:31:32 +00:00
} else {
condition = "WHERE " + inSql
}
}
2013-12-27 18:42:50 +00:00
sqlStr = fmt . Sprintf ( "UPDATE %v SET %v %v" ,
2013-12-18 03:31:32 +00:00
session . Engine . Quote ( session . Statement . TableName ( ) ) ,
strings . Join ( colNames , ", " ) ,
condition )
}
args = append ( args , st . Params ... )
args = append ( args , inArgs ... )
args = append ( args , condiArgs ... )
2013-12-27 18:42:50 +00:00
res , err := session . exec ( sqlStr , args ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return 0 , err
2014-04-11 13:31:44 +00:00
} else if doIncVer {
verValue . SetInt ( verValue . Int ( ) + 1 )
2013-12-18 03:31:32 +00:00
}
2014-04-11 07:37:27 +00:00
if cacher := session . Engine . getCacher2 ( table ) ; cacher != nil && session . Statement . UseCache {
2014-01-07 09:33:27 +00:00
cacher . ClearIds ( session . Statement . TableName ( ) )
cacher . ClearBeans ( session . Statement . TableName ( ) )
2013-12-18 03:31:32 +00:00
}
// handle after update processors
if session . IsAutoCommit {
for _ , closure := range session . afterClosures {
closure ( bean )
}
if processor , ok := interface { } ( bean ) . ( AfterUpdateProcessor ) ; ok {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[event]" , session . Statement . TableName ( ) , " has after update processor" )
2013-12-18 03:31:32 +00:00
processor . AfterUpdate ( )
}
} else {
lenAfterClosures := len ( session . afterClosures )
if lenAfterClosures > 0 {
if value , has := session . afterUpdateBeans [ bean ] ; has && value != nil {
* value = append ( * value , session . afterClosures ... )
} else {
afterClosures := make ( [ ] func ( interface { } ) , lenAfterClosures )
copy ( afterClosures , session . afterClosures )
session . afterUpdateBeans [ bean ] = & afterClosures
}
} else {
if _ , ok := interface { } ( bean ) . ( AfterInsertProcessor ) ; ok {
session . afterUpdateBeans [ bean ] = nil
}
}
}
cleanupProcessorsClosures ( & session . afterClosures ) // cleanup after used
// --
return res . RowsAffected ( )
2013-09-17 09:36:34 +00:00
}
2013-12-27 18:42:50 +00:00
func ( session * Session ) cacheDelete ( sqlStr string , args ... interface { } ) error {
2013-12-24 10:18:48 +00:00
if session . Statement . RefTable == nil || len ( session . Statement . RefTable . PrimaryKeys ) != 1 {
2013-12-18 03:31:32 +00:00
return ErrCacheFailed
}
2014-04-15 03:39:29 +00:00
for _ , filter := range session . Engine . dialect . Filters ( ) {
2014-01-07 09:33:27 +00:00
sqlStr = filter . Do ( sqlStr , session . Engine . dialect , session . Statement . RefTable )
2013-12-18 03:31:32 +00:00
}
2013-12-27 18:42:50 +00:00
newsql := session . Statement . convertIdSql ( sqlStr )
2013-12-18 03:31:32 +00:00
if newsql == "" {
return ErrCacheFailed
}
2014-04-11 07:37:27 +00:00
cacher := session . Engine . getCacher2 ( session . Statement . RefTable )
2013-12-18 03:31:32 +00:00
tableName := session . Statement . TableName ( )
2014-01-25 02:07:11 +00:00
ids , err := core . GetCacheSql ( cacher , tableName , newsql , args )
2013-12-18 03:31:32 +00:00
if err != nil {
resultsSlice , err := session . query ( newsql , args ... )
if err != nil {
return err
}
2014-01-25 02:07:11 +00:00
ids = make ( [ ] core . PK , 0 )
2013-12-18 03:31:32 +00:00
if len ( resultsSlice ) > 0 {
for _ , data := range resultsSlice {
var id int64
2013-12-24 10:18:48 +00:00
if v , ok := data [ session . Statement . RefTable . PrimaryKeys [ 0 ] ] ; ! ok {
2013-12-18 03:31:32 +00:00
return errors . New ( "no id" )
} else {
id , err = strconv . ParseInt ( string ( v ) , 10 , 64 )
if err != nil {
return err
}
}
2014-01-25 02:07:11 +00:00
ids = append ( ids , core . PK { id } )
2013-12-18 03:31:32 +00:00
}
}
} / * else {
session . Engine . LogDebug ( "delete cache sql %v" , newsql )
cacher . DelIds ( tableName , genSqlKey ( newsql , args ) )
} * /
for _ , id := range ids {
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheDelete] delete cache obj" , tableName , id )
2014-01-25 02:07:11 +00:00
sid , err := id . ToString ( )
if err != nil {
return err
}
cacher . DelBean ( tableName , sid )
2013-12-18 03:31:32 +00:00
}
2014-11-08 03:12:37 +00:00
session . Engine . LogDebug ( "[cacheDelete] clear cache sql" , tableName )
2013-12-18 03:31:32 +00:00
cacher . ClearIds ( tableName )
return nil
2013-05-03 07:26:51 +00:00
}
2013-11-22 06:11:07 +00:00
// Delete records, bean's non-empty fields are conditions
2013-05-03 07:26:51 +00:00
func ( session * Session ) Delete ( bean interface { } ) ( int64 , error ) {
2013-12-18 03:31:32 +00:00
err := session . newDb ( )
if err != nil {
return 0 , err
}
2014-05-30 16:28:51 +00:00
defer session . resetStatement ( )
2013-12-18 03:31:32 +00:00
if session . IsAutoClose {
defer session . Close ( )
}
// handle before delete processors
for _ , closure := range session . beforeClosures {
closure ( bean )
}
cleanupProcessorsClosures ( & session . beforeClosures )
if processor , ok := interface { } ( bean ) . ( BeforeDeleteProcessor ) ; ok {
processor . BeforeDelete ( )
}
// --
2014-08-28 16:34:09 +00:00
table := session . Engine . TableInfo ( bean )
2013-12-18 03:31:32 +00:00
session . Statement . RefTable = table
colNames , args := buildConditions ( session . Engine , table , bean , true , true ,
2014-03-24 12:41:07 +00:00
false , true , session . Statement . allUseBool , session . Statement . useAllCols ,
2014-11-05 07:04:53 +00:00
session . Statement . unscoped , session . Statement . mustColumnMap )
2013-12-18 03:31:32 +00:00
var condition = ""
2014-04-29 06:16:53 +00:00
var andStr = session . Engine . dialect . AndStr ( )
2013-12-18 03:31:32 +00:00
session . Statement . processIdParam ( )
if session . Statement . WhereStr != "" {
condition = session . Statement . WhereStr
if len ( colNames ) > 0 {
2014-04-29 06:16:53 +00:00
condition += " " + andStr + " " + strings . Join ( colNames , " " + andStr + " " )
2013-12-18 03:31:32 +00:00
}
} else {
2014-04-29 06:16:53 +00:00
condition = strings . Join ( colNames , " " + andStr + " " )
2013-12-18 03:31:32 +00:00
}
inSql , inArgs := session . Statement . genInSql ( )
if len ( inSql ) > 0 {
if len ( condition ) > 0 {
2014-04-29 06:16:53 +00:00
condition += " " + andStr + " "
2013-12-18 03:31:32 +00:00
}
condition += inSql
args = append ( args , inArgs ... )
}
if len ( condition ) == 0 {
return 0 , ErrNeedDeletedCond
}
2014-11-03 13:03:12 +00:00
sqlStr , sqlStrForCache := "" , ""
2014-11-07 09:56:33 +00:00
argsForCache := make ( [ ] interface { } , 0 , len ( args ) * 2 )
2014-11-05 07:04:53 +00:00
if session . Statement . unscoped || table . DeletedColumn ( ) == nil { // tag "deleted" is disabled
2014-11-03 13:03:12 +00:00
sqlStr = fmt . Sprintf ( "DELETE FROM %v WHERE %v" ,
session . Engine . Quote ( session . Statement . TableName ( ) ) , condition )
sqlStrForCache = sqlStr
copy ( argsForCache , args )
argsForCache = append ( session . Statement . Params , argsForCache ... )
} else {
// !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache.
sqlStrForCache = fmt . Sprintf ( "DELETE FROM %v WHERE %v" ,
session . Engine . Quote ( session . Statement . TableName ( ) ) , condition )
copy ( argsForCache , args )
argsForCache = append ( session . Statement . Params , argsForCache ... )
2014-11-05 06:29:34 +00:00
deletedColumn := table . DeletedColumn ( )
2014-11-03 13:03:12 +00:00
sqlStr = fmt . Sprintf ( "UPDATE %v SET %v = ? WHERE %v" ,
session . Engine . Quote ( session . Statement . TableName ( ) ) ,
2014-11-05 06:29:34 +00:00
session . Engine . Quote ( deletedColumn . Name ) ,
2014-11-03 13:03:12 +00:00
condition )
// !oinume! Insert NowTime to the head of session.Statement.Params
session . Statement . Params = append ( session . Statement . Params , "" )
paramsLen := len ( session . Statement . Params )
copy ( session . Statement . Params [ 1 : paramsLen ] , session . Statement . Params [ 0 : paramsLen - 1 ] )
2014-11-05 06:29:34 +00:00
session . Statement . Params [ 0 ] = session . Engine . NowTime ( deletedColumn . SQLType . Name )
2014-11-03 13:03:12 +00:00
}
2013-12-18 03:31:32 +00:00
args = append ( session . Statement . Params , args ... )
2014-04-11 07:37:27 +00:00
if cacher := session . Engine . getCacher2 ( session . Statement . RefTable ) ; cacher != nil && session . Statement . UseCache {
2014-11-03 13:03:12 +00:00
session . cacheDelete ( sqlStrForCache , argsForCache ... )
2013-12-18 03:31:32 +00:00
}
2013-12-27 18:42:50 +00:00
res , err := session . exec ( sqlStr , args ... )
2013-12-18 03:31:32 +00:00
if err != nil {
return 0 , err
}
// handle after delete processors
if session . IsAutoCommit {
for _ , closure := range session . afterClosures {
closure ( bean )
}
if processor , ok := interface { } ( bean ) . ( AfterDeleteProcessor ) ; ok {
processor . AfterDelete ( )
}
} else {
lenAfterClosures := len ( session . afterClosures )
if lenAfterClosures > 0 {
if value , has := session . afterDeleteBeans [ bean ] ; has && value != nil {
* value = append ( * value , session . afterClosures ... )
} else {
afterClosures := make ( [ ] func ( interface { } ) , lenAfterClosures )
copy ( afterClosures , session . afterClosures )
session . afterDeleteBeans [ bean ] = & afterClosures
}
} else {
if _ , ok := interface { } ( bean ) . ( AfterInsertProcessor ) ; ok {
session . afterDeleteBeans [ bean ] = nil
}
}
}
cleanupProcessorsClosures ( & session . afterClosures )
// --
return res . RowsAffected ( )
2013-05-03 07:26:51 +00:00
}
2014-02-11 06:16:14 +00:00
2014-10-28 15:10:06 +00:00
func ( s * Session ) Sync2 ( beans ... interface { } ) error {
engine := s . Engine
tables , err := engine . DBMetas ( )
if err != nil {
return err
}
structTables := make ( [ ] * core . Table , 0 )
for _ , bean := range beans {
table := engine . TableInfo ( bean )
structTables = append ( structTables , table )
var oriTable * core . Table
for _ , tb := range tables {
if tb . Name == table . Name {
oriTable = tb
break
}
}
if oriTable == nil {
err = engine . StoreEngine ( s . Statement . StoreEngine ) . CreateTable ( bean )
if err != nil {
return err
}
err = engine . CreateUniques ( bean )
if err != nil {
return err
}
err = engine . CreateIndexes ( bean )
if err != nil {
return err
}
} else {
for _ , col := range table . Columns ( ) {
var oriCol * core . Column
for _ , col2 := range oriTable . Columns ( ) {
if col . Name == col2 . Name {
oriCol = col2
break
}
}
if oriCol != nil {
expectedType := engine . dialect . SqlType ( col )
curType := engine . dialect . SqlType ( oriCol )
if expectedType != curType {
if expectedType == core . Text &&
strings . HasPrefix ( curType , core . Varchar ) {
2014-11-08 03:12:37 +00:00
// currently only support mysql & postgres
2014-10-28 15:10:06 +00:00
if engine . dialect . DBType ( ) == core . MYSQL ||
engine . dialect . DBType ( ) == core . POSTGRES {
engine . LogInfof ( "Table %s column %s change type from %s to %s\n" ,
table . Name , col . Name , curType , expectedType )
_ , err = engine . Exec ( engine . dialect . ModifyColumnSql ( table . Name , col ) )
} else {
engine . LogWarnf ( "Table %s column %s db type is %s, struct type is %s\n" ,
table . Name , col . Name , curType , expectedType )
}
} else {
engine . LogWarnf ( "Table %s column %s db type is %s, struct type is %s" ,
table . Name , col . Name , curType , expectedType )
}
}
if col . Default != oriCol . Default {
engine . LogWarnf ( "Table %s Column %s db default is %s, struct default is %s" ,
table . Name , col . Name , oriCol . Default , col . Default )
}
if col . Nullable != oriCol . Nullable {
engine . LogWarnf ( "Table %s Column %s db nullable is %v, struct nullable is %v" ,
table . Name , col . Name , oriCol . Nullable , col . Nullable )
}
} else {
session := engine . NewSession ( )
session . Statement . RefTable = table
defer session . Close ( )
err = session . addColumn ( col . Name )
}
if err != nil {
return err
}
}
var foundIndexNames = make ( map [ string ] bool )
for name , index := range table . Indexes {
var oriIndex * core . Index
for name2 , index2 := range oriTable . Indexes {
if index . Equal ( index2 ) {
oriIndex = index2
foundIndexNames [ name2 ] = true
break
}
}
if oriIndex != nil {
if oriIndex . Type != index . Type {
sql := engine . dialect . DropIndexSql ( table . Name , oriIndex )
_ , err = engine . Exec ( sql )
if err != nil {
return err
}
oriIndex = nil
}
}
if oriIndex == nil {
if index . Type == core . UniqueType {
session := engine . NewSession ( )
session . Statement . RefTable = table
defer session . Close ( )
err = session . addUnique ( table . Name , name )
} else if index . Type == core . IndexType {
session := engine . NewSession ( )
session . Statement . RefTable = table
defer session . Close ( )
err = session . addIndex ( table . Name , name )
}
if err != nil {
return err
}
}
}
for name2 , index2 := range oriTable . Indexes {
if _ , ok := foundIndexNames [ name2 ] ; ! ok {
sql := engine . dialect . DropIndexSql ( table . Name , index2 )
_ , err = engine . Exec ( sql )
if err != nil {
return err
}
}
}
}
}
for _ , table := range tables {
var oriTable * core . Table
for _ , structTable := range structTables {
if table . Name == structTable . Name {
oriTable = structTable
break
}
}
if oriTable == nil {
2014-11-08 03:12:37 +00:00
engine . LogWarnf ( "Table %s has no struct to mapping it" , table . Name )
2014-10-28 15:10:06 +00:00
continue
}
for _ , colName := range table . ColumnsSeq ( ) {
if oriTable . GetColumn ( colName ) == nil {
engine . LogWarnf ( "Table %s has column %s but struct has not related field" ,
table . Name , colName )
}
}
}
return nil
}
2014-11-05 07:04:53 +00:00
// Always disable struct tag "deleted"
func ( session * Session ) Unscoped ( ) * Session {
session . Statement . Unscoped ( )
return session
}
2014-02-11 06:16:14 +00:00
func genCols ( table * core . Table , session * Session , bean interface { } , useCol bool , includeQuote bool ) ( [ ] string , [ ] interface { } , error ) {
colNames := make ( [ ] string , 0 )
args := make ( [ ] interface { } , 0 )
for _ , col := range table . Columns ( ) {
lColName := strings . ToLower ( col . Name )
if useCol && ! col . IsVersion && ! col . IsCreated && ! col . IsUpdated {
if _ , ok := session . Statement . columnMap [ lColName ] ; ! ok {
continue
}
}
if col . MapType == core . ONLYFROMDB {
continue
}
fieldValuePtr , err := col . ValueOf ( bean )
if err != nil {
session . Engine . LogError ( err )
continue
}
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
}
}
}
2014-11-05 06:29:34 +00:00
if col . IsDeleted {
2014-11-03 13:03:12 +00:00
continue
}
2014-02-11 06:16:14 +00:00
if session . Statement . ColumnStr != "" {
if _ , ok := session . Statement . columnMap [ lColName ] ; ! ok {
continue
}
}
if session . Statement . OmitStr != "" {
if _ , ok := session . Statement . columnMap [ lColName ] ; ok {
continue
}
}
if ( col . IsCreated || col . IsUpdated ) && session . Statement . UseAutoTime {
2014-04-23 06:01:04 +00:00
args = append ( args , session . Engine . NowTime ( col . SQLType . Name ) )
2014-02-11 06:16:14 +00:00
} else if col . IsVersion && session . Statement . checkVersion {
args = append ( args , 1 )
2014-05-31 04:19:46 +00:00
//} else if !col.DefaultIsEmpty {
2014-02-11 06:16:14 +00:00
} else {
arg , err := session . value2Interface ( col , fieldValue )
if err != nil {
return colNames , args , err
}
args = append ( args , arg )
}
if includeQuote {
colNames = append ( colNames , session . Engine . Quote ( col . Name ) + " = ?" )
} else {
colNames = append ( colNames , col . Name )
}
}
return colNames , args , nil
}