mirror of https://github.com/VinGarcia/ksql.git
109 lines
2.6 KiB
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
|
|
}
|