Files
bobiqq c231be0094
build-and-push / detect (push) Successful in 7s
build-and-push / build (${{ fromJSON(needs.detect.outputs.services) }}) (push) Failing after 3m33s
services
2026-08-26 11:04:23 +03:00

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)
}