From eb3426d3231f885ec487a25b16497ed6cd7d1bba Mon Sep 17 00:00:00 2001 From: Lunny Xiao Date: Fri, 21 Jul 2023 22:40:15 +0800 Subject: [PATCH] move sql geneartion for update to statement --- internal/statements/delete.go | 116 ++++++++++++++++++++++++++++++++++ session_delete.go | 93 ++++----------------------- 2 files changed, 129 insertions(+), 80 deletions(-) create mode 100644 internal/statements/delete.go diff --git a/internal/statements/delete.go b/internal/statements/delete.go new file mode 100644 index 00000000..7c903178 --- /dev/null +++ b/internal/statements/delete.go @@ -0,0 +1,116 @@ +// Copyright 2023 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/builder" + "xorm.io/xorm/internal/utils" + "xorm.io/xorm/schemas" +) + +func (statement *Statement) writeDeleteOrder(w builder.Writer) error { + if err := statement.WriteOrderBy(w); err != nil { + return err + } + + if statement.LimitN != nil && *statement.LimitN > 0 { + limitNValue := *statement.LimitN + if _, err := fmt.Fprintf(w, " LIMIT %d", limitNValue); err != nil { + return err + } + } + + return nil +} + +// ErrNotImplemented not implemented +var ErrNotImplemented = errors.New("Not implemented") + +func (statement *Statement) writeOrderCond(orderCondWriter builder.Writer, condWriter, orderSQLWriter *builder.BytesWriter, tableName string) error { + if orderSQLWriter.Len() > 0 { + switch statement.dialect.URI().DBType { + case schemas.POSTGRES: + if condWriter.Len() > 0 { + fmt.Fprintf(orderCondWriter, " AND ") + } else { + fmt.Fprintf(orderCondWriter, " WHERE ") + } + fmt.Fprintf(orderCondWriter, "ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQLWriter.String()) + orderCondWriter.Append(orderSQLWriter.Args()...) + case schemas.SQLITE: + if condWriter.Len() > 0 { + fmt.Fprintf(orderCondWriter, " AND ") + } else { + fmt.Fprintf(orderCondWriter, " WHERE ") + } + fmt.Fprintf(orderCondWriter, "rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQLWriter.String()) + // TODO: how to handle delete limit on mssql? + case schemas.MSSQL: + return ErrNotImplemented + default: + fmt.Fprint(orderCondWriter, orderSQLWriter.String()) + orderCondWriter.Append(orderSQLWriter.Args()...) + } + } + return nil +} + +func (statement *Statement) WriteDelete(realSQLWriter, deleteSQLWriter *builder.BytesWriter, nowTime func(*schemas.Column) (interface{}, time.Time, error)) error { + var ( + condWriter = builder.NewWriter() + err error + ) + + if err = statement.Conds().WriteTo(statement.QuoteReplacer(condWriter)); err != nil { + return err + } + + tableNameNoQuote := statement.TableName() + tableName := statement.dialect.Quoter().Quote(tableNameNoQuote) + table := statement.RefTable + fmt.Fprintf(deleteSQLWriter, "DELETE FROM %v", tableName) + if condWriter.Len() > 0 { + fmt.Fprintf(deleteSQLWriter, " WHERE %v", condWriter.String()) + deleteSQLWriter.Append(condWriter.Args()...) + } + + orderSQLWriter := builder.NewWriter() + if err := statement.writeDeleteOrder(orderSQLWriter); err != nil { + return err + } + + orderCondWriter := builder.NewWriter() + if err := statement.writeOrderCond(orderCondWriter, condWriter, orderSQLWriter, tableName); err != nil { + return err + } + + argsForCache := make([]interface{}, 0, len(deleteSQLWriter.Args())*2) + copy(argsForCache, deleteSQLWriter.Args()) + argsForCache = append(deleteSQLWriter.Args(), argsForCache...) + if statement.GetUnscoped() || table == nil || table.DeletedColumn() == nil { // tag "deleted" is disabled + return utils.WriteBuilder(realSQLWriter, deleteSQLWriter, orderCondWriter) + } + + deletedColumn := table.DeletedColumn() + if _, err := fmt.Fprintf(realSQLWriter, "UPDATE %v SET %v = ? WHERE %v", + statement.dialect.Quoter().Quote(statement.TableName()), + statement.dialect.Quoter().Quote(deletedColumn.Name), + condWriter.String()); err != nil { + return err + } + + val, _, err := nowTime(deletedColumn) + if err != nil { + return err + } + realSQLWriter.Append(val) + realSQLWriter.Append(condWriter.Args()...) + + return utils.WriteBuilder(realSQLWriter, orderCondWriter) +} diff --git a/session_delete.go b/session_delete.go index 6c63d9b0..7336040f 100644 --- a/session_delete.go +++ b/session_delete.go @@ -6,22 +6,15 @@ package xorm import ( "errors" - "fmt" "strconv" "xorm.io/builder" "xorm.io/xorm/caches" - "xorm.io/xorm/internal/utils" "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") -) +// ErrNeedDeletedCond delete needs less one condition error +var ErrNeedDeletedCond = errors.New("Delete action needs at least one condition") func (session *Session) cacheDelete(table *schemas.Table, tableName, sqlStr string, args ...interface{}) error { if table == nil || @@ -112,9 +105,8 @@ func (session *Session) delete(beans []interface{}, mustHaveConditions bool) (in } var ( - condWriter = builder.NewWriter() - err error - bean interface{} + err error + bean interface{} ) if len(beans) > 0 { bean = beans[0] @@ -133,90 +125,27 @@ func (session *Session) delete(beans []interface{}, mustHaveConditions bool) (in } } - if err = session.statement.Conds().WriteTo(session.statement.QuoteReplacer(condWriter)); err != nil { - return 0, err - } - pLimitN := session.statement.LimitN - if mustHaveConditions && condWriter.Len() == 0 && (pLimitN == nil || *pLimitN == 0) { + if mustHaveConditions && !session.statement.Conds().IsValid() && (pLimitN == nil || *pLimitN == 0) { return 0, ErrNeedDeletedCond } tableNameNoQuote := session.statement.TableName() - tableName := session.engine.Quote(tableNameNoQuote) table := session.statement.RefTable - deleteSQLWriter := builder.NewWriter() - fmt.Fprintf(deleteSQLWriter, "DELETE FROM %v", tableName) - if condWriter.Len() > 0 { - fmt.Fprintf(deleteSQLWriter, " WHERE %v", condWriter.String()) - deleteSQLWriter.Append(condWriter.Args()...) - } - orderSQLWriter := builder.NewWriter() - if err := session.statement.WriteOrderBy(orderSQLWriter); err != nil { + realSQLWriter := builder.NewWriter() + deleteSQLWriter := builder.NewWriter() + if err := session.statement.WriteDelete(realSQLWriter, deleteSQLWriter, session.engine.nowTime); err != nil { return 0, err } - if pLimitN != nil && *pLimitN > 0 { - limitNValue := *pLimitN - if _, err := fmt.Fprintf(orderSQLWriter, " LIMIT %d", limitNValue); err != nil { - return 0, err - } - } - - orderCondWriter := builder.NewWriter() - if orderSQLWriter.Len() > 0 { - switch session.engine.dialect.URI().DBType { - case schemas.POSTGRES: - if condWriter.Len() > 0 { - fmt.Fprintf(orderCondWriter, " AND ") - } else { - fmt.Fprintf(orderCondWriter, " WHERE ") - } - fmt.Fprintf(orderCondWriter, "ctid IN (SELECT ctid FROM %s%s)", tableName, orderSQLWriter.String()) - orderCondWriter.Append(orderSQLWriter.Args()...) - case schemas.SQLITE: - if condWriter.Len() > 0 { - fmt.Fprintf(orderCondWriter, " AND ") - } else { - fmt.Fprintf(orderCondWriter, " WHERE ") - } - fmt.Fprintf(orderCondWriter, "rowid IN (SELECT rowid FROM %s%s)", tableName, orderSQLWriter.String()) - // TODO: how to handle delete limit on mssql? - case schemas.MSSQL: - return 0, ErrNotImplemented - default: - fmt.Fprint(orderCondWriter, orderSQLWriter.String()) - orderCondWriter.Append(orderSQLWriter.Args()...) - } - } - - realSQLWriter := builder.NewWriter() - argsForCache := make([]interface{}, 0, len(deleteSQLWriter.Args())*2) - copy(argsForCache, deleteSQLWriter.Args()) - argsForCache = append(deleteSQLWriter.Args(), argsForCache...) if session.statement.GetUnscoped() || table == nil || table.DeletedColumn() == nil { // tag "deleted" is disabled - if err := utils.WriteBuilder(realSQLWriter, deleteSQLWriter, orderCondWriter); err != nil { - return 0, err - } } else { deletedColumn := table.DeletedColumn() - if _, err := fmt.Fprintf(realSQLWriter, "UPDATE %v SET %v = ? WHERE %v", - session.engine.Quote(session.statement.TableName()), - session.engine.Quote(deletedColumn.Name), - condWriter.String()); err != nil { - return 0, err - } - val, t, err := session.engine.nowTime(deletedColumn) + _, t, err := session.engine.nowTime(deletedColumn) if err != nil { return 0, err } - realSQLWriter.Append(val) - realSQLWriter.Append(condWriter.Args()...) - - if err := utils.WriteBuilder(realSQLWriter, orderCondWriter); err != nil { - return 0, err - } colName := deletedColumn.Name session.afterClosures = append(session.afterClosures, func(bean interface{}) { @@ -225,6 +154,10 @@ func (session *Session) delete(beans []interface{}, mustHaveConditions bool) (in }) } + argsForCache := make([]interface{}, 0, len(deleteSQLWriter.Args())*2) + copy(argsForCache, deleteSQLWriter.Args()) + argsForCache = append(deleteSQLWriter.Args(), argsForCache...) + if cacher := session.engine.GetCacher(tableNameNoQuote); cacher != nil && session.statement.UseCache { _ = session.cacheDelete(table, tableNameNoQuote, deleteSQLWriter.String(), argsForCache...) }