A vibe coded tangled fork which supports pijul.
1package db
2
3import (
4 "context"
5 "database/sql"
6 "errors"
7 "fmt"
8
9 "github.com/bluesky-social/indigo/atproto/syntax"
10 "tangled.org/core/appview/pagination"
11 "tangled.org/core/knotmirror/models"
12)
13
14func AddRepo(ctx context.Context, e *sql.DB, did syntax.DID, rkey syntax.RecordKey, cid syntax.CID, name, knot string) error {
15 if _, err := e.ExecContext(ctx,
16 `insert into repos (did, rkey, cid, name, knot_domain)
17 values ($1, $2, $3, $4, $5)`,
18 did, rkey, cid, name, knot,
19 ); err != nil {
20 return fmt.Errorf("inserting repo: %w", err)
21 }
22 return nil
23}
24
25func UpsertRepo(ctx context.Context, e *sql.DB, repo *models.Repo) error {
26 if _, err := e.ExecContext(ctx,
27 `insert into repos (did, rkey, cid, name, knot_domain, git_rev, repo_sha, state, error_msg, retry_count, retry_after)
28 values ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
29 on conflict(did, rkey) do update set
30 cid = excluded.cid,
31 name = excluded.name,
32 knot_domain = excluded.knot_domain,
33 git_rev = excluded.git_rev,
34 repo_sha = excluded.repo_sha,
35 state = excluded.state,
36 error_msg = excluded.error_msg,
37 retry_count = excluded.retry_count,
38 retry_after = excluded.retry_after`,
39 // where repos.cid != excluded.cid`,
40 repo.Did,
41 repo.Rkey,
42 repo.Cid,
43 repo.Name,
44 repo.KnotDomain,
45 repo.GitRev,
46 repo.RepoSha,
47 repo.State,
48 repo.ErrorMsg,
49 repo.RetryCount,
50 repo.RetryAfter,
51 ); err != nil {
52 return fmt.Errorf("upserting repo: %w", err)
53 }
54 return nil
55}
56
57func UpdateRepoState(ctx context.Context, e *sql.DB, did syntax.DID, rkey syntax.RecordKey, state models.RepoState) error {
58 if _, err := e.ExecContext(ctx,
59 `update repos
60 set state = $1
61 where did = $2 and rkey = $3`,
62 state,
63 did, rkey,
64 ); err != nil {
65 return fmt.Errorf("updating repo: %w", err)
66 }
67 return nil
68}
69
70func DeleteRepo(ctx context.Context, e *sql.DB, did syntax.DID, rkey syntax.RecordKey) error {
71 if _, err := e.ExecContext(ctx,
72 `delete from repos where did = $1 and rkey = $2`,
73 did,
74 rkey,
75 ); err != nil {
76 return fmt.Errorf("deleting repo: %w", err)
77 }
78 return nil
79}
80
81func GetRepoByName(ctx context.Context, e *sql.DB, did syntax.DID, name string) (*models.Repo, error) {
82 var repo models.Repo
83 if err := e.QueryRowContext(ctx,
84 `select
85 did,
86 rkey,
87 cid,
88 name,
89 knot_domain,
90 git_rev,
91 repo_sha,
92 state,
93 error_msg,
94 retry_count,
95 retry_after
96 from repos
97 where did = $1 and name = $2`,
98 did,
99 name,
100 ).Scan(
101 &repo.Did,
102 &repo.Rkey,
103 &repo.Cid,
104 &repo.Name,
105 &repo.KnotDomain,
106 &repo.GitRev,
107 &repo.RepoSha,
108 &repo.State,
109 &repo.ErrorMsg,
110 &repo.RetryCount,
111 &repo.RetryAfter,
112 ); err != nil {
113 if errors.Is(err, sql.ErrNoRows) {
114 return nil, nil
115 }
116 return nil, fmt.Errorf("querying repo: %w", err)
117 }
118 return &repo, nil
119}
120
121func GetRepoByAtUri(ctx context.Context, e *sql.DB, aturi syntax.ATURI) (*models.Repo, error) {
122 var repo models.Repo
123 if err := e.QueryRowContext(ctx,
124 `select
125 did,
126 rkey,
127 cid,
128 name,
129 knot_domain,
130 git_rev,
131 repo_sha,
132 state,
133 error_msg,
134 retry_count,
135 retry_after
136 from repos
137 where at_uri = $1`,
138 aturi,
139 ).Scan(
140 &repo.Did,
141 &repo.Rkey,
142 &repo.Cid,
143 &repo.Name,
144 &repo.KnotDomain,
145 &repo.GitRev,
146 &repo.RepoSha,
147 &repo.State,
148 &repo.ErrorMsg,
149 &repo.RetryCount,
150 &repo.RetryAfter,
151 ); err != nil {
152 if errors.Is(err, sql.ErrNoRows) {
153 return nil, nil
154 }
155 return nil, fmt.Errorf("querying repo: %w", err)
156 }
157 return &repo, nil
158}
159
160func ListRepos(ctx context.Context, e *sql.DB, page pagination.Page, did, knot, state string) ([]models.Repo, error) {
161 var conditions []string
162 var args []any
163
164 pageClause := ""
165 if page.Limit > 0 {
166 pageClause = " limit $1 offset $2 "
167 args = append(args, page.Limit, page.Offset)
168 }
169
170 whereClause := ""
171 if did != "" {
172 conditions = append(conditions, fmt.Sprintf("did = $%d", len(args)+1))
173 args = append(args, did)
174 }
175 if knot != "" {
176 conditions = append(conditions, fmt.Sprintf("knot_domain = $%d", len(args)+1))
177 args = append(args, knot)
178 }
179 if state != "" {
180 conditions = append(conditions, fmt.Sprintf("state = $%d", len(args)+1))
181 args = append(args, state)
182 }
183 if len(conditions) > 0 {
184 whereClause = "WHERE " + conditions[0]
185 for _, condition := range conditions[1:] {
186 whereClause += " AND " + condition
187 }
188 }
189
190 query := `
191 select
192 did,
193 rkey,
194 cid,
195 name,
196 knot_domain,
197 git_rev,
198 repo_sha,
199 state,
200 error_msg,
201 retry_count,
202 retry_after
203 from repos
204 ` + whereClause + pageClause
205 rows, err := e.QueryContext(ctx, query, args...)
206 if err != nil {
207 return nil, err
208 }
209 defer rows.Close()
210
211 var repos []models.Repo
212 for rows.Next() {
213 var repo models.Repo
214 if err := rows.Scan(
215 &repo.Did,
216 &repo.Rkey,
217 &repo.Cid,
218 &repo.Name,
219 &repo.KnotDomain,
220 &repo.GitRev,
221 &repo.RepoSha,
222 &repo.State,
223 &repo.ErrorMsg,
224 &repo.RetryCount,
225 &repo.RetryAfter,
226 ); err != nil {
227 return nil, fmt.Errorf("scanning row: %w", err)
228 }
229 repos = append(repos, repo)
230 }
231 if err := rows.Err(); err != nil {
232 return nil, fmt.Errorf("scanning rows: %w ", err)
233 }
234
235 return repos, nil
236}
237
238func GetRepoCountsByState(ctx context.Context, e *sql.DB) (map[models.RepoState]int64, error) {
239 const q = `
240 SELECT state, COUNT(*)
241 FROM repos
242 GROUP BY state
243 `
244
245 rows, err := e.QueryContext(ctx, q)
246 if err != nil {
247 return nil, err
248 }
249 defer rows.Close()
250
251 counts := make(map[models.RepoState]int64)
252
253 for rows.Next() {
254 var state string
255 var count int64
256
257 if err := rows.Scan(&state, &count); err != nil {
258 return nil, err
259 }
260
261 counts[models.RepoState(state)] = count
262 }
263
264 if err := rows.Err(); err != nil {
265 return nil, err
266 }
267
268 for _, s := range models.AllRepoStates {
269 if _, ok := counts[s]; !ok {
270 counts[s] = 0
271 }
272 }
273
274 return counts, nil
275}