1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
16
17
18
19 package sqlite
20
21 import (
22 "database/sql"
23 "fmt"
24 "os"
25
26 _ "modernc.org/sqlite"
27
28 "go4.org/jsonconfig"
29 "go4.org/syncutil"
30 "perkeep.org/pkg/env"
31 "perkeep.org/pkg/sorted"
32 "perkeep.org/pkg/sorted/sqlkv"
33 )
34
35 func init() {
36 sorted.RegisterKeyValue("sqlite", newKeyValueFromConfig)
37 }
38
39
40
41 func NewStorage(file string) (sorted.KeyValue, error) {
42 return newKeyValueFromConfig(jsonconfig.Obj{"file": file})
43 }
44
45 func newKeyValueFromConfig(cfg jsonconfig.Obj) (sorted.KeyValue, error) {
46 file := cfg.RequiredString("file")
47 if err := cfg.Validate(); err != nil {
48 return nil, err
49 }
50
51 fi, err := os.Stat(file)
52 if os.IsNotExist(err) || (err == nil && fi.Size() == 0) {
53 if err := InitDB(file); err != nil {
54 return nil, fmt.Errorf("could not initialize sqlite DB at %s: %v", file, err)
55 }
56 }
57 db, err := sql.Open("sqlite", file)
58 if err != nil {
59 return nil, err
60 }
61 kv := &keyValue{
62 file: file,
63 db: db,
64 KeyValue: &sqlkv.KeyValue{
65 DB: db,
66 Gate: syncutil.NewGate(1),
67 },
68 }
69
70 version, err := kv.SchemaVersion()
71 if err != nil {
72 return nil, fmt.Errorf("error getting schema version (need to init database with 'camtool dbinit %s'?): %v", file, err)
73 }
74
75 if err := kv.ping(); err != nil {
76 return nil, err
77 }
78
79 if version != requiredSchemaVersion {
80 if env.IsDev() {
81
82
83 return nil, fmt.Errorf("database schema version is %d; expect %d (run \"devcam server --wipe\" to wipe both your blobs and re-populate the database schema)", version, requiredSchemaVersion)
84 }
85 return nil, fmt.Errorf("database schema version is %d; expect %d (need to re-init/upgrade database?)",
86 version, requiredSchemaVersion)
87 }
88
89 return kv, nil
90
91 }
92
93 type keyValue struct {
94 *sqlkv.KeyValue
95
96 file string
97 db *sql.DB
98 }
99
100 func (kv *keyValue) ping() error {
101
102 _, err := kv.SchemaVersion()
103 return err
104 }
105
106 func (kv *keyValue) SchemaVersion() (version int, err error) {
107 err = kv.db.QueryRow("SELECT value FROM meta WHERE metakey='version'").Scan(&version)
108 return
109 }