package exec import ( "database/sql" "errors" "reflect" "git.gdulai.com/gdulai/simpleorm" "git.gdulai.com/gdulai/simpleorm/schema" log "gitlab.com/gdulai/simpleloglvl" ) // Insert represents an insert query operation. type Insert[T any] struct { target schema.Table toInsert []T results []T } // Creates a new insert operation for the given model type. // // Parameters: // - orm: The ORM instance that manages the database connection and schema // - toInsert: A variadic list of model instances to insert // // Returns: // - Insert[T]: A new insert operation instance // - error: An error if the table could not be found in the schema cache func NewInsert[T any](orm *simpleorm.ORM, toInsert ...T) (Insert[T], error) { table, ok := orm.Cache().Get(reflect.TypeFor[T]().Name()) if !ok { return Insert[T]{}, errors.New("Failed to get table from schema cache") } return Insert[T]{target: *table, toInsert: toInsert}, nil } // Returns the results of the insert operation. // // Returns: // - []T: A slice of model instances representing the inserted rows func (ins *Insert[T]) Results() []T { return ins.results } // Executes the insert operation using the provided database connection. // // Parameters: // - conn: The database connection to use for executing the query // // Returns: // - error: An error if the operation fails func (ins *Insert[T]) Execute(conn *simpleorm.DBConnection) error { return ins.execute(conn, nil) } // Executes the insert operation using the provided database connection or transaction. // // Parameters: // - conn: The database connection to use for executing the query // - tx: Optional transaction to use instead of a connection // // Returns: // - error: An error if the operation fails // // Implementation Details: // 1. Constructs the SQL INSERT statement using GetInsertDML // 2. Prepares parameters by flattening nested any values // 3. Executes the statement and maps results to model instances func (ins *Insert[T]) execute(conn *simpleorm.DBConnection, tx *sql.Tx) error { dml, err := ins.target.GetInsertDML(len(ins.toInsert)) if err != nil { return err } var params []any for i := range len(ins.toInsert) { actualParams := prepareParams(ins.toInsert[i], ins.target) if len(params) == 0 { params = make([]any, len(ins.toInsert)*len(actualParams)) } for j := range actualParams { params[(i*len(actualParams))+j] = actualParams[j] } } log.LogDebug("%s [%s]", dml, params) var stmt *sql.Stmt if tx != nil { stmt, err = tx.Prepare(dml) } else { stmt, err = conn.Prepare(dml) } defer stmt.Close() // Flattent args to make sure it can be parsed correctly var flatParams []any for _, param := range params { if s, ok := param.([]any); ok { flatParams = append(flatParams, s...) } else { flatParams = append(flatParams, param) } } rows, err := stmt.Query(flatParams...) if err != nil { return err } ins.results, err = readRows[T](ins.target, rows) if err != nil { return err } return nil }