package database import ( "context" "database/sql" "strings" ) func AppendMany[T any](args []any, values []T) []any { for _, value := range values { args = append(args, value) } return args } // QueryMany executes a query and returns a slice of results. It is a small helper function reducing // the boilerplate code needed to execute a query and scan the results. The scanFunc is used to scan // each row into a result. func QueryMany[T any]( ctx context.Context, db *sql.DB, query string, args []any, scanFunc func(*sql.Rows) (T, error), ) ([]T, error) { rows, err := db.QueryContext(ctx, query, args...) if err != nil { return nil, err } defer rows.Close() var results []T for rows.Next() { result, err := scanFunc(rows) if err != nil { return nil, err } results = append(results, result) } return results, nil } // QueryOne executes a query and returns a single result. It is a small helper function reducing the // boilerplate code needed to execute a query and scan the results. The scanFunc is used to scan // the row into a result. func QueryOne[T any]( ctx context.Context, db *sql.DB, query string, args []any, scanFunc func(*sql.Rows) (T, error), ) (T, error) { rows, err := db.QueryContext(ctx, query, args...) if err != nil { return *new(T), err } defer rows.Close() if !rows.Next() { return *new(T), nil } return scanFunc(rows) } // Generates a string of placeholders for a SQL query. For example, if count is 3, the result will // be "?, ?, ?". func Placeholders(count int) string { placeholders := make([]string, count) for i := range placeholders { placeholders[i] = "?" } return strings.Join(placeholders, ", ") } type Condition struct { SQL string Value any Ok bool } // Takes a list of where strings and argument values together with a list of conditions. Those // conditions that are Ok will be added to the list of where strings and argument values. Useful // when having optional conditionals that used to filter a query. func Where(wheres []string, args []any, conditions ...Condition) ([]string, []any) { for _, v := range conditions { if v.Ok { wheres = append(wheres, v.SQL) args = append(args, v.Value) } } return wheres, args } // Takes a list of SQL condition snippets and joins them with AND. If the list is empty, returns // TRUE. func WhereSQL(wheres []string) string { where := "TRUE" if len(wheres) > 0 { where = strings.Join(wheres, " AND ") } return where }