summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/go-xorm/xorm/session.go
diff options
context:
space:
mode:
authorLunny Xiao <xiaolunwen@gmail.com>2017-01-03 16:20:28 +0800
committerGitHub <noreply@github.com>2017-01-03 16:20:28 +0800
commit980dd0bf513597abc4753585e6cc7eb8b30272d1 (patch)
tree8ca1f6bfb1a518cb6bdd5261d2454b279fb43101 /vendor/github.com/go-xorm/xorm/session.go
parent70900bd16724ccb73d8b69e830f046b4815c9595 (diff)
downloadgitea-980dd0bf513597abc4753585e6cc7eb8b30272d1.tar.gz
gitea-980dd0bf513597abc4753585e6cc7eb8b30272d1.zip
Update xorm and dependencies vendor for feature to dump to other database (#565)
* update xorm and dependencies vendor for feature to dump to other database * fix golint
Diffstat (limited to 'vendor/github.com/go-xorm/xorm/session.go')
-rw-r--r--vendor/github.com/go-xorm/xorm/session.go2530
1 files changed, 27 insertions, 2503 deletions
diff --git a/vendor/github.com/go-xorm/xorm/session.go b/vendor/github.com/go-xorm/xorm/session.go
index 10033313b7..1eabdc897f 100644
--- a/vendor/github.com/go-xorm/xorm/session.go
+++ b/vendor/github.com/go-xorm/xorm/session.go
@@ -113,7 +113,10 @@ func (session *Session) Prepare() *Session {
return session
}
-// Sql !DEPRECIATED! will be deprecated, please use SQL instead.
+// 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.
+//
+// Deprecated: use SQL instead.
func (session *Session) Sql(query string, args ...interface{}) *Session {
return session.SQL(query, args...)
}
@@ -143,15 +146,16 @@ func (session *Session) Or(query interface{}, args ...interface{}) *Session {
return session
}
-// Id will be deprecated, please use ID instead
+// Id provides converting id as a query condition
+//
+// Deprecated: use ID instead
func (session *Session) Id(id interface{}) *Session {
- session.Statement.Id(id)
- return session
+ return session.ID(id)
}
// ID provides converting id as a query condition
func (session *Session) ID(id interface{}) *Session {
- session.Statement.Id(id)
+ session.Statement.ID(id)
return session
}
@@ -376,84 +380,6 @@ func (session *Session) Conds() builder.Cond {
return session.Statement.cond
}
-// Begin a transaction
-func (session *Session) Begin() error {
- if session.IsAutoCommit {
- tx, err := session.DB().Begin()
- if err != nil {
- return err
- }
- session.IsAutoCommit = false
- session.IsCommitedOrRollbacked = false
- session.Tx = tx
- session.saveLastSQL("BEGIN TRANSACTION")
- }
- return nil
-}
-
-// Rollback When using transaction, you can rollback if any error
-func (session *Session) Rollback() error {
- if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
- session.saveLastSQL(session.Engine.dialect.RollBackStr())
- session.IsCommitedOrRollbacked = true
- return session.Tx.Rollback()
- }
- return nil
-}
-
-// Commit When using transaction, Commit will commit all operations.
-func (session *Session) Commit() error {
- if !session.IsAutoCommit && !session.IsCommitedOrRollbacked {
- session.saveLastSQL("COMMIT")
- 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
-}
-
func cleanupProcessorsClosures(slices *[]func(interface{})) {
if len(*slices) > 0 {
*slices = make([]func(interface{}), 0)
@@ -506,184 +432,12 @@ func (session *Session) scanMapIntoStruct(obj interface{}, objMap map[string][]b
return nil
}
-// Execute sql
-func (session *Session) innerExec(sqlStr string, args ...interface{}) (sql.Result, error) {
- if session.prepareStmt {
- stmt, err := session.doPrepare(sqlStr)
- if err != nil {
- return nil, err
- }
-
- res, err := stmt.Exec(args...)
- if err != nil {
- return nil, err
- }
- return res, nil
- }
-
- return session.DB().Exec(sqlStr, args...)
-}
-
-func (session *Session) exec(sqlStr string, args ...interface{}) (sql.Result, error) {
- for _, filter := range session.Engine.dialect.Filters() {
- // TODO: for table name, it's no need to RefTable
- sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
- }
-
- session.saveLastSQL(sqlStr, args...)
-
- return session.Engine.logSQLExecutionTime(sqlStr, args, func() (sql.Result, error) {
- if session.IsAutoCommit {
- // FIXME: oci8 can not auto commit (github.com/mattn/go-oci8)
- if session.Engine.dialect.DBType() == core.ORACLE {
- session.Begin()
- r, err := session.Tx.Exec(sqlStr, args...)
- session.Commit()
- return r, err
- }
- return session.innerExec(sqlStr, args...)
- }
- return session.Tx.Exec(sqlStr, args...)
- })
-}
-
-// Exec raw sql
-func (session *Session) Exec(sqlStr string, args ...interface{}) (sql.Result, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- return session.exec(sqlStr, args...)
-}
-
-// CreateTable create a table according a bean
-func (session *Session) CreateTable(bean interface{}) error {
- v := rValue(bean)
- session.Statement.setRefValue(v)
-
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- return session.createOneTable()
-}
-
-// CreateIndexes create indexes
-func (session *Session) CreateIndexes(bean interface{}) error {
- v := rValue(bean)
- session.Statement.setRefValue(v)
-
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- sqls := session.Statement.genIndexSQL()
- for _, sqlStr := range sqls {
- _, err := session.exec(sqlStr)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// CreateUniques create uniques
-func (session *Session) CreateUniques(bean interface{}) error {
- v := rValue(bean)
- session.Statement.setRefValue(v)
-
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- sqls := session.Statement.genUniqueSQL()
- for _, sqlStr := range sqls {
- _, err := session.exec(sqlStr)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-func (session *Session) createOneTable() error {
- sqlStr := session.Statement.genCreateTableSQL()
- _, err := session.exec(sqlStr)
- return err
-}
-
-// to be deleted
-func (session *Session) createAll() error {
- if session.IsAutoClose {
- defer session.Close()
- }
-
- for _, table := range session.Engine.Tables {
- session.Statement.RefTable = table
- session.Statement.tableName = table.Name
- err := session.createOneTable()
- session.resetStatement()
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// DropIndexes drop indexes
-func (session *Session) DropIndexes(bean interface{}) error {
- v := rValue(bean)
- session.Statement.setRefValue(v)
-
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- sqls := session.Statement.genDelIndexSQL()
- for _, sqlStr := range sqls {
- _, err := session.exec(sqlStr)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-// DropTable drop table will drop table if exist, if drop failed, it will return error
-func (session *Session) DropTable(beanOrTableName interface{}) error {
- tableName, err := session.Engine.tableName(beanOrTableName)
- if err != nil {
- return err
- }
-
- var needDrop = true
- if !session.Engine.dialect.SupportDropIfExists() {
- sqlStr, args := session.Engine.dialect.TableCheckSql(tableName)
- results, err := session.query(sqlStr, args...)
- if err != nil {
- return err
- }
- needDrop = len(results) > 0
- }
-
- if needDrop {
- sqlStr := session.Engine.Dialect().DropTableSql(tableName)
- _, err = session.exec(sqlStr)
- return err
- }
- return nil
-}
-
func (session *Session) canCache() bool {
if session.Statement.RefTable == nil ||
session.Statement.JoinStr != "" ||
session.Statement.RawSQL != "" ||
!session.Statement.UseCache ||
+ session.Statement.IsForUpdate ||
session.Tx != nil ||
len(session.Statement.selectStr) > 0 {
return false
@@ -691,332 +445,6 @@ func (session *Session) canCache() bool {
return true
}
-func (session *Session) cacheGet(bean interface{}, sqlStr string, args ...interface{}) (has bool, err error) {
- // if has no reftable, then don't use cache currently
- if !session.canCache() {
- return false, ErrCacheFailed
- }
-
- for _, filter := range session.Engine.dialect.Filters() {
- sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
- }
- newsql := session.Statement.convertIDSQL(sqlStr)
- if newsql == "" {
- return false, ErrCacheFailed
- }
-
- cacher := session.Engine.getCacher2(session.Statement.RefTable)
- tableName := session.Statement.TableName()
- session.Engine.logger.Debug("[cacheGet] find sql:", newsql, args)
- ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
- table := session.Statement.RefTable
- if err != nil {
- var res = make([]string, len(table.PrimaryKeys))
- rows, err := session.DB().Query(newsql, args...)
- if err != nil {
- return false, err
- }
- defer rows.Close()
-
- if rows.Next() {
- err = rows.ScanSlice(&res)
- if err != nil {
- return false, err
- }
- } else {
- return false, ErrCacheFailed
- }
-
- 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}
- session.Engine.logger.Debug("[cacheGet] cache ids:", newsql, ids)
- err = core.PutCacheSql(cacher, ids, tableName, newsql, args)
- if err != nil {
- return false, err
- }
- } else {
- session.Engine.logger.Debug("[cacheGet] cache hit sql:", newsql)
- }
-
- if len(ids) > 0 {
- structValue := reflect.Indirect(reflect.ValueOf(bean))
- id := ids[0]
- session.Engine.logger.Debug("[cacheGet] get bean:", tableName, id)
- sid, err := id.ToString()
- if err != nil {
- return false, err
- }
- cacheBean := cacher.GetBean(tableName, sid)
- if cacheBean == nil {
- /*newSession := session.Engine.NewSession()
- defer newSession.Close()
- cacheBean = reflect.New(structValue.Type()).Interface()
- newSession.Id(id).NoCache()
- if session.Statement.AltTableName != "" {
- newSession.Table(session.Statement.AltTableName)
- }
- if !session.Statement.UseCascade {
- newSession.NoCascade()
- }
- has, err = newSession.Get(cacheBean)
- */
- cacheBean = bean
- has, err = session.nocacheGet(cacheBean, sqlStr, args...)
- if err != nil || !has {
- return has, err
- }
-
- session.Engine.logger.Debug("[cacheGet] cache bean:", tableName, id, cacheBean)
- cacher.PutBean(tableName, sid, cacheBean)
- } else {
- session.Engine.logger.Debug("[cacheGet] cache hit bean:", tableName, id, cacheBean)
- has = true
- }
- structValue.Set(reflect.Indirect(reflect.ValueOf(cacheBean)))
-
- return has, nil
- }
- return false, nil
-}
-
-func (session *Session) cacheFind(t reflect.Type, sqlStr string, rowsSlicePtr interface{}, args ...interface{}) (err error) {
- if !session.canCache() ||
- indexNoCase(sqlStr, "having") != -1 ||
- indexNoCase(sqlStr, "group by") != -1 {
- return ErrCacheFailed
- }
-
- for _, filter := range session.Engine.dialect.Filters() {
- sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
- }
-
- newsql := session.Statement.convertIDSQL(sqlStr)
- if newsql == "" {
- return ErrCacheFailed
- }
-
- tableName := session.Statement.TableName()
-
- table := session.Statement.RefTable
- cacher := session.Engine.getCacher2(table)
- ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
- if err != nil {
- rows, err := session.DB().Query(newsql, args...)
- if err != nil {
- return err
- }
- defer rows.Close()
-
- var i int
- ids = make([]core.PK, 0)
- for rows.Next() {
- i++
- if i > 500 {
- session.Engine.logger.Debug("[cacheFind] ids length > 500, no cache")
- return ErrCacheFailed
- }
- var res = make([]string, len(table.PrimaryKeys))
- err = rows.ScanSlice(&res)
- if err != nil {
- return err
- }
-
- 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)
- }
-
- session.Engine.logger.Debug("[cacheFind] cache sql:", ids, tableName, newsql, args)
- err = core.PutCacheSql(cacher, ids, tableName, newsql, args)
- if err != nil {
- return err
- }
- } else {
- session.Engine.logger.Debug("[cacheFind] cache hit sql:", newsql, args)
- }
-
- sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
-
- ididxes := make(map[string]int)
- var ides []core.PK
- var temps = make([]interface{}, len(ids))
-
- for idx, id := range ids {
- sid, err := id.ToString()
- if err != nil {
- return err
- }
- bean := cacher.GetBean(tableName, sid)
- if bean == nil {
- ides = append(ides, id)
- ididxes[sid] = idx
- } else {
- session.Engine.logger.Debug("[cacheFind] cache hit bean:", tableName, id, bean)
-
- pk := session.Engine.IdOf(bean)
- xid, err := pk.ToString()
- if err != nil {
- return err
- }
-
- if sid != xid {
- session.Engine.logger.Error("[cacheFind] error cache", xid, sid, bean)
- 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()
-
- if len(table.PrimaryKeys) == 1 {
- ff := make([]interface{}, 0, len(ides))
- for _, ie := range ides {
- ff = append(ff, ie[0])
- }
-
- newSession.In("`"+table.PrimaryKeys[0]+"`", ff...)
- } else {
- for _, ie := range ides {
- cond := builder.NewCond()
- for i, name := range table.PrimaryKeys {
- cond = cond.And(builder.Eq{"`" + name + "`": ie[i]})
- }
- newSession.Or(cond)
- }
- }
-
- err = newSession.NoCache().Find(beans)
- 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()
- }
- id := session.Engine.IdOfV(rv)
- sid, err := id.ToString()
- if err != nil {
- return err
- }
-
- bean := rv.Interface()
- temps[ididxes[sid]] = bean
- session.Engine.logger.Debug("[cacheFind] cache bean:", tableName, id, bean, temps)
- cacher.PutBean(tableName, sid, bean)
- }
- }
-
- for j := 0; j < len(temps); j++ {
- bean := temps[j]
- if bean == nil {
- session.Engine.logger.Warn("[cacheFind] cache no hit:", tableName, ids[j], temps)
- // return errors.New("cache error") // !nashtsai! no need to return error, but continue instead
- continue
- }
- 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 {
- var key = ids[j]
- keyType := sliceValue.Type().Key()
- var ikey interface{}
- if len(key) == 1 {
- ikey, err = str2PK(fmt.Sprintf("%v", key[0]), keyType)
- if err != nil {
- return err
- }
- } else {
- if keyType.Kind() != reflect.Slice {
- return errors.New("table have multiple primary keys, key is not core.PK or slice")
- }
- ikey = key
- }
-
- if t.Kind() == reflect.Ptr {
- sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.ValueOf(bean))
- } else {
- sliceValue.SetMapIndex(reflect.ValueOf(ikey), reflect.Indirect(reflect.ValueOf(bean)))
- }
- }
- }
-
- return nil
-}
-
-// IterFunc only use by Iterate
-type IterFunc func(idx int, bean interface{}) error
-
-// Rows return sql.Rows compatible Rows obj, as a forward Iterator object for iterating record by record, bean's non-empty fields
-// are conditions.
-func (session *Session) Rows(bean interface{}) (*Rows, error) {
- return newRows(session, bean)
-}
-
-// 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
-func (session *Session) Iterate(bean interface{}, fun IterFunc) error {
- rows, err := session.Rows(bean)
- if err != nil {
- return err
- }
- defer rows.Close()
-
- i := 0
- for rows.Next() {
- b := reflect.New(rows.beanType).Interface()
- err = rows.Scan(b)
- if err != nil {
- return err
- }
- err = fun(i, b)
- if err != nil {
- return err
- }
- i++
- }
- return err
-}
-
func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) {
crc := crc32.ChecksumIEEE([]byte(sqlStr))
// TODO try hash(sqlStr+len(sqlStr))
@@ -1032,580 +460,6 @@ func (session *Session) doPrepare(sqlStr string) (stmt *core.Stmt, err error) {
return
}
-func (session *Session) nocacheGet(bean interface{}, sqlStr string, args ...interface{}) (bool, error) {
- var rawRows *core.Rows
- var err error
- session.queryPreprocess(&sqlStr, args...)
- if session.IsAutoCommit {
- _, rawRows, err = session.innerQuery(sqlStr, args...)
- } else {
- rawRows, err = session.Tx.Query(sqlStr, args...)
- }
- if err != nil {
- return false, err
- }
-
- defer rawRows.Close()
-
- if rawRows.Next() {
- if fields, err := rawRows.Columns(); err == nil {
- err = session.row2Bean(rawRows, fields, len(fields), bean)
- }
- return true, err
- }
- return false, nil
-}
-
-// Get retrieve one record from database, bean's non-empty fields
-// will be as conditions
-func (session *Session) Get(bean interface{}) (bool, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- session.Statement.setRefValue(rValue(bean))
-
- var sqlStr string
- var args []interface{}
-
- if session.Statement.RawSQL == "" {
- if len(session.Statement.TableName()) <= 0 {
- return false, ErrTableNotFound
- }
- session.Statement.Limit(1)
- sqlStr, args = session.Statement.genGetSQL(bean)
- } else {
- sqlStr = session.Statement.RawSQL
- args = session.Statement.RawParams
- }
-
- if session.canCache() {
- if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil &&
- !session.Statement.unscoped {
- has, err := session.cacheGet(bean, sqlStr, args...)
- if err != ErrCacheFailed {
- return has, err
- }
- }
- }
-
- return session.nocacheGet(bean, sqlStr, args...)
-}
-
-// Count counts the records. bean's non-empty fields
-// are conditions.
-func (session *Session) Count(bean interface{}) (int64, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- var sqlStr string
- var args []interface{}
- if session.Statement.RawSQL == "" {
- sqlStr, args = session.Statement.genCountSQL(bean)
- } else {
- sqlStr = session.Statement.RawSQL
- args = session.Statement.RawParams
- }
-
- session.queryPreprocess(&sqlStr, args...)
-
- var err error
- var total int64
- if session.IsAutoCommit {
- err = session.DB().QueryRow(sqlStr, args...).Scan(&total)
- } else {
- err = session.Tx.QueryRow(sqlStr, args...).Scan(&total)
- }
-
- if err == sql.ErrNoRows || err == nil {
- return total, nil
- }
-
- return 0, err
-}
-
-// Sum call sum some column. bean's non-empty fields are conditions.
-func (session *Session) Sum(bean interface{}, columnName string) (float64, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- var sqlStr string
- var args []interface{}
- if len(session.Statement.RawSQL) == 0 {
- sqlStr, args = session.Statement.genSumSQL(bean, columnName)
- } else {
- sqlStr = session.Statement.RawSQL
- args = session.Statement.RawParams
- }
-
- session.queryPreprocess(&sqlStr, args...)
-
- var err error
- var res float64
- if session.IsAutoCommit {
- err = session.DB().QueryRow(sqlStr, args...).Scan(&res)
- } else {
- err = session.Tx.QueryRow(sqlStr, args...).Scan(&res)
- }
-
- if err == sql.ErrNoRows || err == nil {
- return res, nil
- }
- return 0, err
-}
-
-// Sums call sum some columns. bean's non-empty fields are conditions.
-func (session *Session) Sums(bean interface{}, columnNames ...string) ([]float64, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- var sqlStr string
- var args []interface{}
- if len(session.Statement.RawSQL) == 0 {
- sqlStr, args = session.Statement.genSumSQL(bean, columnNames...)
- } else {
- sqlStr = session.Statement.RawSQL
- args = session.Statement.RawParams
- }
-
- session.queryPreprocess(&sqlStr, args...)
-
- var err error
- var res = make([]float64, len(columnNames), len(columnNames))
- if session.IsAutoCommit {
- err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res)
- } else {
- err = session.Tx.QueryRow(sqlStr, args...).ScanSlice(&res)
- }
-
- if err == sql.ErrNoRows || err == nil {
- return res, nil
- }
- return nil, err
-}
-
-// SumsInt sum specify columns and return as []int64 instead of []float64
-func (session *Session) SumsInt(bean interface{}, columnNames ...string) ([]int64, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- var sqlStr string
- var args []interface{}
- if len(session.Statement.RawSQL) == 0 {
- sqlStr, args = session.Statement.genSumSQL(bean, columnNames...)
- } else {
- sqlStr = session.Statement.RawSQL
- args = session.Statement.RawParams
- }
-
- session.queryPreprocess(&sqlStr, args...)
-
- var err error
- var res = make([]int64, 0, len(columnNames))
- if session.IsAutoCommit {
- err = session.DB().QueryRow(sqlStr, args...).ScanSlice(&res)
- } else {
- err = session.Tx.QueryRow(sqlStr, args...).ScanSlice(&res)
- }
-
- if err == sql.ErrNoRows || err == nil {
- return res, nil
- }
- return nil, err
-}
-
-func (session *Session) noCacheFind(sliceValue reflect.Value, sqlStr string, args ...interface{}) error {
- var rawRows *core.Rows
- var err error
-
- session.queryPreprocess(&sqlStr, args...)
- if session.IsAutoCommit {
- _, rawRows, err = session.innerQuery(sqlStr, args...)
- } else {
- rawRows, err = session.Tx.Query(sqlStr, args...)
- }
- if err != nil {
- return err
- }
- defer rawRows.Close()
-
- fields, err := rawRows.Columns()
- if err != nil {
- return err
- }
-
- var newElemFunc func() reflect.Value
- sliceElementType := sliceValue.Type().Elem()
- if sliceElementType.Kind() == reflect.Ptr {
- newElemFunc = func() reflect.Value {
- return reflect.New(sliceElementType.Elem())
- }
- } else {
- newElemFunc = func() reflect.Value {
- return reflect.New(sliceElementType)
- }
- }
-
- var sliceValueSetFunc func(*reflect.Value)
-
- if sliceValue.Kind() == reflect.Slice {
- if sliceElementType.Kind() == reflect.Ptr {
- sliceValueSetFunc = func(newValue *reflect.Value) {
- sliceValue.Set(reflect.Append(sliceValue, reflect.ValueOf(newValue.Interface())))
- }
- } else {
- sliceValueSetFunc = func(newValue *reflect.Value) {
- sliceValue.Set(reflect.Append(sliceValue, reflect.Indirect(reflect.ValueOf(newValue.Interface()))))
- }
- }
- }
-
- var newValue = newElemFunc()
- dataStruct := rValue(newValue.Interface())
- if dataStruct.Kind() != reflect.Struct {
- return errors.New("Expected a pointer to a struct")
- }
-
- return session.rows2Beans(rawRows, fields, len(fields), session.Engine.autoMapType(dataStruct), newElemFunc, sliceValueSetFunc)
-}
-
-// Find retrieve records from table, condiBeans's non-empty fields
-// are conditions. beans could be []Struct, []*Struct, map[int64]Struct
-// map[int64]*Struct
-func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) error {
- defer session.resetStatement()
- 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()
-
- if session.Statement.RefTable == nil {
- if sliceElementType.Kind() == reflect.Ptr {
- if sliceElementType.Elem().Kind() == reflect.Struct {
- pv := reflect.New(sliceElementType.Elem())
- session.Statement.setRefValue(pv.Elem())
- } else {
- return errors.New("slice type")
- }
- } else if sliceElementType.Kind() == reflect.Struct {
- pv := reflect.New(sliceElementType)
- session.Statement.setRefValue(pv.Elem())
- } else {
- return errors.New("slice type")
- }
- }
-
- var table = session.Statement.RefTable
-
- var addedTableName = (len(session.Statement.JoinStr) > 0)
- var autoCond builder.Cond
- if !session.Statement.noAutoCondition && len(condiBean) > 0 {
- var err error
- autoCond, err = session.Statement.buildConds(table, condiBean[0], true, true, false, true, addedTableName)
- if err != nil {
- panic(err)
- }
- } else {
- // !oinume! Add "<col> IS NULL" to WHERE whatever condiBean is given.
- // See https://github.com/go-xorm/xorm/issues/179
- if col := table.DeletedColumn(); col != nil && !session.Statement.unscoped { // tag "deleted" is enabled
- var colName = session.Engine.Quote(col.Name)
- if addedTableName {
- var nm = session.Statement.TableName()
- if len(session.Statement.TableAlias) > 0 {
- nm = session.Statement.TableAlias
- }
- colName = session.Engine.Quote(nm) + "." + colName
- }
- autoCond = builder.IsNull{colName}.Or(builder.Eq{colName: "0001-01-01 00:00:00"})
- }
- }
-
- var sqlStr string
- var args []interface{}
- if session.Statement.RawSQL == "" {
- if len(session.Statement.TableName()) <= 0 {
- return ErrTableNotFound
- }
-
- var columnStr = session.Statement.ColumnStr
- if len(session.Statement.selectStr) > 0 {
- columnStr = session.Statement.selectStr
- } else {
- if session.Statement.JoinStr == "" {
- if columnStr == "" {
- if session.Statement.GroupByStr != "" {
- columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
- } else {
- columnStr = session.Statement.genColumnStr()
- }
- }
- } else {
- if columnStr == "" {
- if session.Statement.GroupByStr != "" {
- columnStr = session.Statement.Engine.Quote(strings.Replace(session.Statement.GroupByStr, ",", session.Engine.Quote(","), -1))
- } else {
- columnStr = "*"
- }
- }
- }
- }
-
- condSQL, condArgs, _ := builder.ToSQL(session.Statement.cond.And(autoCond))
-
- args = append(session.Statement.joinArgs, condArgs...)
- sqlStr = session.Statement.genSelectSQL(columnStr, condSQL)
- // for mssql and use limit
- qs := strings.Count(sqlStr, "?")
- if len(args)*2 == qs {
- args = append(args, args...)
- }
- } else {
- sqlStr = session.Statement.RawSQL
- args = session.Statement.RawParams
- }
-
- var err error
- if session.canCache() {
- if cacher := session.Engine.getCacher2(table); cacher != nil &&
- !session.Statement.IsDistinct &&
- !session.Statement.unscoped {
- err = session.cacheFind(sliceElementType, sqlStr, rowsSlicePtr, args...)
- if err != ErrCacheFailed {
- return err
- }
- err = nil // !nashtsai! reset err to nil for ErrCacheFailed
- session.Engine.logger.Warn("Cache Find Failed")
- }
- }
-
- if sliceValue.Kind() != reflect.Map {
- return session.noCacheFind(sliceValue, sqlStr, args...)
- }
-
- resultsSlice, err := session.query(sqlStr, args...)
- if err != nil {
- return err
- }
-
- keyType := sliceValue.Type().Key()
-
- for _, 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
- }
- var key interface{}
- // if there is only one pk, we can put the id as map key.
- if len(table.PrimaryKeys) == 1 {
- key, err = str2PK(string(results[table.PrimaryKeys[0]]), keyType)
- if err != nil {
- return err
- }
- } else {
- if keyType.Kind() != reflect.Slice {
- panic("don't support multiple primary key's map has non-slice key type")
- } else {
- var keys core.PK = make([]interface{}, 0, len(table.PrimaryKeys))
- for _, pk := range table.PrimaryKeys {
- skey, err := str2PK(string(results[pk]), keyType)
- if err != nil {
- return err
- }
- keys = append(keys, skey)
- }
- key = keys
- }
- }
-
- 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
-}
-
-// Ping test if database is ok
-func (session *Session) Ping() error {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- return session.DB().Ping()
-}
-
-// IsTableExist if a table is exist
-func (session *Session) IsTableExist(beanOrTableName interface{}) (bool, error) {
- tableName, err := session.Engine.tableName(beanOrTableName)
- if err != nil {
- return false, err
- }
-
- return session.isTableExist(tableName)
-}
-
-func (session *Session) isTableExist(tableName string) (bool, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
- sqlStr, args := session.Engine.dialect.TableCheckSql(tableName)
- results, err := session.query(sqlStr, args...)
- return len(results) > 0, err
-}
-
-// IsTableEmpty if table have any records
-func (session *Session) IsTableEmpty(bean interface{}) (bool, error) {
- v := rValue(bean)
- t := v.Type()
-
- if t.Kind() == reflect.String {
- return session.isTableEmpty(bean.(string))
- } else if t.Kind() == reflect.Struct {
- rows, err := session.Count(bean)
- return rows == 0, err
- }
- return false, errors.New("bean should be a struct or struct's point")
-}
-
-func (session *Session) isTableEmpty(tableName string) (bool, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- var total int64
- sqlStr := fmt.Sprintf("select count(*) from %s", session.Engine.Quote(tableName))
- err := session.DB().QueryRow(sqlStr).Scan(&total)
- session.saveLastSQL(sqlStr)
- if err != nil {
- if err == sql.ErrNoRows {
- err = nil
- }
- return true, err
- }
-
- return total == 0, nil
-}
-
-func (session *Session) isIndexExist(tableName, idxName string, unique bool) (bool, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
- var idx string
- if unique {
- idx = uniqueName(tableName, idxName)
- } else {
- idx = indexName(tableName, idxName)
- }
- sqlStr, args := session.Engine.dialect.IndexCheckSql(tableName, idx)
- results, err := session.query(sqlStr, args...)
- return len(results) > 0, err
-}
-
-// find if index is exist according cols
-func (session *Session) isIndexExist2(tableName string, cols []string, unique bool) (bool, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- indexes, err := session.Engine.dialect.GetIndexes(tableName)
- if err != nil {
- return false, err
- }
-
- for _, index := range indexes {
- if sliceEq(index.Cols, cols) {
- if unique {
- return index.Type == core.UniqueType, nil
- }
- return index.Type == core.IndexType, nil
- }
- }
- return false, nil
-}
-
-func (session *Session) addColumn(colName string) error {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- col := session.Statement.RefTable.GetColumn(colName)
- sql, args := session.Statement.genAddColumnStr(col)
- _, err := session.exec(sql, args...)
- return err
-}
-
-func (session *Session) addIndex(tableName, idxName string) error {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
- index := session.Statement.RefTable.Indexes[idxName]
- sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index)
-
- _, err := session.exec(sqlStr)
- return err
-}
-
-func (session *Session) addUnique(tableName, uqeName string) error {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
- index := session.Statement.RefTable.Indexes[uqeName]
- sqlStr := session.Engine.dialect.CreateIndexSql(tableName, index)
- _, err := session.exec(sqlStr)
- return err
-}
-
-// To be deleted
-func (session *Session) dropAll() error {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- for _, table := range session.Engine.Tables {
- session.Statement.Init()
- session.Statement.RefTable = table
- sqlStr := session.Engine.Dialect().DropTableSql(session.Statement.TableName())
- _, err := session.exec(sqlStr)
- if err != nil {
- return err
- }
- }
- return nil
-}
-
func (session *Session) getField(dataStruct *reflect.Value, key string, table *core.Table, idx int) *reflect.Value {
var col *core.Column
if col = table.GetColumnIdx(key, idx); col == nil {
@@ -1839,16 +693,24 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
hasAssigned = true
t := vv.Convert(core.TimeType).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
- dbTZ := session.Engine.DatabaseTZ
- if dbTZ == nil {
+ dbTZ := session.Engine.DatabaseTZ
+ if dbTZ == nil {
+ if session.Engine.dialect.DBType() == core.SQLITE {
+ dbTZ = time.UTC
+ } else {
dbTZ = time.Local
}
+ }
+
+ // set new location if database don't save timezone or give an incorrect timezone
+ if len(z) == 0 || t.Year() == 0 || t.Location().String() != dbTZ.String() { // !nashtsai! HACK tmp work around for lib/pq doesn't properly time with location
session.Engine.logger.Debugf("empty zone key[%v] : %v | zone: %v | location: %+v\n", key, t, z, *t.Location())
t = time.Date(t.Year(), t.Month(), t.Day(), t.Hour(),
t.Minute(), t.Second(), t.Nanosecond(), dbTZ)
}
+
// !nashtsai! convert to engine location
if col.TimeZone == nil {
t = t.In(session.Engine.TZLocation)
@@ -2121,7 +983,6 @@ func (session *Session) _row2Bean(rows *core.Rows, fields []string, fieldsCount
}
}
return nil
-
}
func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{}) {
@@ -2132,357 +993,6 @@ func (session *Session) queryPreprocess(sqlStr *string, paramStr ...interface{})
session.saveLastSQL(*sqlStr, paramStr...)
}
-func (session *Session) query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
-
- session.queryPreprocess(&sqlStr, paramStr...)
-
- if session.IsAutoCommit {
- return session.innerQuery2(sqlStr, paramStr...)
- }
- return session.txQuery(session.Tx, sqlStr, paramStr...)
-}
-
-func (session *Session) txQuery(tx *core.Tx, sqlStr string, params ...interface{}) (resultsSlice []map[string][]byte, err error) {
- rows, err := tx.Query(sqlStr, params...)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
-
- return rows2maps(rows)
-}
-
-func (session *Session) innerQuery(sqlStr string, params ...interface{}) (*core.Stmt, *core.Rows, error) {
- var callback func() (*core.Stmt, *core.Rows, error)
- if session.prepareStmt {
- callback = func() (*core.Stmt, *core.Rows, error) {
- stmt, err := session.doPrepare(sqlStr)
- if err != nil {
- return nil, nil, err
- }
- rows, err := stmt.Query(params...)
- if err != nil {
- return nil, nil, err
- }
- return stmt, rows, nil
- }
- } else {
- callback = func() (*core.Stmt, *core.Rows, error) {
- rows, err := session.DB().Query(sqlStr, params...)
- if err != nil {
- return nil, nil, err
- }
- return nil, rows, err
- }
- }
- stmt, rows, err := session.Engine.logSQLQueryTime(sqlStr, params, callback)
- if err != nil {
- return nil, nil, err
- }
- return stmt, rows, nil
-}
-
-func (session *Session) innerQuery2(sqlStr string, params ...interface{}) ([]map[string][]byte, error) {
- _, rows, err := session.innerQuery(sqlStr, params...)
- if rows != nil {
- defer rows.Close()
- }
- if err != nil {
- return nil, err
- }
- return rows2maps(rows)
-}
-
-// Query a raw sql and return records as []map[string][]byte
-func (session *Session) Query(sqlStr string, paramStr ...interface{}) (resultsSlice []map[string][]byte, err error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- return session.query(sqlStr, paramStr...)
-}
-
-// =============================
-// 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...)
-}
-
-// Insert insert one or more beans
-func (session *Session) Insert(beans ...interface{}) (int64, error) {
- var affected int64
- var err error
-
- if session.IsAutoClose {
- defer session.Close()
- }
-
- for _, bean := range beans {
- sliceValue := reflect.Indirect(reflect.ValueOf(bean))
- if sliceValue.Kind() == reflect.Slice {
- size := sliceValue.Len()
- if size > 0 {
- if session.Engine.SupportInsertMany() {
- cnt, err := session.innerInsertMulti(bean)
- session.resetStatement()
- if err != nil {
- return affected, err
- }
- affected += cnt
- } else {
- for i := 0; i < size; i++ {
- cnt, err := session.innerInsert(sliceValue.Index(i).Interface())
- session.resetStatement()
- if err != nil {
- return affected, err
- }
- affected += cnt
- }
- }
- }
- } else {
- cnt, err := session.innerInsert(bean)
- session.resetStatement()
- if err != nil {
- return affected, err
- }
- affected += cnt
- }
- }
-
- return affected, err
-}
-
-func (session *Session) innerInsertMulti(rowsSlicePtr interface{}) (int64, error) {
- sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
- if sliceValue.Kind() != reflect.Slice {
- return 0, errors.New("needs a pointer to a slice")
- }
-
- if sliceValue.Len() <= 0 {
- return 0, errors.New("could not insert a empty slice")
- }
-
- session.Statement.setRefValue(sliceValue.Index(0))
-
- if len(session.Statement.TableName()) <= 0 {
- return 0, ErrTableNotFound
- }
-
- table := session.Statement.RefTable
- size := sliceValue.Len()
-
- var colNames []string
- var colMultiPlaces []string
- var args []interface{}
- var cols []*core.Column
-
- for i := 0; i < size; i++ {
- v := sliceValue.Index(i)
- vv := reflect.Indirect(v)
- elemValue := v.Interface()
- var colPlaces []string
-
- // handle BeforeInsertProcessor
- // !nashtsai! does user expect it's same slice to passed closure when using Before()/After() when insert multi??
- for _, closure := range session.beforeClosures {
- closure(elemValue)
- }
-
- if processor, ok := interface{}(elemValue).(BeforeInsertProcessor); ok {
- processor.BeforeInsert()
- }
- // --
-
- if i == 0 {
- for _, col := range table.Columns() {
- ptrFieldValue, err := col.ValueOfV(&vv)
- if err != nil {
- return 0, err
- }
- fieldValue := *ptrFieldValue
- if col.IsAutoIncrement && isZero(fieldValue.Interface()) {
- continue
- }
- if col.MapType == core.ONLYFROMDB {
- continue
- }
- if col.IsDeleted {
- continue
- }
- if session.Statement.ColumnStr != "" {
- if _, ok := session.Statement.columnMap[strings.ToLower(col.Name)]; !ok {
- continue
- }
- }
- if session.Statement.OmitStr != "" {
- if _, ok := session.Statement.columnMap[strings.ToLower(col.Name)]; ok {
- continue
- }
- }
- if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime {
- val, t := session.Engine.NowTime2(col.SQLType.Name)
- args = append(args, val)
-
- var colName = col.Name
- session.afterClosures = append(session.afterClosures, func(bean interface{}) {
- col := table.GetColumn(colName)
- setColumnTime(bean, col, t)
- })
- } else if col.IsVersion && session.Statement.checkVersion {
- args = append(args, 1)
- var colName = col.Name
- session.afterClosures = append(session.afterClosures, func(bean interface{}) {
- col := table.GetColumn(colName)
- setColumnInt(bean, col, 1)
- })
- } else {
- arg, err := session.value2Interface(col, fieldValue)
- if err != nil {
- return 0, err
- }
- args = append(args, arg)
- }
-
- colNames = append(colNames, col.Name)
- cols = append(cols, col)
- colPlaces = append(colPlaces, "?")
- }
- } else {
- for _, col := range cols {
- ptrFieldValue, err := col.ValueOfV(&vv)
- if err != nil {
- return 0, err
- }
- fieldValue := *ptrFieldValue
-
- if col.IsAutoIncrement && isZero(fieldValue.Interface()) {
- continue
- }
- if col.MapType == core.ONLYFROMDB {
- continue
- }
- if col.IsDeleted {
- continue
- }
- if session.Statement.ColumnStr != "" {
- if _, ok := session.Statement.columnMap[strings.ToLower(col.Name)]; !ok {
- continue
- }
- }
- if session.Statement.OmitStr != "" {
- if _, ok := session.Statement.columnMap[strings.ToLower(col.Name)]; ok {
- continue
- }
- }
- if (col.IsCreated || col.IsUpdated) && session.Statement.UseAutoTime {
- val, t := session.Engine.NowTime2(col.SQLType.Name)
- args = append(args, val)
-
- var colName = col.Name
- session.afterClosures = append(session.afterClosures, func(bean interface{}) {
- col := table.GetColumn(colName)
- setColumnTime(bean, col, t)
- })
- } else if col.IsVersion && session.Statement.checkVersion {
- args = append(args, 1)
- var colName = col.Name
- session.afterClosures = append(session.afterClosures, func(bean interface{}) {
- col := table.GetColumn(colName)
- setColumnInt(bean, col, 1)
- })
- } else {
- arg, err := session.value2Interface(col, fieldValue)
- if err != nil {
- return 0, err
- }
- args = append(args, arg)
- }
-
- colPlaces = append(colPlaces, "?")
- }
- }
- colMultiPlaces = append(colMultiPlaces, strings.Join(colPlaces, ", "))
- }
- cleanupProcessorsClosures(&session.beforeClosures)
-
- statement := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
- session.Engine.Quote(session.Statement.TableName()),
- 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
- }
-
- if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
- session.cacheInsert(session.Statement.TableName())
- }
-
- lenAfterClosures := len(session.afterClosures)
- for i := 0; i < size; i++ {
- elemValue := reflect.Indirect(sliceValue.Index(i)).Addr().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()
-}
-
-// InsertMulti insert multiple records
-func (session *Session) InsertMulti(rowsSlicePtr interface{}) (int64, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- sliceValue := reflect.Indirect(reflect.ValueOf(rowsSlicePtr))
- if sliceValue.Kind() != reflect.Slice {
- return 0, ErrParamsType
-
- }
-
- if sliceValue.Len() <= 0 {
- return 0, nil
- }
-
- return session.innerInsertMulti(rowsSlicePtr)
-}
-
func (session *Session) str2Time(col *core.Column, data string) (outTime time.Time, outErr error) {
sdata := strings.TrimSpace(data)
var x time.Time
@@ -3011,6 +1521,9 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
if err != nil {
return 0, err
}
+ if col.SQLType.IsBlob() {
+ return data, nil
+ }
return string(data), nil
}
}
@@ -3020,6 +1533,9 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
if err != nil {
return 0, err
}
+ if col.SQLType.IsBlob() {
+ return data, nil
+ }
return string(data), nil
}
@@ -3128,806 +1644,6 @@ func (session *Session) value2Interface(col *core.Column, fieldValue reflect.Val
}
}
-func (session *Session) innerInsert(bean interface{}) (int64, error) {
- session.Statement.setRefValue(rValue(bean))
- if len(session.Statement.TableName()) <= 0 {
- return 0, ErrTableNotFound
- }
-
- table := session.Statement.RefTable
-
- // handle BeforeInsertProcessor
- for _, closure := range session.beforeClosures {
- closure(bean)
- }
- cleanupProcessorsClosures(&session.beforeClosures) // cleanup after used
-
- if processor, ok := interface{}(bean).(BeforeInsertProcessor); ok {
- processor.BeforeInsert()
- }
- // --
- colNames, args, err := genCols(session.Statement.RefTable, session, bean, false, false)
- if err != nil {
- return 0, err
- }
- // insert expr columns, override if exists
- exprColumns := session.Statement.getExpr()
- exprColVals := make([]string, 0, len(exprColumns))
- for _, v := range exprColumns {
- // remove the expr columns
- for i, colName := range colNames {
- if colName == v.colName {
- colNames = append(colNames[:i], colNames[i+1:]...)
- args = append(args[:i], args[i+1:]...)
- }
- }
-
- // append expr column to the end
- colNames = append(colNames, v.colName)
- exprColVals = append(exprColVals, v.expr)
- }
-
- colPlaces := strings.Repeat("?, ", len(colNames)-len(exprColumns))
- if len(exprColVals) > 0 {
- colPlaces = colPlaces + strings.Join(exprColVals, ", ")
- } else {
- colPlaces = colPlaces[0 : len(colPlaces)-2]
- }
-
- sqlStr := fmt.Sprintf("INSERT INTO %s (%v%v%v) VALUES (%v)",
- session.Engine.Quote(session.Statement.TableName()),
- 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.
- if session.Engine.dialect.DBType() == core.ORACLE && len(table.AutoIncrement) > 0 {
- //assert table.AutoIncrement != ""
- res, err := session.query("select seq_atable.currval from dual", args...)
- if err != nil {
- return 0, err
- }
-
- handleAfterInsertProcessorFunc(bean)
-
- if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
- session.cacheInsert(session.Statement.TableName())
- }
-
- if table.Version != "" && session.Statement.checkVersion {
- verValue, err := table.VersionColumn().ValueOf(bean)
- if err != nil {
- session.Engine.logger.Error(err)
- } else if verValue.IsValid() && verValue.CanSet() {
- verValue.SetInt(1)
- }
- }
-
- if len(res) < 1 {
- return 0, errors.New("insert no error but not returned id")
- }
-
- idByte := res[0][table.AutoIncrement]
- id, err := strconv.ParseInt(string(idByte), 10, 64)
- if err != nil || id <= 0 {
- return 1, err
- }
-
- aiValue, err := table.AutoIncrColumn().ValueOf(bean)
- if err != nil {
- session.Engine.logger.Error(err)
- }
-
- if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
- return 1, nil
- }
-
- aiValue.Set(int64ToIntValue(id, aiValue.Type()))
-
- return 1, nil
- } else if session.Engine.dialect.DBType() == core.POSTGRES && len(table.AutoIncrement) > 0 {
- //assert table.AutoIncrement != ""
- sqlStr = sqlStr + " RETURNING " + session.Engine.Quote(table.AutoIncrement)
- res, err := session.query(sqlStr, args...)
-
- if err != nil {
- return 0, err
- }
- handleAfterInsertProcessorFunc(bean)
-
- if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
- session.cacheInsert(session.Statement.TableName())
- }
-
- if table.Version != "" && session.Statement.checkVersion {
- verValue, err := table.VersionColumn().ValueOf(bean)
- if err != nil {
- session.Engine.logger.Error(err)
- } else if verValue.IsValid() && verValue.CanSet() {
- verValue.SetInt(1)
- }
- }
-
- if len(res) < 1 {
- return 0, errors.New("insert no error but not returned id")
- }
-
- idByte := res[0][table.AutoIncrement]
- id, err := strconv.ParseInt(string(idByte), 10, 64)
- if err != nil || id <= 0 {
- return 1, err
- }
-
- aiValue, err := table.AutoIncrColumn().ValueOf(bean)
- if err != nil {
- session.Engine.logger.Error(err)
- }
-
- if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
- return 1, nil
- }
-
- aiValue.Set(int64ToIntValue(id, aiValue.Type()))
-
- return 1, nil
- } else {
- res, err := session.exec(sqlStr, args...)
- if err != nil {
- return 0, err
- }
-
- defer handleAfterInsertProcessorFunc(bean)
-
- if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
- session.cacheInsert(session.Statement.TableName())
- }
-
- if table.Version != "" && session.Statement.checkVersion {
- verValue, err := table.VersionColumn().ValueOf(bean)
- if err != nil {
- session.Engine.logger.Error(err)
- } else if verValue.IsValid() && verValue.CanSet() {
- verValue.SetInt(1)
- }
- }
-
- if table.AutoIncrement == "" {
- return res.RowsAffected()
- }
-
- var id int64
- id, err = res.LastInsertId()
- if err != nil || id <= 0 {
- return res.RowsAffected()
- }
-
- aiValue, err := table.AutoIncrColumn().ValueOf(bean)
- if err != nil {
- session.Engine.logger.Error(err)
- }
-
- if aiValue == nil || !aiValue.IsValid() || !aiValue.CanSet() {
- return res.RowsAffected()
- }
-
- aiValue.Set(int64ToIntValue(id, aiValue.Type()))
-
- return res.RowsAffected()
- }
-}
-
-// InsertOne insert only one struct into database as a record.
-// The in parameter bean must a struct or a point to struct. The return
-// parameter is inserted and error
-func (session *Session) InsertOne(bean interface{}) (int64, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- return session.innerInsert(bean)
-}
-
-func (session *Session) cacheInsert(tables ...string) error {
- if session.Statement.RefTable == nil {
- return ErrCacheFailed
- }
-
- table := session.Statement.RefTable
- cacher := session.Engine.getCacher2(table)
-
- for _, t := range tables {
- session.Engine.logger.Debug("[cache] clear sql:", t)
- cacher.ClearIds(t)
- }
-
- return nil
-}
-
-func (session *Session) cacheUpdate(sqlStr string, args ...interface{}) error {
- if session.Statement.RefTable == nil ||
- session.Tx != nil {
- return ErrCacheFailed
- }
-
- oldhead, newsql := session.Statement.convertUpdateSQL(sqlStr)
- if newsql == "" {
- return ErrCacheFailed
- }
- for _, filter := range session.Engine.dialect.Filters() {
- newsql = filter.Do(newsql, session.Engine.dialect, session.Statement.RefTable)
- }
- session.Engine.logger.Debug("[cacheUpdate] new sql", oldhead, newsql)
-
- var nStart int
- if len(args) > 0 {
- if strings.Index(sqlStr, "?") > -1 {
- nStart = strings.Count(oldhead, "?")
- } else {
- // only for pq, TODO: if any other databse?
- nStart = strings.Count(oldhead, "$")
- }
- }
- table := session.Statement.RefTable
- cacher := session.Engine.getCacher2(table)
- tableName := session.Statement.TableName()
- session.Engine.logger.Debug("[cacheUpdate] get cache sql", newsql, args[nStart:])
- ids, err := core.GetCacheSql(cacher, tableName, newsql, args[nStart:])
- if err != nil {
- rows, err := session.DB().Query(newsql, args[nStart:]...)
- if err != nil {
- return err
- }
- defer rows.Close()
-
- ids = make([]core.PK, 0)
- for rows.Next() {
- var res = make([]string, len(table.PrimaryKeys))
- err = rows.ScanSlice(&res)
- if err != nil {
- return err
- }
- 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)
- }
- session.Engine.logger.Debug("[cacheUpdate] find updated id", ids)
- } /*else {
- session.Engine.LogDebug("[xorm:cacheUpdate] del cached sql:", tableName, newsql, args)
- cacher.DelIds(tableName, genSqlKey(newsql, args))
- }*/
-
- for _, id := range ids {
- sid, err := id.ToString()
- if err != nil {
- return err
- }
- if bean := cacher.GetBean(tableName, sid); bean != nil {
- sqls := splitNNoCase(sqlStr, "where", 2)
- 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 {
- session.Engine.logger.Debug("[cacheUpdate] cannot find column", tableName, colName)
- return ErrCacheFailed
- }
-
- if col := table.GetColumn(colName); col != nil {
- fieldValue, err := col.ValueOf(bean)
- if err != nil {
- session.Engine.logger.Error(err)
- } else {
- session.Engine.logger.Debug("[cacheUpdate] set bean field", bean, colName, fieldValue.Interface())
- if col.IsVersion && session.Statement.checkVersion {
- fieldValue.SetInt(fieldValue.Int() + 1)
- } else {
- fieldValue.Set(reflect.ValueOf(args[idx]))
- }
- }
- } else {
- session.Engine.logger.Errorf("[cacheUpdate] ERROR: column %v is not table %v's",
- colName, table.Name)
- }
- }
-
- session.Engine.logger.Debug("[cacheUpdate] update cache", tableName, id, bean)
- cacher.PutBean(tableName, sid, bean)
- }
- }
- session.Engine.logger.Debug("[cacheUpdate] clear cached table sql:", tableName)
- cacher.ClearIds(tableName)
- return nil
-}
-
-// Update records, bean's non-empty fields are updated contents,
-// condiBean' non-empty filds are conditions
-// CAUTION:
-// 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
-func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int64, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- v := rValue(bean)
- t := v.Type()
-
- var colNames []string
- var args []interface{}
-
- // 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()
- }
- // --
-
- var err error
- var isMap = t.Kind() == reflect.Map
- var isStruct = t.Kind() == reflect.Struct
- if isStruct {
- session.Statement.setRefValue(v)
-
- if len(session.Statement.TableName()) <= 0 {
- return 0, ErrTableNotFound
- }
-
- if session.Statement.ColumnStr == "" {
- colNames, args = buildUpdates(session.Engine, session.Statement.RefTable, bean, false, false,
- false, false, session.Statement.allUseBool, session.Statement.useAllCols,
- session.Statement.mustColumnMap, session.Statement.nullableMap,
- session.Statement.columnMap, true, session.Statement.unscoped)
- } else {
- colNames, args, err = genCols(session.Statement.RefTable, session, bean, true, true)
- if err != nil {
- return 0, err
- }
- }
- } else if isMap {
- 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
- }
-
- table := session.Statement.RefTable
-
- if session.Statement.UseAutoTime && table != nil && table.Updated != "" {
- colNames = append(colNames, session.Engine.Quote(table.Updated)+" = ?")
- col := table.UpdatedColumn()
- val, t := session.Engine.NowTime2(col.SQLType.Name)
- args = append(args, val)
-
- var colName = col.Name
- if isStruct {
- session.afterClosures = append(session.afterClosures, func(bean interface{}) {
- col := table.GetColumn(colName)
- setColumnTime(bean, col, t)
- })
- }
- }
-
- //for update action to like "column = column + ?"
- incColumns := session.Statement.getInc()
- for _, v := range incColumns {
- colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+session.Engine.Quote(v.colName)+" + ?")
- args = append(args, v.arg)
- }
- //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)
- }
- //for update action to like "column = expression"
- exprColumns := session.Statement.getExpr()
- for _, v := range exprColumns {
- colNames = append(colNames, session.Engine.Quote(v.colName)+" = "+v.expr)
- }
-
- session.Statement.processIdParam()
-
- var autoCond builder.Cond
- if !session.Statement.noAutoCondition && len(condiBean) > 0 {
- var err error
- autoCond, err = session.Statement.buildConds(session.Statement.RefTable, condiBean[0], true, true, false, true, false)
- if err != nil {
- return 0, err
- }
- }
-
- st := session.Statement
- defer session.resetStatement()
-
- var sqlStr string
- var condArgs []interface{}
- var condSQL string
- cond := session.Statement.cond.And(autoCond)
-
- doIncVer := false
- var verValue *reflect.Value
- if table != nil && table.Version != "" && session.Statement.checkVersion {
- verValue, err = table.VersionColumn().ValueOf(bean)
- if err != nil {
- return 0, err
- }
-
- cond = cond.And(builder.Eq{session.Engine.Quote(table.Version): verValue.Interface()})
- condSQL, condArgs, _ = builder.ToSQL(cond)
-
- if len(condSQL) > 0 {
- condSQL = "WHERE " + condSQL
- }
-
- if st.LimitN > 0 {
- condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
- }
-
- sqlStr = fmt.Sprintf("UPDATE %v SET %v, %v %v",
- session.Engine.Quote(session.Statement.TableName()),
- strings.Join(colNames, ", "),
- session.Engine.Quote(table.Version)+" = "+session.Engine.Quote(table.Version)+" + 1",
- condSQL)
-
- doIncVer = true
- } else {
- condSQL, condArgs, _ = builder.ToSQL(cond)
- if len(condSQL) > 0 {
- condSQL = "WHERE " + condSQL
- }
-
- if st.LimitN > 0 {
- condSQL = condSQL + fmt.Sprintf(" LIMIT %d", st.LimitN)
- }
-
- sqlStr = fmt.Sprintf("UPDATE %v SET %v %v",
- session.Engine.Quote(session.Statement.TableName()),
- strings.Join(colNames, ", "),
- condSQL)
- }
-
- res, err := session.exec(sqlStr, append(args, condArgs...)...)
- if err != nil {
- return 0, err
- } else if doIncVer {
- if verValue != nil && verValue.IsValid() && verValue.CanSet() {
- verValue.SetInt(verValue.Int() + 1)
- }
- }
-
- if table != nil {
- if cacher := session.Engine.getCacher2(table); cacher != nil && session.Statement.UseCache {
- cacher.ClearIds(session.Statement.TableName())
- cacher.ClearBeans(session.Statement.TableName())
- }
- }
-
- // handle after update processors
- if session.IsAutoCommit {
- for _, closure := range session.afterClosures {
- closure(bean)
- }
- if processor, ok := interface{}(bean).(AfterUpdateProcessor); ok {
- session.Engine.logger.Debug("[event]", session.Statement.TableName(), " has after update processor")
- 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)
- // FIXME: if bean is a map type, it will panic because map cannot be as map key
- session.afterUpdateBeans[bean] = &afterClosures
- }
-
- } else {
- if _, ok := interface{}(bean).(AfterInsertProcessor); ok {
- session.afterUpdateBeans[bean] = nil
- }
- }
- }
- cleanupProcessorsClosures(&session.afterClosures) // cleanup after used
- // --
-
- return res.RowsAffected()
-}
-
-func (session *Session) cacheDelete(sqlStr string, args ...interface{}) error {
- if session.Statement.RefTable == nil ||
- session.Tx != nil {
- return ErrCacheFailed
- }
-
- for _, filter := range session.Engine.dialect.Filters() {
- sqlStr = filter.Do(sqlStr, session.Engine.dialect, session.Statement.RefTable)
- }
-
- newsql := session.Statement.convertIDSQL(sqlStr)
- if newsql == "" {
- return ErrCacheFailed
- }
-
- cacher := session.Engine.getCacher2(session.Statement.RefTable)
- tableName := session.Statement.TableName()
- ids, err := core.GetCacheSql(cacher, tableName, newsql, args)
- if err != nil {
- resultsSlice, err := session.query(newsql, args...)
- if err != nil {
- return err
- }
- ids = make([]core.PK, 0)
- if len(resultsSlice) > 0 {
- for _, data := range resultsSlice {
- var id int64
- var pk core.PK = make([]interface{}, 0)
- for _, col := range session.Statement.RefTable.PKColumns() {
- if v, ok := data[col.Name]; !ok {
- return errors.New("no id")
- } else if col.SQLType.IsText() {
- pk = append(pk, string(v))
- } else if col.SQLType.IsNumeric() {
- id, err = strconv.ParseInt(string(v), 10, 64)
- if err != nil {
- return err
- }
- pk = append(pk, id)
- } else {
- return errors.New("not supported primary key type")
- }
- }
- ids = append(ids, pk)
- }
- }
- } /*else {
- session.Engine.LogDebug("delete cache sql %v", newsql)
- cacher.DelIds(tableName, genSqlKey(newsql, args))
- }*/
-
- for _, id := range ids {
- session.Engine.logger.Debug("[cacheDelete] delete cache obj", tableName, id)
- sid, err := id.ToString()
- if err != nil {
- return err
- }
- cacher.DelBean(tableName, sid)
- }
- session.Engine.logger.Debug("[cacheDelete] clear cache sql", tableName)
- cacher.ClearIds(tableName)
- return nil
-}
-
-// Delete records, bean's non-empty fields are conditions
-func (session *Session) Delete(bean interface{}) (int64, error) {
- defer session.resetStatement()
- if session.IsAutoClose {
- defer session.Close()
- }
-
- session.Statement.setRefValue(rValue(bean))
- var table = session.Statement.RefTable
-
- // handle before delete processors
- for _, closure := range session.beforeClosures {
- closure(bean)
- }
- cleanupProcessorsClosures(&session.beforeClosures)
-
- if processor, ok := interface{}(bean).(BeforeDeleteProcessor); ok {
- processor.BeforeDelete()
- }
-
- // --
- condSQL, condArgs, _ := session.Statement.genConds(bean)
- if len(condSQL) == 0 && session.Statement.LimitN == 0 {
- return 0, ErrNeedDeletedCond
- }
-
- var tableName = session.Engine.Quote(session.Statement.TableName())
- var deleteSQL string
- if len(condSQL) > 0 {
- deleteSQL = fmt.Sprintf("DELETE FROM %v WHERE %v", tableName, condSQL)
- } else {
- deleteSQL = fmt.Sprintf("DELETE FROM %v", tableName)
- }
-
- var orderSQL string
- if len(session.Statement.OrderStr) > 0 {
- orderSQL += fmt.Sprintf(" ORDER BY %s", session.Statement.OrderStr)
- }
- if session.Statement.LimitN > 0 {
- orderSQL += fmt.Sprintf(" LIMIT %d", session.Statement.LimitN)
- }
-
- if len(orderSQL) > 0 {
- switch session.Engine.dialect.DBType() {
- case core.POSTGRES:
- inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
- if len(condSQL) > 0 {
- deleteSQL += " AND " + inSQL
- } else {
- deleteSQL += " WHERE " + inSQL
- }
- case core.SQLITE:
- inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
- if len(condSQL) > 0 {
- deleteSQL += " AND " + inSQL
- } else {
- deleteSQL += " WHERE " + inSQL
- }
- // TODO: how to handle delete limit on mssql?
- case core.MSSQL:
- return 0, ErrNotImplemented
- default:
- deleteSQL += orderSQL
- }
- }
-
- var realSQL string
- argsForCache := make([]interface{}, 0, len(condArgs)*2)
- if session.Statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled
- realSQL = deleteSQL
- copy(argsForCache, condArgs)
- argsForCache = append(condArgs, argsForCache...)
- } else {
- // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache.
- copy(argsForCache, condArgs)
- argsForCache = append(condArgs, argsForCache...)
-
- deletedColumn := table.DeletedColumn()
- realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v",
- session.Engine.Quote(session.Statement.TableName()),
- session.Engine.Quote(deletedColumn.Name),
- condSQL)
-
- if len(orderSQL) > 0 {
- switch session.Engine.dialect.DBType() {
- case core.POSTGRES:
- inSQL := fmt.Sprintf("ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQL)
- if len(condSQL) > 0 {
- realSQL += " AND " + inSQL
- } else {
- realSQL += " WHERE " + inSQL
- }
- case core.SQLITE:
- inSQL := fmt.Sprintf("rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQL)
- if len(condSQL) > 0 {
- realSQL += " AND " + inSQL
- } else {
- realSQL += " WHERE " + inSQL
- }
- // TODO: how to handle delete limit on mssql?
- case core.MSSQL:
- return 0, ErrNotImplemented
- default:
- realSQL += orderSQL
- }
- }
-
- // !oinume! Insert NowTime to the head of session.Statement.Params
- condArgs = append(condArgs, "")
- paramsLen := len(condArgs)
- copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
-
- val, t := session.Engine.NowTime2(deletedColumn.SQLType.Name)
- condArgs[0] = val
-
- var colName = deletedColumn.Name
- session.afterClosures = append(session.afterClosures, func(bean interface{}) {
- col := table.GetColumn(colName)
- setColumnTime(bean, col, t)
- })
- }
-
- if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache {
- session.cacheDelete(deleteSQL, argsForCache...)
- }
-
- res, err := session.exec(realSQL, condArgs...)
- 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()
-}
-
// saveLastSQL stores executed query information
func (session *Session) saveLastSQL(sql string, args ...interface{}) {
session.lastSQL = sql
@@ -3949,198 +1665,6 @@ func (session *Session) tbNameNoSchema(table *core.Table) string {
return table.Name
}
-// Sync2 synchronize structs to database tables
-func (session *Session) Sync2(beans ...interface{}) error {
- engine := session.Engine
-
- tables, err := engine.DBMetas()
- if err != nil {
- return err
- }
-
- var structTables []*core.Table
-
- for _, bean := range beans {
- v := rValue(bean)
- table := engine.mapType(v)
- structTables = append(structTables, table)
- var tbName = session.tbNameNoSchema(table)
-
- var oriTable *core.Table
- for _, tb := range tables {
- if strings.EqualFold(tb.Name, tbName) {
- oriTable = tb
- break
- }
- }
-
- if oriTable == nil {
- err = session.StoreEngine(session.Statement.StoreEngine).CreateTable(bean)
- if err != nil {
- return err
- }
-
- err = session.CreateUniques(bean)
- if err != nil {
- return err
- }
-
- err = session.CreateIndexes(bean)
- if err != nil {
- return err
- }
- } else {
- for _, col := range table.Columns() {
- var oriCol *core.Column
- for _, col2 := range oriTable.Columns() {
- if strings.EqualFold(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) {
- // currently only support mysql & postgres
- if engine.dialect.DBType() == core.MYSQL ||
- engine.dialect.DBType() == core.POSTGRES {
- engine.logger.Infof("Table %s column %s change type from %s to %s\n",
- tbName, col.Name, curType, expectedType)
- _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col))
- } else {
- engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s\n",
- tbName, col.Name, curType, expectedType)
- }
- } else if strings.HasPrefix(curType, core.Varchar) && strings.HasPrefix(expectedType, core.Varchar) {
- if engine.dialect.DBType() == core.MYSQL {
- if oriCol.Length < col.Length {
- engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n",
- tbName, col.Name, oriCol.Length, col.Length)
- _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col))
- }
- }
- } else {
- if !(strings.HasPrefix(curType, expectedType) && curType[len(expectedType)] == '(') {
- engine.logger.Warnf("Table %s column %s db type is %s, struct type is %s",
- tbName, col.Name, curType, expectedType)
- }
- }
- } else if expectedType == core.Varchar {
- if engine.dialect.DBType() == core.MYSQL {
- if oriCol.Length < col.Length {
- engine.logger.Infof("Table %s column %s change type from varchar(%d) to varchar(%d)\n",
- tbName, col.Name, oriCol.Length, col.Length)
- _, err = engine.Exec(engine.dialect.ModifyColumnSql(table.Name, col))
- }
- }
- }
- if col.Default != oriCol.Default {
- engine.logger.Warnf("Table %s Column %s db default is %s, struct default is %s",
- tbName, col.Name, oriCol.Default, col.Default)
- }
- if col.Nullable != oriCol.Nullable {
- engine.logger.Warnf("Table %s Column %s db nullable is %v, struct nullable is %v",
- tbName, col.Name, oriCol.Nullable, col.Nullable)
- }
- } else {
- session := engine.NewSession()
- session.Statement.RefTable = table
- session.Statement.tableName = tbName
- defer session.Close()
- err = session.addColumn(col.Name)
- }
- if err != nil {
- return err
- }
- }
-
- var foundIndexNames = make(map[string]bool)
- var addedNames = make(map[string]*core.Index)
-
- 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(tbName, oriIndex)
- _, err = engine.Exec(sql)
- if err != nil {
- return err
- }
- oriIndex = nil
- }
- }
-
- if oriIndex == nil {
- addedNames[name] = index
- }
- }
-
- for name2, index2 := range oriTable.Indexes {
- if _, ok := foundIndexNames[name2]; !ok {
- sql := engine.dialect.DropIndexSql(tbName, index2)
- _, err = engine.Exec(sql)
- if err != nil {
- return err
- }
- }
- }
-
- for name, index := range addedNames {
- if index.Type == core.UniqueType {
- session := engine.NewSession()
- session.Statement.RefTable = table
- session.Statement.tableName = tbName
- defer session.Close()
- err = session.addUnique(tbName, name)
- } else if index.Type == core.IndexType {
- session := engine.NewSession()
- session.Statement.RefTable = table
- session.Statement.tableName = tbName
- defer session.Close()
- err = session.addIndex(tbName, name)
- }
- if err != nil {
- return err
- }
- }
- }
- }
-
- for _, table := range tables {
- var oriTable *core.Table
- for _, structTable := range structTables {
- if strings.EqualFold(table.Name, session.tbNameNoSchema(structTable)) {
- oriTable = structTable
- break
- }
- }
-
- if oriTable == nil {
- //engine.LogWarnf("Table %s has no struct to mapping it", table.Name)
- continue
- }
-
- for _, colName := range table.ColumnsSeq() {
- if oriTable.GetColumn(colName) == nil {
- engine.logger.Warnf("Table %s has column %s but struct has not related field", table.Name, colName)
- }
- }
- }
- return nil
-}
-
// Unscoped always disable struct tag "deleted"
func (session *Session) Unscoped() *Session {
session.Statement.Unscoped()