2017-01-03 11:20:28 +03:00
// Copyright 2016 The Xorm Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package xorm
import (
"errors"
"fmt"
"strconv"
"github.com/go-xorm/core"
)
2017-09-13 08:18:22 +03:00
func ( session * Session ) cacheDelete ( table * core . Table , tableName , sqlStr string , args ... interface { } ) error {
if table == nil ||
2017-08-22 14:39:52 +03:00
session . tx != nil {
2017-01-03 11:20:28 +03:00
return ErrCacheFailed
}
2017-08-22 14:39:52 +03:00
for _ , filter := range session . engine . dialect . Filters ( ) {
2017-09-13 08:18:22 +03:00
sqlStr = filter . Do ( sqlStr , session . engine . dialect , table )
2017-01-03 11:20:28 +03:00
}
2017-08-22 14:39:52 +03:00
newsql := session . statement . convertIDSQL ( sqlStr )
2017-01-03 11:20:28 +03:00
if newsql == "" {
return ErrCacheFailed
}
2018-07-20 05:10:17 +03:00
cacher := session . engine . getCacher ( tableName )
2017-09-13 08:18:22 +03:00
pkColumns := table . PKColumns ( )
2017-01-03 11:20:28 +03:00
ids , err := core . GetCacheSql ( cacher , tableName , newsql , args )
if err != nil {
2017-09-13 08:18:22 +03:00
resultsSlice , err := session . queryBytes ( newsql , args ... )
2017-01-03 11:20:28 +03:00
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 )
2017-09-13 08:18:22 +03:00
for _ , col := range pkColumns {
2017-01-03 11:20:28 +03:00
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 )
}
}
2017-09-13 08:18:22 +03:00
}
2017-01-03 11:20:28 +03:00
for _ , id := range ids {
2017-09-13 08:18:22 +03:00
session . engine . logger . Debug ( "[cacheDelete] delete cache obj:" , tableName , id )
2017-01-03 11:20:28 +03:00
sid , err := id . ToString ( )
if err != nil {
return err
}
cacher . DelBean ( tableName , sid )
}
2017-09-13 08:18:22 +03:00
session . engine . logger . Debug ( "[cacheDelete] clear cache table:" , tableName )
2017-01-03 11:20:28 +03:00
cacher . ClearIds ( tableName )
return nil
}
// Delete records, bean's non-empty fields are conditions
func ( session * Session ) Delete ( bean interface { } ) ( int64 , error ) {
2017-08-22 14:39:52 +03:00
if session . isAutoClose {
2017-01-03 11:20:28 +03:00
defer session . Close ( )
}
2018-07-20 05:10:17 +03:00
if err := session . statement . setRefBean ( bean ) ; err != nil {
2017-05-02 03:50:33 +03:00
return 0 , err
}
2017-01-03 11:20:28 +03:00
// handle before delete processors
for _ , closure := range session . beforeClosures {
closure ( bean )
}
cleanupProcessorsClosures ( & session . beforeClosures )
if processor , ok := interface { } ( bean ) . ( BeforeDeleteProcessor ) ; ok {
processor . BeforeDelete ( )
}
2017-08-22 14:39:52 +03:00
condSQL , condArgs , err := session . statement . genConds ( bean )
if err != nil {
return 0 , err
}
if len ( condSQL ) == 0 && session . statement . LimitN == 0 {
2017-01-03 11:20:28 +03:00
return 0 , ErrNeedDeletedCond
}
2017-09-13 08:18:22 +03:00
var tableNameNoQuote = session . statement . TableName ( )
var tableName = session . engine . Quote ( tableNameNoQuote )
var table = session . statement . RefTable
2017-01-03 11:20:28 +03:00
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
2017-08-22 14:39:52 +03:00
if len ( session . statement . OrderStr ) > 0 {
orderSQL += fmt . Sprintf ( " ORDER BY %s" , session . statement . OrderStr )
2017-01-03 11:20:28 +03:00
}
2017-08-22 14:39:52 +03:00
if session . statement . LimitN > 0 {
orderSQL += fmt . Sprintf ( " LIMIT %d" , session . statement . LimitN )
2017-01-03 11:20:28 +03:00
}
if len ( orderSQL ) > 0 {
2017-08-22 14:39:52 +03:00
switch session . engine . dialect . DBType ( ) {
2017-01-03 11:20:28 +03:00
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 )
2017-08-22 14:39:52 +03:00
if session . statement . unscoped || table . DeletedColumn ( ) == nil { // tag "deleted" is disabled
2017-01-03 11:20:28 +03:00
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" ,
2017-08-22 14:39:52 +03:00
session . engine . Quote ( session . statement . TableName ( ) ) ,
session . engine . Quote ( deletedColumn . Name ) ,
2017-01-03 11:20:28 +03:00
condSQL )
if len ( orderSQL ) > 0 {
2017-08-22 14:39:52 +03:00
switch session . engine . dialect . DBType ( ) {
2017-01-03 11:20:28 +03:00
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
}
}
2017-10-01 19:52:35 +03:00
// !oinume! Insert nowTime to the head of session.statement.Params
2017-01-03 11:20:28 +03:00
condArgs = append ( condArgs , "" )
paramsLen := len ( condArgs )
copy ( condArgs [ 1 : paramsLen ] , condArgs [ 0 : paramsLen - 1 ] )
2017-10-01 19:52:35 +03:00
val , t := session . engine . nowTime ( deletedColumn )
2017-01-03 11:20:28 +03:00
condArgs [ 0 ] = val
var colName = deletedColumn . Name
session . afterClosures = append ( session . afterClosures , func ( bean interface { } ) {
col := table . GetColumn ( colName )
setColumnTime ( bean , col , t )
} )
}
2018-12-12 04:01:41 +03:00
if cacher := session . engine . getCacher ( tableNameNoQuote ) ; cacher != nil && session . statement . UseCache {
2017-09-13 08:18:22 +03:00
session . cacheDelete ( table , tableNameNoQuote , deleteSQL , argsForCache ... )
2017-01-03 11:20:28 +03:00
}
2017-09-13 08:18:22 +03:00
session . statement . RefTable = table
2017-01-03 11:20:28 +03:00
res , err := session . exec ( realSQL , condArgs ... )
if err != nil {
return 0 , err
}
// handle after delete processors
2017-08-22 14:39:52 +03:00
if session . isAutoCommit {
2017-01-03 11:20:28 +03:00
for _ , closure := range session . afterClosures {
closure ( bean )
}
if processor , ok := interface { } ( bean ) . ( 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 {
2017-01-25 17:54:52 +03:00
if _ , ok := interface { } ( bean ) . ( AfterDeleteProcessor ) ; ok {
2017-01-03 11:20:28 +03:00
session . afterDeleteBeans [ bean ] = nil
}
}
}
cleanupProcessorsClosures ( & session . afterClosures )
// --
return res . RowsAffected ( )
}