113 lines
3.4 KiB
Go
113 lines
3.4 KiB
Go
|
|
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)
|
||
|
|
}
|