stardb/batch.go

113 lines
3.4 KiB
Go
Raw Normal View History

2026-03-07 19:27:44 +08:00
package stardb
import (
"context"
"database/sql"
"fmt"
"reflect"
"strings"
)
// BatchInsert performs batch insert operation
// Usage: BatchInsert("users", []string{"name", "age"}, [][]interface{}{{"Alice", 25}, {"Bob", 30}})
func (s *StarDB) BatchInsert(tableName string, columns []string, values [][]interface{}) (sql.Result, error) {
return s.batchInsert(nil, tableName, columns, values)
}
// BatchInsertContext performs batch insert with context
func (s *StarDB) BatchInsertContext(ctx context.Context, tableName string, columns []string, values [][]interface{}) (sql.Result, error) {
return s.batchInsert(ctx, tableName, columns, values)
}
// batchInsert is the internal implementation
func (s *StarDB) batchInsert(ctx context.Context, tableName string, columns []string, values [][]interface{}) (sql.Result, error) {
if len(values) == 0 {
return nil, fmt.Errorf("no values to insert")
}
// Build placeholders: (?, ?), (?, ?), ...
placeholderGroup := "(" + strings.Repeat("?, ", len(columns)-1) + "?)"
placeholders := strings.Repeat(placeholderGroup+", ", len(values)-1) + placeholderGroup
// Build SQL
query := fmt.Sprintf("INSERT INTO %s (%s) VALUES %s",
tableName,
strings.Join(columns, ", "),
placeholders)
// Flatten values
var args []interface{}
for _, row := range values {
args = append(args, row...)
}
return s.exec(ctx, query, args...)
}
// BatchInsertStructs performs batch insert using structs
func (s *StarDB) BatchInsertStructs(tableName string, structs interface{}, autoIncrementFields ...string) (sql.Result, error) {
return s.batchInsertStructs(nil, tableName, structs, autoIncrementFields...)
}
// BatchInsertStructsContext performs batch insert using structs with context
func (s *StarDB) BatchInsertStructsContext(ctx context.Context, tableName string, structs interface{}, autoIncrementFields ...string) (sql.Result, error) {
return s.batchInsertStructs(ctx, tableName, structs, autoIncrementFields...)
}
// batchInsertStructs is the internal implementation
func (s *StarDB) batchInsertStructs(ctx context.Context, tableName string, structs interface{}, autoIncrementFields ...string) (sql.Result, error) {
// Get slice of structs
targetValue := reflect.ValueOf(structs)
if targetValue.Kind() == reflect.Ptr {
targetValue = targetValue.Elem()
}
if targetValue.Kind() != reflect.Slice && targetValue.Kind() != reflect.Array {
return nil, fmt.Errorf("structs must be a slice or array")
}
if targetValue.Len() == 0 {
return nil, fmt.Errorf("no structs to insert")
}
// Get field names from first struct
firstStruct := targetValue.Index(0).Interface()
fieldNames, err := getStructFieldNames(firstStruct, "db")
if err != nil {
return nil, err
}
// Filter out auto-increment fields
var columns []string
for _, fieldName := range fieldNames {
isAutoIncrement := false
for _, autoField := range autoIncrementFields {
if fieldName == autoField {
isAutoIncrement = true
break
}
}
if !isAutoIncrement {
columns = append(columns, fieldName)
}
}
// Extract values from all structs
var values [][]interface{}
for i := 0; i < targetValue.Len(); i++ {
structVal := targetValue.Index(i).Interface()
fieldValues, err := getStructFieldValues(structVal, "db")
if err != nil {
return nil, err
}
var row []interface{}
for _, col := range columns {
row = append(row, fieldValues[col])
}
values = append(values, row)
}
return s.batchInsert(ctx, tableName, columns, values)
}