diff --git a/engine.go b/engine.go index cc498441..07a23d30 100644 --- a/engine.go +++ b/engine.go @@ -776,6 +776,8 @@ func (engine *Engine) mapType(v reflect.Value) *core.Table { col.Default = "1" case k == "UPDATED": col.IsUpdated = true + case k == "DELETED": + col.IsDeleted = true case strings.HasPrefix(k, "INDEX(") && strings.HasSuffix(k, ")"): indexName := k[len("INDEX")+1 : len(k)-1] indexNames[indexName] = core.IndexType @@ -1414,3 +1416,10 @@ func (engine *Engine) FormatTime(sqlTypeName string, t time.Time) (v interface{} } return } + +// Always disable struct tag "deleted" +func (engine *Engine) Unscoped() *Session { + session := engine.NewSession() + session.IsAutoClose = true + return session.Unscoped() +} diff --git a/session.go b/session.go index 9af02bfe..acc52420 100644 --- a/session.go +++ b/session.go @@ -1079,7 +1079,7 @@ func (session *Session) Find(rowsSlicePtr interface{}, condiBean ...interface{}) if len(condiBean) > 0 { colNames, args := buildConditions(session.Engine, table, condiBean[0], true, true, false, true, session.Statement.allUseBool, session.Statement.useAllCols, - session.Statement.mustColumnMap) + session.Statement.unscoped, session.Statement.mustColumnMap) session.Statement.ConditionStr = strings.Join(colNames, " AND ") session.Statement.BeanArgs = args } @@ -3172,7 +3172,7 @@ func (session *Session) Update(bean interface{}, condiBean ...interface{}) (int6 if len(condiBean) > 0 { condiColNames, condiArgs = buildConditions(session.Engine, session.Statement.RefTable, condiBean[0], true, true, false, true, session.Statement.allUseBool, session.Statement.useAllCols, - session.Statement.mustColumnMap) + session.Statement.unscoped, session.Statement.mustColumnMap) } var condition = "" @@ -3376,7 +3376,7 @@ func (session *Session) Delete(bean interface{}) (int64, error) { session.Statement.RefTable = table colNames, args := buildConditions(session.Engine, table, bean, true, true, false, true, session.Statement.allUseBool, session.Statement.useAllCols, - session.Statement.mustColumnMap) + session.Statement.unscoped, session.Statement.mustColumnMap) var condition = "" var andStr = session.Engine.dialect.AndStr() @@ -3402,13 +3402,39 @@ func (session *Session) Delete(bean interface{}) (int64, error) { return 0, ErrNeedDeletedCond } - sqlStr := fmt.Sprintf("DELETE FROM %v WHERE %v", - session.Engine.Quote(session.Statement.TableName()), condition) + sqlStr, sqlStrForCache := "", "" + argsForCache := make([]interface{}, 0, len(args) * 2) + if session.Statement.unscoped || table.DeletedColumn() == nil { // tag "deleted" is disabled + sqlStr = fmt.Sprintf("DELETE FROM %v WHERE %v", + session.Engine.Quote(session.Statement.TableName()), condition) + + sqlStrForCache = sqlStr + copy(argsForCache, args) + argsForCache = append(session.Statement.Params, argsForCache...) + } else { + // !oinume! sqlStrForCache and argsForCache is needed to behave as executing "DELETE FROM ..." for cache. + sqlStrForCache = fmt.Sprintf("DELETE FROM %v WHERE %v", + session.Engine.Quote(session.Statement.TableName()), condition) + copy(argsForCache, args) + argsForCache = append(session.Statement.Params, argsForCache...) + + deletedColumn := table.DeletedColumn() + sqlStr = fmt.Sprintf("UPDATE %v SET %v = ? WHERE %v", + session.Engine.Quote(session.Statement.TableName()), + session.Engine.Quote(deletedColumn.Name), + condition) + + // !oinume! Insert NowTime to the head of session.Statement.Params + session.Statement.Params = append(session.Statement.Params, "") + paramsLen := len(session.Statement.Params) + copy(session.Statement.Params[1:paramsLen], session.Statement.Params[0:paramsLen-1]) + session.Statement.Params[0] = session.Engine.NowTime(deletedColumn.SQLType.Name) + } args = append(session.Statement.Params, args...) if cacher := session.Engine.getCacher2(session.Statement.RefTable); cacher != nil && session.Statement.UseCache { - session.cacheDelete(sqlStr, args...) + session.cacheDelete(sqlStrForCache, argsForCache...) } res, err := session.exec(sqlStr, args...) @@ -3612,6 +3638,12 @@ func (s *Session) Sync2(beans ...interface{}) error { return nil } +// Always disable struct tag "deleted" +func (session *Session) Unscoped() *Session { + session.Statement.Unscoped() + return session +} + func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, includeQuote bool) ([]string, []interface{}, error) { colNames := make([]string, 0) args := make([]interface{}, 0) @@ -3651,6 +3683,10 @@ func genCols(table *core.Table, session *Session, bean interface{}, useCol bool, } } + if col.IsDeleted { + continue + } + if session.Statement.ColumnStr != "" { if _, ok := session.Statement.columnMap[lColName]; !ok { continue diff --git a/statement.go b/statement.go index 7391629d..12006128 100644 --- a/statement.go +++ b/statement.go @@ -57,6 +57,7 @@ type Statement struct { IsDistinct bool allUseBool bool checkVersion bool + unscoped bool mustColumnMap map[string]bool inColumns map[string]*inParam incrColumns map[string]incrParam @@ -91,6 +92,7 @@ func (statement *Statement) Init() { statement.useAllCols = false statement.mustColumnMap = make(map[string]bool) statement.checkVersion = true + statement.unscoped = false statement.inColumns = make(map[string]*inParam) statement.incrColumns = make(map[string]incrParam) statement.decrColumns = make(map[string]decrParam) @@ -286,6 +288,9 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, if !includeAutoIncr && col.IsAutoIncrement { continue } + if col.IsDeleted { + continue + } if engine.dialect.DBType() == core.MSSQL && col.SQLType.Name == core.Text { continue @@ -465,7 +470,7 @@ func buildUpdates(engine *Engine, table *core.Table, bean interface{}, // Auto generating conditions according a struct func buildConditions(engine *Engine, table *core.Table, bean interface{}, includeVersion bool, includeUpdated bool, includeNil bool, - includeAutoIncr bool, allUseBool bool, useAllCols bool, + includeAutoIncr bool, allUseBool bool, useAllCols bool, unscoped bool, mustColumnMap map[string]bool) ([]string, []interface{}) { colNames := make([]string, 0) @@ -490,6 +495,10 @@ func buildConditions(engine *Engine, table *core.Table, bean interface{}, continue } + if col.IsDeleted && !unscoped { // tag "deleted" is enabled + colNames = append(colNames, fmt.Sprintf("%v IS NULL", engine.Quote(col.Name))) + } + fieldValue := *fieldValuePtr if fieldValue.Interface() == nil { continue @@ -926,6 +935,12 @@ func (statement *Statement) Having(conditions string) *Statement { return statement } +// Always disable struct tag "deleted" +func (statement *Statement) Unscoped() *Statement { + statement.unscoped = true + return statement +} + func (statement *Statement) genColumnStr() string { table := statement.RefTable colNames := make([]string, 0) @@ -1030,7 +1045,7 @@ func (statement *Statement) genGetSql(bean interface{}) (string, []interface{}) colNames, args := buildConditions(statement.Engine, table, bean, true, true, false, true, statement.allUseBool, statement.useAllCols, - statement.mustColumnMap) + statement.unscoped, statement.mustColumnMap) statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.dialect.AndStr()+" ") statement.BeanArgs = args @@ -1076,7 +1091,8 @@ func (statement *Statement) genCountSql(bean interface{}) (string, []interface{} statement.RefTable = table colNames, args := buildConditions(statement.Engine, table, bean, true, true, false, - true, statement.allUseBool, statement.useAllCols, statement.mustColumnMap) + true, statement.allUseBool, statement.useAllCols, + statement.unscoped, statement.mustColumnMap) statement.ConditionStr = strings.Join(colNames, " "+statement.Engine.Dialect().AndStr()+" ") statement.BeanArgs = args