This repository has no description
1package db
2
3import (
4 "database/sql"
5 "errors"
6
7 "github.com/bluesky-social/indigo/atproto/syntax"
8)
9
10type Repo struct {
11 Knot string
12 Owner syntax.DID
13 Rkey syntax.RecordKey
14 RepoDid syntax.DID
15 CreatedAt string
16}
17
18func (d *DB) AddRepo(repo Repo) error {
19 var createdAt sql.NullString
20 if repo.CreatedAt != "" {
21 createdAt = sql.NullString{String: repo.CreatedAt, Valid: true}
22 }
23 _, err := d.Exec(
24 `insert into repos (knot, owner, rkey, repo_did, created_at)
25 values (?, ?, ?, ?, ?)
26 on conflict(owner, rkey) do update set
27 knot = excluded.knot,
28 repo_did = excluded.repo_did,
29 created_at = coalesce(excluded.created_at, repos.created_at)`,
30 repo.Knot, repo.Owner.String(), repo.Rkey.String(), repo.RepoDid.String(), createdAt,
31 )
32 return err
33}
34
35func (d *DB) CollapseRepoSiblings(owner, repoDid syntax.DID) (int64, error) {
36 res, err := d.Exec(
37 `delete from repos
38 where owner = ?
39 and repo_did = ?
40 and (
41 (created_at is null and exists (
42 select 1 from repos r2
43 where r2.owner = repos.owner
44 and r2.repo_did = repos.repo_did
45 and r2.created_at is not null
46 and r2.rkey <> repos.rkey
47 ))
48 or (created_at is not null and created_at < (
49 select max(created_at) from repos
50 where owner = ? and repo_did = ? and created_at is not null
51 ))
52 )`,
53 owner.String(), repoDid.String(), owner.String(), repoDid.String(),
54 )
55 if err != nil {
56 return 0, err
57 }
58 return res.RowsAffected()
59}
60
61func (d *DB) Knots() ([]string, error) {
62 rows, err := d.Query(`select distinct knot from repos`)
63 if err != nil {
64 return nil, err
65 }
66 defer rows.Close()
67
68 var knots []string
69 for rows.Next() {
70 var knot string
71 if err := rows.Scan(&knot); err != nil {
72 return nil, err
73 }
74 knots = append(knots, knot)
75 }
76
77 if err := rows.Err(); err != nil {
78 return nil, err
79 }
80
81 return knots, nil
82}
83
84func scanRepo(row interface{ Scan(...any) error }) (*Repo, error) {
85 var knot, owner, rkey, repoDid string
86 if err := row.Scan(&knot, &owner, &rkey, &repoDid); err != nil {
87 return nil, err
88 }
89 return &Repo{
90 Knot: knot,
91 Owner: syntax.DID(owner),
92 Rkey: syntax.RecordKey(rkey),
93 RepoDid: syntax.DID(repoDid),
94 }, nil
95}
96
97func (d *DB) SiblingRkeysForRepoDid(owner, repoDid syntax.DID, excludeRkey syntax.RecordKey) ([]string, error) {
98 rows, err := d.Query(
99 `select rkey from repos
100 where owner = ?
101 and coalesce(repo_did, '') = ?
102 and rkey <> ?`,
103 owner.String(), repoDid.String(), excludeRkey.String(),
104 )
105 if err != nil {
106 return nil, err
107 }
108 defer rows.Close()
109
110 var collect func(acc []string) ([]string, error)
111 collect = func(acc []string) ([]string, error) {
112 if !rows.Next() {
113 return acc, rows.Err()
114 }
115 var r string
116 if err := rows.Scan(&r); err != nil {
117 return acc, err
118 }
119 return collect(append(acc, r))
120 }
121 return collect(nil)
122}
123
124func (d *DB) GetRepoByDid(repoDid syntax.DID) (*Repo, error) {
125 return scanRepo(d.QueryRow(
126 `select knot, owner, rkey, repo_did from repos where repo_did = ?`,
127 repoDid.String(),
128 ))
129}
130
131func (d *DB) GetRepoByOwnerRkey(owner syntax.DID, rkey syntax.RecordKey) (*Repo, error) {
132 return scanRepo(d.QueryRow(
133 `select knot, owner, rkey, repo_did from repos where owner = ? and rkey = ?`,
134 owner.String(), rkey.String(),
135 ))
136}
137
138func (d *DB) AllRepos() ([]Repo, error) {
139 rows, err := d.Query(`select knot, owner, rkey, repo_did from repos`)
140 if err != nil {
141 return nil, err
142 }
143 defer rows.Close()
144
145 var repos []Repo
146 for rows.Next() {
147 r, err := scanRepo(rows)
148 if err != nil {
149 return nil, err
150 }
151 repos = append(repos, *r)
152 }
153
154 if err := rows.Err(); err != nil {
155 return nil, err
156 }
157
158 return repos, nil
159}
160
161func (d *DB) DeleteRepo(repoDid syntax.DID) error {
162 _, err := d.Exec(`delete from repos where repo_did = ?`, repoDid)
163 return err
164}
165
166// DeleteRepoByOwnerRkey deletes a repo by (owner,rkey) pair and returns deleted repos DID.
167func (d *DB) DeleteRepoByOwnerRkey(owner syntax.DID, rkey syntax.RecordKey) (syntax.DID, error) {
168 var repoDid string
169 err := d.QueryRow(
170 `delete from repos where owner = ? and rkey = ? returning repo_did`,
171 owner.String(), rkey.String(),
172 ).Scan(&repoDid)
173 if errors.Is(err, sql.ErrNoRows) {
174 return "", nil
175 }
176 if err != nil {
177 return "", err
178 }
179 return syntax.DID(repoDid), nil
180}