This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / spindle / db / repos.go
4.3 kB 180 lines
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}