1package sqlite_blockstore
2
3import (
4 "context"
5 "fmt"
6
7 "github.com/bluesky-social/indigo/atproto/syntax"
8 "github.com/haileyok/cocoon/internal/db"
9 "github.com/haileyok/cocoon/models"
10 blocks "github.com/ipfs/go-block-format"
11 "github.com/ipfs/go-cid"
12 "gorm.io/gorm/clause"
13)
14
15type SqliteBlockstore struct {
16 db *db.DB
17 did string
18 readonly bool
19 inserts map[cid.Cid]blocks.Block
20}
21
22func New(did string, db *db.DB) *SqliteBlockstore {
23 return &SqliteBlockstore{
24 did: did,
25 db: db,
26 readonly: false,
27 inserts: map[cid.Cid]blocks.Block{},
28 }
29}
30
31func NewReadOnly(did string, db *db.DB) *SqliteBlockstore {
32 return &SqliteBlockstore{
33 did: did,
34 db: db,
35 readonly: true,
36 inserts: map[cid.Cid]blocks.Block{},
37 }
38}
39
40func (bs *SqliteBlockstore) Get(ctx context.Context, cid cid.Cid) (blocks.Block, error) {
41 var block models.Block
42
43 maybeBlock, ok := bs.inserts[cid]
44 if ok {
45 return maybeBlock, nil
46 }
47
48 if err := bs.db.Raw("SELECT * FROM blocks WHERE did = ? AND cid = ?", nil, bs.did, cid.Bytes()).Scan(&block).Error; err != nil {
49 return nil, err
50 }
51
52 b, err := blocks.NewBlockWithCid(block.Value, cid)
53 if err != nil {
54 return nil, err
55 }
56
57 return b, nil
58}
59
60func (bs *SqliteBlockstore) Put(ctx context.Context, block blocks.Block) error {
61 bs.inserts[block.Cid()] = block
62
63 if bs.readonly {
64 return nil
65 }
66
67 b := models.Block{
68 Did: bs.did,
69 Cid: block.Cid().Bytes(),
70 Rev: syntax.NewTIDNow(0).String(), // TODO: WARN, this is bad. don't do this
71 Value: block.RawData(),
72 }
73
74 if err := bs.db.Create(&b, []clause.Expression{clause.OnConflict{
75 Columns: []clause.Column{{Name: "did"}, {Name: "cid"}},
76 UpdateAll: true,
77 }}).Error; err != nil {
78 return err
79 }
80
81 return nil
82}
83
84func (bs *SqliteBlockstore) DeleteBlock(context.Context, cid.Cid) error {
85 panic("not implemented")
86}
87
88func (bs *SqliteBlockstore) Has(context.Context, cid.Cid) (bool, error) {
89 panic("not implemented")
90}
91
92func (bs *SqliteBlockstore) GetSize(context.Context, cid.Cid) (int, error) {
93 panic("not implemented")
94}
95
96func (bs *SqliteBlockstore) PutMany(ctx context.Context, blocks []blocks.Block) error {
97 tx := bs.db.BeginDangerously()
98
99 for _, block := range blocks {
100 bs.inserts[block.Cid()] = block
101
102 if bs.readonly {
103 continue
104 }
105
106 b := models.Block{
107 Did: bs.did,
108 Cid: block.Cid().Bytes(),
109 Rev: syntax.NewTIDNow(0).String(), // TODO: WARN, this is bad. don't do this
110 Value: block.RawData(),
111 }
112
113 if err := tx.Clauses(clause.OnConflict{
114 Columns: []clause.Column{{Name: "did"}, {Name: "cid"}},
115 UpdateAll: true,
116 }).Create(&b).Error; err != nil {
117 tx.Rollback()
118 return err
119 }
120 }
121
122 if bs.readonly {
123 return nil
124 }
125
126 tx.Commit()
127
128 return nil
129}
130
131func (bs *SqliteBlockstore) AllKeysChan(ctx context.Context) (<-chan cid.Cid, error) {
132 return nil, fmt.Errorf("iteration not allowed on sqlite blockstore")
133}
134
135func (bs *SqliteBlockstore) HashOnRead(enabled bool) {
136 panic("not implemented")
137}