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