bob/upsert.go

194 lines
5.4 KiB
Go
Raw Normal View History

2021-07-25 04:41:39 +00:00
package bob
import (
"bytes"
"errors"
"strings"
"github.com/lann/builder"
)
type UpsertBuilder builder.Builder
type upsertData struct {
2021-07-31 04:48:40 +00:00
Dialect int
Into string
Columns []string
Values [][]interface{}
Key []interface{}
Replace [][]interface{}
2021-07-25 04:41:39 +00:00
Placeholder string
}
func init() {
builder.Register(UpsertBuilder{}, upsertData{})
}
// dialect specifies database dialect used.
2021-07-25 06:47:52 +00:00
func (u UpsertBuilder) dialect(db int) UpsertBuilder {
return builder.Set(u, "Dialect", db).(UpsertBuilder)
2021-07-25 04:41:39 +00:00
}
// Table sets which table to be dropped.
func (u UpsertBuilder) into(name string) UpsertBuilder {
2021-07-25 06:47:52 +00:00
return builder.Set(u, "Into", name).(UpsertBuilder)
2021-07-25 04:41:39 +00:00
}
// Columns sets the columns for the data to be inserted.
2021-07-25 06:47:52 +00:00
func (u UpsertBuilder) Columns(columns ...string) UpsertBuilder {
return builder.Extend(u, "Columns", columns).(UpsertBuilder)
2021-07-25 04:41:39 +00:00
}
// Values sets the values in relation with the columns.
// Please not that only string, int, and bool type are supported.
// Inputting other types other than those might result in your SQL not working properly.
2021-07-25 06:47:52 +00:00
func (u UpsertBuilder) Values(values ...interface{}) UpsertBuilder {
return builder.Append(u, "Values", values).(UpsertBuilder)
2021-07-25 04:41:39 +00:00
}
// Key specifies which key to be checked on conflict.
// Must be used on PostgreSQL and SQLite.
2021-07-25 06:47:52 +00:00
func (u UpsertBuilder) Key(key ...interface{}) UpsertBuilder {
var value interface{}
column := key[0]
if len(key) > 1 && key[0] != nil {
value = key[1]
} else {
value = ""
}
return builder.Extend(u, "Key", []interface{}{column, value}).(UpsertBuilder)
2021-07-25 04:41:39 +00:00
}
// Replace sets the column and value respectively for the data to be changed on
2021-07-31 04:48:40 +00:00
// a specific row.
2021-07-25 06:47:52 +00:00
func (u UpsertBuilder) Replace(column interface{}, value interface{}) UpsertBuilder {
return builder.Append(u, "Replace", []interface{}{column, value}).(UpsertBuilder)
2021-07-25 04:41:39 +00:00
}
// PlaceholderFormat changes the default placeholder (?) to desired placeholder.
2021-07-25 06:47:52 +00:00
func (u UpsertBuilder) PlaceholderFormat(f string) UpsertBuilder {
return builder.Set(u, "Placeholder", f).(UpsertBuilder)
2021-07-25 04:41:39 +00:00
}
// ToSql returns 3 variables filled out with the correct values based on bindings, etc.
2021-07-25 06:47:52 +00:00
func (u UpsertBuilder) ToSql() (string, []interface{}, error) {
data := builder.GetStruct(u).(upsertData)
2021-07-25 04:41:39 +00:00
return data.ToSql()
}
// ToSql returns 3 variables filled out with the correct values based on bindings, etc.
func (d *upsertData) ToSql() (sqlStr string, args []interface{}, err error) {
2021-07-25 06:58:44 +00:00
if len(d.Into) == 0 || d.Into == "" {
2021-07-25 04:41:39 +00:00
err = errors.New("upsert statements must specify a table")
return
}
2021-07-25 06:58:44 +00:00
if len(d.Columns) == 0 || d.Columns[0] == "" {
2021-07-25 04:41:39 +00:00
err = errors.New("upsert statement must have at least one column")
return
}
if len(d.Values) == 0 {
err = errors.New("upsert statements must have at least one set of values")
return
}
if len(d.Replace) == 0 {
err = errors.New("upsert statement must have at least one key value pair to be replaced")
return
}
sql := &bytes.Buffer{}
2021-07-31 04:44:21 +00:00
if d.Dialect == MSSQL {
2021-07-25 04:41:39 +00:00
if len(d.Key) == 0 {
err = errors.New("unique key and value must be provided for MS SQL")
2021-07-25 06:58:44 +00:00
return
2021-07-25 04:41:39 +00:00
}
2021-07-31 04:48:40 +00:00
sql.WriteString("IF NOT EXISTS (SELECT * FROM \"" + d.Into + "\" WHERE \"" + d.Key[0].(string) + "\" = ?) ")
2021-07-25 04:41:39 +00:00
args = append(args, d.Key[1])
}
sql.WriteString("INSERT INTO ")
2021-07-31 04:48:40 +00:00
sql.WriteString("\"" + d.Into + "\"")
2021-07-25 04:41:39 +00:00
sql.WriteString(" ")
var columns []string
for _, v := range d.Columns {
columns = append(columns, "\""+v+"\"")
}
sql.WriteString("(")
sql.WriteString(strings.Join(columns, ", "))
sql.WriteString(") ")
sql.WriteString("VALUES ")
var values []string
for i := 0; i < len(d.Values); i++ {
var tempValues []string
for _, v := range d.Values[i] {
args = append(args, v)
tempValues = append(tempValues, "?")
}
values = append(values, "("+strings.Join(tempValues, ", ")+")")
}
2021-07-31 04:48:40 +00:00
2021-07-25 04:41:39 +00:00
sql.WriteString(strings.Join(values, ", "))
2021-07-25 06:47:52 +00:00
sql.WriteString(" ")
2021-07-25 04:41:39 +00:00
var replaces []string
for i := 0; i < len(d.Replace); i++ {
args = append(args, d.Replace[i][1])
replace := "\"" + d.Replace[i][0].(string) + "\" = ?"
replaces = append(replaces, replace)
}
2021-07-31 04:44:21 +00:00
if d.Dialect == MySQL {
2021-07-25 04:41:39 +00:00
// INSERT INTO table (col) VALUES (values) ON DUPLICATE KEY UPDATE col = value
2021-07-31 04:48:40 +00:00
2021-07-25 04:41:39 +00:00
sql.WriteString("ON DUPLICATE KEY UPDATE ")
sql.WriteString(strings.Join(replaces, ", "))
2021-07-31 04:44:21 +00:00
} else if d.Dialect == PostgreSQL || d.Dialect == SQLite {
2021-07-25 04:41:39 +00:00
// INSERT INTO players (user_name, age) VALUES('steven', 32) ON CONFLICT(user_name) DO UPDATE SET age=excluded.age;
2021-07-31 04:48:40 +00:00
2021-07-25 06:47:52 +00:00
if len(d.Key) == 0 {
err = errors.New("unique key must be provided for PostgreSQL and SQLite")
return
}
2021-07-25 04:41:39 +00:00
sql.WriteString("ON CONFLICT ")
2021-07-31 04:48:40 +00:00
sql.WriteString("(\"" + d.Key[0].(string) + "\") ")
2021-07-25 04:41:39 +00:00
sql.WriteString("DO UPDATE SET ")
sql.WriteString(strings.Join(replaces, ", "))
2021-07-31 04:44:21 +00:00
} else if d.Dialect == MSSQL {
2021-07-25 04:41:39 +00:00
// IF NOT EXISTS (SELECT * FROM dbo.Table1 WHERE ID = @ID)
2021-07-31 04:48:40 +00:00
// INSERT INTO dbo.Table1(ID, Name, ItemName, ItemCatName, ItemQty)
// VALUES(@ID, @Name, @ItemName, @ItemCatName, @ItemQty)
// ELSE
// UPDATE dbo.Table1
// SET Name = @Name,
// ItemName = @ItemName,
// ItemCatName = @ItemCatName,
// ItemQty = @ItemQty
// WHERE ID = @ID
2021-07-25 04:41:39 +00:00
sql.WriteString("ELSE ")
2021-07-31 04:48:40 +00:00
sql.WriteString("UPDATE \"" + d.Into + "\" SET ")
2021-07-25 04:41:39 +00:00
sql.WriteString(strings.Join(replaces, ", "))
2021-07-31 04:48:40 +00:00
sql.WriteString(" WHERE \"" + d.Key[0].(string) + "\" = ?")
2021-07-25 04:41:39 +00:00
args = append(args, d.Key[1])
} else {
err = ErrDialectNotSupported
return
}
2021-07-31 04:48:40 +00:00
2021-07-25 04:41:39 +00:00
sql.WriteString(";")
sqlStr = ReplacePlaceholder(sql.String(), d.Placeholder)
return
2021-07-31 04:48:40 +00:00
}