144 lines
3.5 KiB
Go
144 lines
3.5 KiB
Go
package dbr
|
|
|
|
import (
|
|
"bytes"
|
|
"database/sql"
|
|
"fmt"
|
|
"time"
|
|
)
|
|
|
|
// DeleteBuilder contains the clauses for a DELETE statement
|
|
type DeleteBuilder struct {
|
|
*Session
|
|
Runner
|
|
|
|
From string
|
|
WhereFragments []*whereFragment
|
|
OrderBys []string
|
|
LimitCount uint64
|
|
LimitValid bool
|
|
OffsetCount uint64
|
|
OffsetValid bool
|
|
}
|
|
|
|
// DeleteFrom creates a new DeleteBuilder for the given table
|
|
func (sess *Session) DeleteFrom(from string) *DeleteBuilder {
|
|
return &DeleteBuilder{
|
|
Session: sess,
|
|
Runner: sess.cxn.Db,
|
|
From: from,
|
|
}
|
|
}
|
|
|
|
// DeleteFrom creates a new DeleteBuilder for the given table
|
|
// in the context for a transaction
|
|
func (tx *Tx) DeleteFrom(from string) *DeleteBuilder {
|
|
return &DeleteBuilder{
|
|
Session: tx.Session,
|
|
Runner: tx.Tx,
|
|
From: from,
|
|
}
|
|
}
|
|
|
|
// Where appends a WHERE clause to the statement whereSqlOrMap can be a
|
|
// string or map. If it's a string, args wil replaces any places holders
|
|
func (b *DeleteBuilder) Where(whereSqlOrMap interface{}, args ...interface{}) *DeleteBuilder {
|
|
b.WhereFragments = append(b.WhereFragments, newWhereFragment(whereSqlOrMap, args))
|
|
return b
|
|
}
|
|
|
|
// OrderBy appends an ORDER BY clause to the statement
|
|
func (b *DeleteBuilder) OrderBy(ord string) *DeleteBuilder {
|
|
b.OrderBys = append(b.OrderBys, ord)
|
|
return b
|
|
}
|
|
|
|
// OrderDir appends an ORDER BY clause with a direction to the statement
|
|
func (b *DeleteBuilder) OrderDir(ord string, isAsc bool) *DeleteBuilder {
|
|
if isAsc {
|
|
b.OrderBys = append(b.OrderBys, ord+" ASC")
|
|
} else {
|
|
b.OrderBys = append(b.OrderBys, ord+" DESC")
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Limit sets a LIMIT clause for the statement; overrides any existing LIMIT
|
|
func (b *DeleteBuilder) Limit(limit uint64) *DeleteBuilder {
|
|
b.LimitCount = limit
|
|
b.LimitValid = true
|
|
return b
|
|
}
|
|
|
|
// Offset sets an OFFSET clause for the statement; overrides any existing OFFSET
|
|
func (b *DeleteBuilder) Offset(offset uint64) *DeleteBuilder {
|
|
b.OffsetCount = offset
|
|
b.OffsetValid = true
|
|
return b
|
|
}
|
|
|
|
// ToSql serialized the DeleteBuilder to a SQL string
|
|
// It returns the string with placeholders and a slice of query arguments
|
|
func (b *DeleteBuilder) ToSql() (string, []interface{}) {
|
|
if len(b.From) == 0 {
|
|
panic("no table specified")
|
|
}
|
|
|
|
var sql bytes.Buffer
|
|
var args []interface{}
|
|
|
|
sql.WriteString("DELETE FROM ")
|
|
sql.WriteString(b.From)
|
|
|
|
// Write WHERE clause if we have any fragments
|
|
if len(b.WhereFragments) > 0 {
|
|
sql.WriteString(" WHERE ")
|
|
writeWhereFragmentsToSql(b.WhereFragments, &sql, &args)
|
|
}
|
|
|
|
// Ordering and limiting
|
|
if len(b.OrderBys) > 0 {
|
|
sql.WriteString(" ORDER BY ")
|
|
for i, s := range b.OrderBys {
|
|
if i > 0 {
|
|
sql.WriteString(", ")
|
|
}
|
|
sql.WriteString(s)
|
|
}
|
|
}
|
|
|
|
if b.LimitValid {
|
|
sql.WriteString(" LIMIT ")
|
|
fmt.Fprint(&sql, b.LimitCount)
|
|
}
|
|
|
|
if b.OffsetValid {
|
|
sql.WriteString(" OFFSET ")
|
|
fmt.Fprint(&sql, b.OffsetCount)
|
|
}
|
|
|
|
return sql.String(), args
|
|
}
|
|
|
|
// Exec executes the statement represented by the DeleteBuilder
|
|
// It returns the raw database/sql Result and an error if there was one
|
|
func (b *DeleteBuilder) Exec() (sql.Result, error) {
|
|
sql, args := b.ToSql()
|
|
|
|
fullSql, err := Interpolate(sql, args)
|
|
if err != nil {
|
|
return nil, b.EventErrKv("dbr.delete.exec.interpolate", err, kvs{"sql": fullSql})
|
|
}
|
|
|
|
// Start the timer:
|
|
startTime := time.Now()
|
|
defer func() { b.TimingKv("dbr.delete", time.Since(startTime).Nanoseconds(), kvs{"sql": fullSql}) }()
|
|
|
|
result, err := b.Runner.Exec(fullSql)
|
|
if err != nil {
|
|
return result, b.EventErrKv("dbr.delete.exec.exec", err, kvs{"sql": fullSql})
|
|
}
|
|
|
|
return result, nil
|
|
}
|