A website inspired by Last.fm that will keep track of your listening statistics
lastfm
music
statistics
1package database
2
3import (
4 "context"
5 "database/sql"
6 "strings"
7)
8
9func AppendMany[T any](args []any, values []T) []any {
10 for _, value := range values {
11 args = append(args, value)
12 }
13 return args
14}
15
16// QueryMany executes a query and returns a slice of results. It is a small helper function reducing
17// the boilerplate code needed to execute a query and scan the results. The scanFunc is used to scan
18// each row into a result.
19func QueryMany[T any](
20 ctx context.Context, db *sql.DB, query string, args []any, scanFunc func(*sql.Rows) (T, error),
21) ([]T, error) {
22 rows, err := db.QueryContext(ctx, query, args...)
23 if err != nil {
24 return nil, err
25 }
26 defer rows.Close()
27
28 var results []T
29 for rows.Next() {
30 result, err := scanFunc(rows)
31 if err != nil {
32 return nil, err
33 }
34 results = append(results, result)
35 }
36 return results, nil
37}
38
39// QueryOne executes a query and returns a single result. It is a small helper function reducing the
40// boilerplate code needed to execute a query and scan the results. The scanFunc is used to scan
41// the row into a result.
42func QueryOne[T any](
43 ctx context.Context, db *sql.DB, query string, args []any, scanFunc func(*sql.Rows) (T, error),
44) (T, error) {
45 rows, err := db.QueryContext(ctx, query, args...)
46 if err != nil {
47 return *new(T), err
48 }
49 defer rows.Close()
50
51 if !rows.Next() {
52 return *new(T), nil
53 }
54
55 return scanFunc(rows)
56}
57
58// Generates a string of placeholders for a SQL query. For example, if count is 3, the result will
59// be "?, ?, ?".
60func Placeholders(count int) string {
61 placeholders := make([]string, count)
62 for i := range placeholders {
63 placeholders[i] = "?"
64 }
65 return strings.Join(placeholders, ", ")
66}
67
68type Condition struct {
69 SQL string
70 Value any
71 Ok bool
72}
73
74// Takes a list of where strings and argument values together with a list of conditions. Those
75// conditions that are Ok will be added to the list of where strings and argument values. Useful
76// when having optional conditionals that used to filter a query.
77func Where(wheres []string, args []any, conditions ...Condition) ([]string, []any) {
78 for _, v := range conditions {
79 if v.Ok {
80 wheres = append(wheres, v.SQL)
81 args = append(args, v.Value)
82 }
83 }
84 return wheres, args
85}
86
87// Takes a list of SQL condition snippets and joins them with AND. If the list is empty, returns
88// TRUE.
89func WhereSQL(wheres []string) string {
90 where := "TRUE"
91 if len(wheres) > 0 {
92 where = strings.Join(wheres, " AND ")
93 }
94 return where
95}