+83
knotclient/cursor/sqlite.go
+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
+
}