ksql/kbuilder/insert.go

109 lines
2.6 KiB
Go

package kbuilder
import (
"fmt"
"reflect"
"strings"
"github.com/vingarcia/ksql"
"github.com/vingarcia/ksql/internal/structs"
)
// Insert is the struct template for building INSERT queries
type Insert struct {
// Into expects a table name, e.g. "users"
Into string
// Data expected either a single record annotated with `ksql` tags
// or a list of records annotated likewise.
Data interface{}
}
// Build is a utility function for finding the dialect based on the driver and
// then calling BuildQuery(dialect)
func (i Insert) Build(driver string) (sqlQuery string, params []interface{}, _ error) {
dialect, err := ksql.GetDriverDialect(driver)
if err != nil {
return "", nil, err
}
return i.BuildQuery(dialect)
}
// BuildQuery implements the queryBuilder interface
func (i Insert) BuildQuery(dialect ksql.Dialect) (sqlQuery string, params []interface{}, _ error) {
var b strings.Builder
b.WriteString("INSERT INTO " + dialect.Escape(i.Into))
if i.Into == "" {
return "", nil, fmt.Errorf(
"expected the Into attr to contain the tablename, but got an empty string instead",
)
}
if i.Data == nil {
return "", nil, fmt.Errorf(
"expected the Data attr to contain a struct or a list of structs, but got `%v`",
i.Data,
)
}
v := reflect.ValueOf(i.Data)
t := v.Type()
if t.Kind() != reflect.Slice {
// Convert it to a slice of a single element:
v = reflect.Append(reflect.MakeSlice(reflect.SliceOf(t), 0, 1), v)
} else {
t = t.Elem()
}
if v.Len() == 0 {
return "", nil, fmt.Errorf(
"can't create an insertion query from an empty list of values",
)
}
isPtr := false
if t.Kind() == reflect.Ptr {
isPtr = true
t = t.Elem()
}
if t.Kind() != reflect.Struct {
return "", nil, fmt.Errorf("expected Data attr to be a struct or slice of structs but got: %v", t)
}
info, err := structs.GetTagInfo(t)
if err != nil {
return "", nil, err
}
b.WriteString(" (")
var escapedNames []string
for i := 0; i < info.NumFields(); i++ {
name := info.ByIndex(i).ColumnName
escapedNames = append(escapedNames, dialect.Escape(name))
}
b.WriteString(strings.Join(escapedNames, ", "))
b.WriteString(") VALUES ")
params = []interface{}{}
values := []string{}
for i := 0; i < v.Len(); i++ {
record := v.Index(i)
if isPtr {
record = record.Elem()
}
placeholders := []string{}
for j := 0; j < info.NumFields(); j++ {
placeholders = append(placeholders, dialect.Placeholder(len(params)))
params = append(params, record.Field(j).Interface())
}
values = append(values, "("+strings.Join(placeholders, ", ")+")")
}
b.WriteString(strings.Join(values, ", "))
return b.String(), params, nil
}