mirror of https://github.com/gogits/gogs.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
240 lines
6.5 KiB
240 lines
6.5 KiB
// 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" |
|
) |
|
|
|
func (session *Session) cacheDelete(table *core.Table, tableName, sqlStr string, args ...interface{}) error { |
|
if table == nil || |
|
session.tx != nil { |
|
return ErrCacheFailed |
|
} |
|
|
|
for _, filter := range session.engine.dialect.Filters() { |
|
sqlStr = filter.Do(sqlStr, session.engine.dialect, table) |
|
} |
|
|
|
newsql := session.statement.convertIDSQL(sqlStr) |
|
if newsql == "" { |
|
return ErrCacheFailed |
|
} |
|
|
|
cacher := session.engine.getCacher(tableName) |
|
pkColumns := table.PKColumns() |
|
ids, err := core.GetCacheSql(cacher, tableName, newsql, args) |
|
if err != nil { |
|
resultsSlice, err := session.queryBytes(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 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) |
|
} |
|
} |
|
} |
|
|
|
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 table:", tableName) |
|
cacher.ClearIds(tableName) |
|
return nil |
|
} |
|
|
|
// Delete records, bean's non-empty fields are conditions |
|
func (session *Session) Delete(bean interface{}) (int64, error) { |
|
if session.isAutoClose { |
|
defer session.Close() |
|
} |
|
|
|
if err := session.statement.setRefBean(bean); err != nil { |
|
return 0, err |
|
} |
|
|
|
// 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, err := session.statement.genConds(bean) |
|
if err != nil { |
|
return 0, err |
|
} |
|
if len(condSQL) == 0 && session.statement.LimitN == 0 { |
|
return 0, ErrNeedDeletedCond |
|
} |
|
|
|
var tableNameNoQuote = session.statement.TableName() |
|
var tableName = session.engine.Quote(tableNameNoQuote) |
|
var table = session.statement.RefTable |
|
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.nowTime(deletedColumn) |
|
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.getCacher(tableName); cacher != nil && session.statement.UseCache { |
|
session.cacheDelete(table, tableNameNoQuote, deleteSQL, argsForCache...) |
|
} |
|
|
|
session.statement.RefTable = table |
|
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).(AfterDeleteProcessor); ok { |
|
session.afterDeleteBeans[bean] = nil |
|
} |
|
} |
|
} |
|
cleanupProcessorsClosures(&session.afterClosures) |
|
// -- |
|
|
|
return res.RowsAffected() |
|
}
|
|
|