add bulk write function
This commit is contained in:
parent
8da21b1024
commit
d6ac97d15b
|
@ -0,0 +1,142 @@
|
|||
package postgresql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/volatiletech/sqlboiler/v4/boil"
|
||||
|
||||
"github.com/Selly-Modules/logger"
|
||||
)
|
||||
|
||||
const (
|
||||
indexSign = "$%d"
|
||||
decodeTag = "boil"
|
||||
)
|
||||
|
||||
type bulkInsertItem map[string]interface{}
|
||||
|
||||
type BulkInsertPayload struct {
|
||||
TableName string
|
||||
Data interface{}
|
||||
Columns []string
|
||||
}
|
||||
|
||||
func BulkInsert(ctx context.Context, db boil.ContextExecutor, payload BulkInsertPayload) error {
|
||||
// convert data to map
|
||||
insertRows, err := toMap(payload.Data)
|
||||
if err != nil {
|
||||
logger.Error("convert data to map", logger.LogData{
|
||||
Source: "module.postgresql.bulk_insert.BulkInsert",
|
||||
Message: err.Error(),
|
||||
Data: payload.Data,
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
numOfColumns = len(payload.Columns)
|
||||
incSigns = make([]string, 0)
|
||||
insertValues = make([]interface{}, 0)
|
||||
listOfSigns = make([]string, numOfColumns)
|
||||
listOfColumns = make([]string, numOfColumns)
|
||||
)
|
||||
|
||||
// prepare array of dollar signs
|
||||
for i := range listOfSigns {
|
||||
listOfSigns[i] = indexSign
|
||||
listOfColumns[i] = payload.Columns[i]
|
||||
}
|
||||
|
||||
// prepare sign for every column
|
||||
signs := strings.Join(listOfSigns, ",")
|
||||
insertColumns := strings.Join(listOfColumns, ",")
|
||||
|
||||
for index, r := range insertRows {
|
||||
currentIncSigns := getIncSignValues(index, numOfColumns)
|
||||
incSigns = append(incSigns, fmt.Sprintf("("+signs+")",
|
||||
currentIncSigns...,
|
||||
))
|
||||
|
||||
currentInsertValues := getInsertValues(r, payload.Columns, numOfColumns)
|
||||
insertValues = append(insertValues, currentInsertValues...)
|
||||
}
|
||||
|
||||
// exec
|
||||
stm := getSQLStatement(payload.TableName, insertColumns, incSigns)
|
||||
_, err = db.ExecContext(ctx, stm, insertValues...)
|
||||
if err != nil {
|
||||
logger.Error("insert to db", logger.LogData{
|
||||
Source: "module.postgresql.bulk_insert.BulkInsert",
|
||||
Message: err.Error(),
|
||||
Data: logger.Map{"statement": stm},
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getSQLStatement(tableName, insertColumns string, incSigns []string) string {
|
||||
return fmt.Sprintf(`
|
||||
INSERT INTO %s (
|
||||
%s
|
||||
)
|
||||
VALUES %s
|
||||
`, tableName, insertColumns, strings.Join(incSigns, ","))
|
||||
}
|
||||
|
||||
func getIncSignValues(index, numOfColumns int) (result []interface{}) {
|
||||
for i := 1; i <= numOfColumns; i++ {
|
||||
result = append(result, index*numOfColumns+i)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func getInsertValues(row bulkInsertItem, columns []string, numOfColumns int) (result []interface{}) {
|
||||
for i := 0; i < numOfColumns; i++ {
|
||||
result = append(result, row[columns[i]])
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func toMap(input interface{}) ([]bulkInsertItem, error) {
|
||||
result := make([]bulkInsertItem, 0)
|
||||
|
||||
v := reflect.ValueOf(input)
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// only accept slices
|
||||
if v.Kind() != reflect.Slice {
|
||||
err := fmt.Errorf("toMap only accepts slices; got %T", v)
|
||||
logger.Error("invalid type", logger.LogData{
|
||||
Source: "external.postgresql.bulk_insert.toMap",
|
||||
Message: err.Error(),
|
||||
Data: nil,
|
||||
})
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// loop and assign data to result
|
||||
for x := 0; x < v.Len(); x++ {
|
||||
item := bulkInsertItem{}
|
||||
typ := v.Index(x).Type()
|
||||
|
||||
// loop each field
|
||||
for i := 0; i < v.Index(x).NumField(); i++ {
|
||||
fi := typ.Field(i)
|
||||
t := fi.Tag.Get(decodeTag)
|
||||
if t != "" {
|
||||
// set key of map to value in struct field
|
||||
item[t] = v.Index(x).Field(i).Interface()
|
||||
}
|
||||
}
|
||||
result = append(result, item)
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
Loading…
Reference in New Issue