239 lines
5.2 KiB
Go
239 lines
5.2 KiB
Go
package dbr
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"reflect"
|
|
"strings"
|
|
)
|
|
|
|
// InsertStmt builds `INSERT INTO ...`.
|
|
type InsertStmt struct {
|
|
runner
|
|
EventReceiver
|
|
Dialect
|
|
|
|
raw
|
|
|
|
Table string
|
|
Column []string
|
|
Value [][]interface{}
|
|
ReturnColumn []string
|
|
RecordID *int64
|
|
comments Comments
|
|
}
|
|
|
|
type InsertBuilder = InsertStmt
|
|
|
|
func (b *InsertStmt) Build(d Dialect, buf Buffer) error {
|
|
if b.raw.Query != "" {
|
|
return b.raw.Build(d, buf)
|
|
}
|
|
|
|
if b.Table == "" {
|
|
return ErrTableNotSpecified
|
|
}
|
|
|
|
if len(b.Column) == 0 {
|
|
return ErrColumnNotSpecified
|
|
}
|
|
|
|
err := b.comments.Build(d, buf)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
buf.WriteString("INSERT INTO ")
|
|
buf.WriteString(d.QuoteIdent(b.Table))
|
|
|
|
var placeholderBuf strings.Builder
|
|
placeholderBuf.WriteString("(")
|
|
buf.WriteString(" (")
|
|
for i, col := range b.Column {
|
|
if i > 0 {
|
|
buf.WriteString(",")
|
|
placeholderBuf.WriteString(",")
|
|
}
|
|
buf.WriteString(d.QuoteIdent(col))
|
|
placeholderBuf.WriteString(placeholder)
|
|
}
|
|
buf.WriteString(") VALUES ")
|
|
placeholderBuf.WriteString(")")
|
|
placeholderStr := placeholderBuf.String()
|
|
|
|
for i, tuple := range b.Value {
|
|
if i > 0 {
|
|
buf.WriteString(", ")
|
|
}
|
|
buf.WriteString(placeholderStr)
|
|
|
|
buf.WriteValue(tuple...)
|
|
}
|
|
|
|
if len(b.ReturnColumn) > 0 {
|
|
buf.WriteString(" RETURNING ")
|
|
for i, col := range b.ReturnColumn {
|
|
if i > 0 {
|
|
buf.WriteString(",")
|
|
}
|
|
buf.WriteString(d.QuoteIdent(col))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// InsertInto creates an InsertStmt.
|
|
func InsertInto(table string) *InsertStmt {
|
|
return &InsertStmt{
|
|
Table: table,
|
|
}
|
|
}
|
|
|
|
// InsertInto creates an InsertStmt.
|
|
func (sess *Session) InsertInto(table string) *InsertStmt {
|
|
b := InsertInto(table)
|
|
b.runner = sess
|
|
b.EventReceiver = sess.EventReceiver
|
|
b.Dialect = sess.Dialect
|
|
return b
|
|
}
|
|
|
|
// InsertInto creates an InsertStmt.
|
|
func (tx *Tx) InsertInto(table string) *InsertStmt {
|
|
b := InsertInto(table)
|
|
b.runner = tx
|
|
b.EventReceiver = tx.EventReceiver
|
|
b.Dialect = tx.Dialect
|
|
return b
|
|
}
|
|
|
|
// InsertBySql creates an InsertStmt from raw query.
|
|
func InsertBySql(query string, value ...interface{}) *InsertStmt {
|
|
return &InsertStmt{
|
|
raw: raw{
|
|
Query: query,
|
|
Value: value,
|
|
},
|
|
}
|
|
}
|
|
|
|
// InsertBySql creates an InsertStmt from raw query.
|
|
func (sess *Session) InsertBySql(query string, value ...interface{}) *InsertStmt {
|
|
b := InsertBySql(query, value...)
|
|
b.runner = sess
|
|
b.EventReceiver = sess.EventReceiver
|
|
b.Dialect = sess.Dialect
|
|
return b
|
|
}
|
|
|
|
// InsertBySql creates an InsertStmt from raw query.
|
|
func (tx *Tx) InsertBySql(query string, value ...interface{}) *InsertStmt {
|
|
b := InsertBySql(query, value...)
|
|
b.runner = tx
|
|
b.EventReceiver = tx.EventReceiver
|
|
b.Dialect = tx.Dialect
|
|
return b
|
|
}
|
|
|
|
func (b *InsertStmt) Columns(column ...string) *InsertStmt {
|
|
b.Column = column
|
|
return b
|
|
}
|
|
|
|
// Comment adds a comment to prepended. All multi-line sql comment characters are stripped
|
|
func (b *InsertStmt) Comment(comment string) *InsertStmt {
|
|
b.comments = b.comments.Append(comment)
|
|
return b
|
|
}
|
|
|
|
// Values adds a tuple to be inserted.
|
|
// The order of the tuple should match Columns.
|
|
func (b *InsertStmt) Values(value ...interface{}) *InsertStmt {
|
|
b.Value = append(b.Value, value)
|
|
return b
|
|
}
|
|
|
|
// Record adds a tuple for columns from a struct.
|
|
//
|
|
// If there is a field called "Id" or "ID" in the struct,
|
|
// it will be set to LastInsertId.
|
|
func (b *InsertStmt) Record(structValue interface{}) *InsertStmt {
|
|
v := reflect.Indirect(reflect.ValueOf(structValue))
|
|
|
|
if v.Kind() == reflect.Struct {
|
|
found := make([]interface{}, len(b.Column)+1)
|
|
// ID is recommended by golint here
|
|
s := newTagStore()
|
|
s.findValueByName(v, append(b.Column, "id"), found, false)
|
|
|
|
value := found[:len(found)-1]
|
|
for i, v := range value {
|
|
if v != nil {
|
|
value[i] = v.(reflect.Value).Interface()
|
|
}
|
|
}
|
|
|
|
if v.CanSet() {
|
|
switch idField := found[len(found)-1].(type) {
|
|
case reflect.Value:
|
|
if idField.Kind() == reflect.Int64 {
|
|
b.RecordID = idField.Addr().Interface().(*int64)
|
|
}
|
|
}
|
|
}
|
|
b.Values(value...)
|
|
}
|
|
return b
|
|
}
|
|
|
|
// Returning specifies the returning columns for postgres.
|
|
func (b *InsertStmt) Returning(column ...string) *InsertStmt {
|
|
b.ReturnColumn = column
|
|
return b
|
|
}
|
|
|
|
// Pair adds (column, value) to be inserted.
|
|
// It is an error to mix Pair with Values and Record.
|
|
func (b *InsertStmt) Pair(column string, value interface{}) *InsertStmt {
|
|
b.Column = append(b.Column, column)
|
|
switch len(b.Value) {
|
|
case 0:
|
|
b.Values(value)
|
|
case 1:
|
|
b.Value[0] = append(b.Value[0], value)
|
|
default:
|
|
panic("pair only allows one record to insert")
|
|
}
|
|
return b
|
|
}
|
|
|
|
func (b *InsertStmt) Exec() (sql.Result, error) {
|
|
return b.ExecContext(context.Background())
|
|
}
|
|
|
|
func (b *InsertStmt) ExecContext(ctx context.Context) (sql.Result, error) {
|
|
result, err := exec(ctx, b.runner, b.EventReceiver, b, b.Dialect)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if b.RecordID != nil {
|
|
if id, err := result.LastInsertId(); err == nil {
|
|
*b.RecordID = id
|
|
}
|
|
b.RecordID = nil
|
|
}
|
|
|
|
return result, nil
|
|
}
|
|
|
|
func (b *InsertStmt) LoadContext(ctx context.Context, value interface{}) error {
|
|
_, err := query(ctx, b.runner, b.EventReceiver, b, b.Dialect, value)
|
|
return err
|
|
}
|
|
|
|
func (b *InsertStmt) Load(value interface{}) error {
|
|
return b.LoadContext(context.Background(), value)
|
|
}
|