1package cursor
2
3import (
4 "database/sql"
5 "fmt"
6
7 _ "github.com/mattn/go-sqlite3"
8)
9
10type SqliteStore struct {
11 db *sql.DB
12 tableName string
13}
14
15type SqliteStoreOpt func(*SqliteStore)
16
17func WithTableName(name string) SqliteStoreOpt {
18 return func(s *SqliteStore) {
19 s.tableName = name
20 }
21}
22
23func NewSQLiteStore(dbPath string, opts ...SqliteStoreOpt) (*SqliteStore, error) {
24 db, err := sql.Open("sqlite3", dbPath+"?_foreign_keys=1")
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
45func (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
55func (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
69func (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}