140 lines
4.0 KiB
Go
140 lines
4.0 KiB
Go
// Copyright 2020 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 statements
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"xorm.io/xorm/dialects"
|
|
"xorm.io/xorm/schemas"
|
|
)
|
|
|
|
var (
|
|
// ErrNeedDeletedCond delete needs less one condition error
|
|
ErrNeedDeletedCond = errors.New("Delete action needs at least one condition")
|
|
|
|
// ErrNotImplemented not implemented
|
|
ErrNotImplemented = errors.New("Not implemented")
|
|
)
|
|
|
|
// GenDeleteSQL generated delete SQL according conditions
|
|
func (statement *Statement) GenDeleteSQL(bean interface{}) (string, string, []interface{}, error) {
|
|
condSQL, condArgs, err := statement.GenConds(bean)
|
|
if err != nil {
|
|
return "", "", nil, err
|
|
}
|
|
pLimitN := statement.LimitN
|
|
if len(condSQL) == 0 && (pLimitN == nil || *pLimitN == 0) {
|
|
return "", "", nil, ErrNeedDeletedCond
|
|
}
|
|
|
|
var tableNameNoQuote = statement.TableName()
|
|
var tableName = statement.quote(tableNameNoQuote)
|
|
var table = 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(statement.OrderStr) > 0 {
|
|
orderSQL += fmt.Sprintf(" ORDER BY %s", statement.OrderStr)
|
|
}
|
|
if pLimitN != nil && *pLimitN > 0 {
|
|
limitNValue := *pLimitN
|
|
orderSQL += fmt.Sprintf(" LIMIT %d", limitNValue)
|
|
}
|
|
|
|
if len(orderSQL) > 0 {
|
|
switch statement.dialect.DBType() {
|
|
case schemas.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 schemas.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 schemas.MSSQL:
|
|
return "", "", nil, ErrNotImplemented
|
|
default:
|
|
deleteSQL += orderSQL
|
|
}
|
|
}
|
|
|
|
var realSQL string
|
|
argsForCache := make([]interface{}, 0, len(condArgs)*2)
|
|
if statement.GetUnscoped() || 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 caches.
|
|
copy(argsForCache, condArgs)
|
|
argsForCache = append(condArgs, argsForCache...)
|
|
|
|
deletedColumn := table.DeletedColumn()
|
|
realSQL = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v",
|
|
statement.quote(statement.TableName()),
|
|
statement.quote(deletedColumn.Name),
|
|
condSQL)
|
|
|
|
if len(orderSQL) > 0 {
|
|
switch statement.dialect.DBType() {
|
|
case schemas.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 schemas.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 schemas.MSSQL:
|
|
return "", "", nil, ErrNotImplemented
|
|
default:
|
|
realSQL += orderSQL
|
|
}
|
|
}
|
|
|
|
// !oinume! Insert nowTime to the head of statement.Params
|
|
condArgs = append(condArgs, "")
|
|
paramsLen := len(condArgs)
|
|
copy(condArgs[1:paramsLen], condArgs[0:paramsLen-1])
|
|
|
|
now := ColumnNow(deletedColumn, statement.defaultTimeZone)
|
|
val := dialects.FormatTime(statement.dialect, deletedColumn.SQLType.Name, now)
|
|
condArgs[0] = val
|
|
}
|
|
return realSQL, deleteSQL, condArgs, nil
|
|
}
|
|
|
|
// ColumnNow returns the current time for a column
|
|
func ColumnNow(col *schemas.Column, defaultTimeZone *time.Location) time.Time {
|
|
t := time.Now()
|
|
tz := defaultTimeZone
|
|
if !col.DisableTimeZone && col.TimeZone != nil {
|
|
tz = col.TimeZone
|
|
}
|
|
return t.In(tz)
|
|
}
|