@@ -12,10 +12,13 @@ import (
12
12
"strings"
13
13
)
14
14
15
+ // Array is an interface for small fixed-size arrays.
16
+ // It exists because generics can't specify array sizes: https://github.com/golang/go/issues/44253
15
17
type Array interface {
16
18
[1 ]any | [2 ]any | [3 ]any | [4 ]any | [5 ]any | [6 ]any | [7 ]any | [8 ]any | [9 ]any | [10 ]any | [11 ]any | [12 ]any | [13 ]any | [14 ]any | [15 ]any | [16 ]any | [17 ]any | [18 ]any | [19 ]any | [20 ]any
17
19
}
18
20
21
+ // MassInsertable represents a struct that contains dynamic values for a mass insert query.
19
22
type MassInsertable [T Array ] interface {
20
23
GetMassInsertValues () T
21
24
}
@@ -25,6 +28,48 @@ type MassInsertBuilder[Item MassInsertable[DynamicParams], StaticParams Array, D
25
28
placeholderTemplate string
26
29
}
27
30
31
+ // NewMassInsertBuilder creates a new MassInsertBuilder that can build mass insert database queries.
32
+ //
33
+ // Parameters in mass insert queries are split into two types: static parameters
34
+ // and dynamic parameters. Static parameters are the same for all items being
35
+ // inserted, while dynamic parameters are different for each item.
36
+ //
37
+ // The given query should be a normal INSERT query for a single row. It can also
38
+ // have ON CONFLICT clauses, as long as the clause uses `excluded` instead of
39
+ // positional parameters.
40
+ //
41
+ // The placeholder template is used to replace the `VALUES` part of the given
42
+ // query. It should contain a positional placeholder ($1, $2, ...) for each
43
+ // static placeholder, and a fmt directive (`$%d`) for each dynamic placeholder.
44
+ //
45
+ // The given query and placeholder template are validated here and the function
46
+ // will panic if they're invalid (e.g. if the `VALUES` part of the insert query
47
+ // can't be found, or if the placeholder template doesn't have the right things).
48
+ // The idea is to use this function to populate a global variable with the mass
49
+ // insert builder, so the panic will happen at startup if the query or
50
+ // placeholder template are invalid (instead of returning an error when trying
51
+ // to use the query later).
52
+ //
53
+ // Example:
54
+ //
55
+ // type Message struct {
56
+ // ChatID int
57
+ // RemoteID string
58
+ // MXID id.EventID
59
+ // Timestamp time.Time
60
+ // }
61
+ //
62
+ // func (msg *Message) GetMassInsertValues() [3]any {
63
+ // return [3]any{msg.RemoteID, msg.MXID, msg.Timestamp.UnixMilli()}
64
+ // }
65
+ //
66
+ // const insertMessageQuery = `INSERT INTO message (chat_id, remote_id, mxid, timestamp) VALUES ($1, $2, $3, $4)`
67
+ // var massInsertMessageBuilder = dbutil.NewMassInsertBuilder[Message, [2]any](insertMessageQuery, "($1, $%d, $%d, $%d, $%d)")
68
+ //
69
+ // func DoMassInsert(ctx context.Context, messages []*Message) error {
70
+ // query, params := massInsertMessageBuilder.Build([1]any{messages[0].ChatID}, messages)
71
+ // return db.Exec(ctx, query, params...)
72
+ // }
28
73
func NewMassInsertBuilder [Item MassInsertable [DynamicParams ], StaticParams Array , DynamicParams Array ](
29
74
singleInsertQuery , placeholderTemplate string ,
30
75
) * MassInsertBuilder [Item , StaticParams , DynamicParams ] {
0 commit comments