Monorepo for Tangled tangled.org

knotclient: introduce sqlite3 based cursor store

Signed-off-by: oppiliappan <me@oppi.li>

oppi.li fe8c96f0 60444e90

verified
Changed files
+83
knotclient
cursor
+83
knotclient/cursor/sqlite.go
··· 1 + package cursor 2 + 3 + import ( 4 + "database/sql" 5 + "fmt" 6 + 7 + _ "github.com/mattn/go-sqlite3" 8 + ) 9 + 10 + type SqliteStore struct { 11 + db *sql.DB 12 + tableName string 13 + } 14 + 15 + type SqliteStoreOpt func(*SqliteStore) 16 + 17 + func WithTableName(name string) SqliteStoreOpt { 18 + return func(s *SqliteStore) { 19 + s.tableName = name 20 + } 21 + } 22 + 23 + func NewSQLiteStore(dbPath string, opts ...SqliteStoreOpt) (*SqliteStore, error) { 24 + db, err := sql.Open("sqlite3", dbPath) 25 + if err != nil { 26 + return nil, fmt.Errorf("failed to open sqlite database: %w", err) 27 + } 28 + 29 + store := &SqliteStore{ 30 + db: db, 31 + tableName: "cursors", 32 + } 33 + 34 + for _, o := range opts { 35 + o(store) 36 + } 37 + 38 + if err := store.init(); err != nil { 39 + return nil, err 40 + } 41 + 42 + return store, nil 43 + } 44 + 45 + func (s *SqliteStore) init() error { 46 + createTable := fmt.Sprintf(` 47 + create table if not exists %s ( 48 + knot text primary key, 49 + cursor text 50 + );`, s.tableName) 51 + _, err := s.db.Exec(createTable) 52 + return err 53 + } 54 + 55 + func (s *SqliteStore) Set(knot string, cursor int64) { 56 + query := fmt.Sprintf(` 57 + insert into %s (knot, cursor) 58 + values (?, ?) 59 + on conflict(knot) do update set cursor=excluded.cursor; 60 + `, s.tableName) 61 + 62 + _, err := s.db.Exec(query, knot, cursor) 63 + 64 + if err != nil { 65 + // TODO: log here 66 + } 67 + } 68 + 69 + func (s *SqliteStore) Get(knot string) (cursor int64) { 70 + query := fmt.Sprintf(` 71 + select cursor from %s where knot = ?; 72 + `, s.tableName) 73 + err := s.db.QueryRow(query, knot).Scan(&cursor) 74 + 75 + if err != nil { 76 + if err != sql.ErrNoRows { 77 + // TODO: log here 78 + } 79 + return 0 80 + } 81 + 82 + return cursor 83 + }