forked from dougt/whereismyfox
/
db.go
208 lines (164 loc) · 4.53 KB
/
db.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
package main
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
type Command struct {
Id int64 `json: "id"`
Name string `json: "name"`
Description string `json: "description"`
}
type Device struct {
User string
Id int64 `json: "id"`
Name string `json: "name"`
Endpoint string `json: "endpoint"`
Latitude float64 `json: "latitude"`
Longitude float64 `json: "longitude"`
Timestamp string `json: "timestamp"`
}
type DB struct {
connection *sql.DB
}
func OpenDB(dbpath string) (*DB, error) {
conn, err := sql.Open("sqlite3", dbpath)
if err != nil {
return nil, err
}
_, err = conn.Exec(
`create table if not exists devices
(id integer primary key autoincrement,
user text, name text, endpoint text unique,
latitude float default 0, longitude float default 0,
timestamp text default "");`)
if err != nil {
return nil, err
}
_, err = conn.Exec(
`drop table if exists commands`)
if err != nil {
return nil, err
}
_, err = conn.Exec(
`create table commands
(id integer primary key, name text, description text,
unique (id, name, description))`)
if err != nil {
return nil, err
}
// FIXME sqlite3 seems to need a special pragma to enforce
// foreign keys, so the go-sql abstraction will have to leak
// here somehow.
// https://www.sqlite.org/foreignkeys.html#fk_enable
_, err = conn.Exec(
`create table if not exists commands_for_device
(device_id integer references devices(id),
command_id integer references commands(id),
primary key (device_id, command_id))`)
if err != nil {
return nil, err
}
return &DB{conn}, nil
}
func (self DB) Close() {
self.connection.Close()
self.connection = nil
}
func (self DB) AddDevice(user, name, endpoint string) (*Device, error) {
res, err := self.connection.Exec(
`insert into devices(user, name, endpoint) values(?, ?, ?)`,
user, name, endpoint)
if err != nil {
return nil, err
}
id, _ := res.LastInsertId()
return &Device{Id: id, Name: name, User: user, Endpoint: endpoint}, nil
}
func (self DB) AddCommand(id int64, name, description string) (*Command, error) {
_, err := self.connection.Exec(
`insert into commands(id, name, description) values(?, ?, ?)`,
id, name, description)
if err != nil {
return nil, err
}
return &Command{Id: id, Name: name, Description: description}, nil
}
func (self DB) AddCommandForDevice(device, command int64) error {
_, err := self.connection.Exec(
`insert into commands_for_device(device_id, command_id)
values(?, ?)`, device, command)
return err
}
func (self DB) UpdateCommandsForDevice(device int64, commands []int64) error {
_, err := self.connection.Exec(
`delete from commands_for_device where device_id=?`, device)
if err != nil {
return err
}
// FIXME should be a transaction?
for _, cmdid := range commands {
if err = self.AddCommandForDevice(device, cmdid); err != nil {
return err
}
}
return nil
}
func (self DB) GetDeviceById(id int64) (*Device, error) {
row := self.connection.QueryRow(
`select id, user, name, endpoint, latitude, longitude, timestamp
from devices where id=?`, id)
d := Device{}
err := row.Scan(
&d.Id, &d.User, &d.Name,
&d.Endpoint, &d.Latitude,
&d.Longitude, &d.Timestamp)
if err != nil {
return nil, err
}
return &d, nil
}
func (self DB) ListCommandsForDevice(d *Device) ([]*Command, error) {
res, err := self.connection.Query(
`select id, name, description
from (commands, commands_for_device)
where commands.id = commands_for_device.command_id
and commands_for_device.device_id=?`, d.Id)
if err != nil {
return nil, err
}
commands := []*Command{}
for res.Next() {
c := Command{}
err = res.Scan(&c.Id, &c.Name, &c.Description)
if err != nil {
return nil, err
}
commands = append(commands, &c)
}
return commands, nil
}
func (self DB) UpdateDeviceLocation(device *Device, latitude, longitude float64) error {
_, err := self.connection.Exec(
`update devices set latitude=?, longitude=?, timestamp=strftime('%s', 'now')
where id=?`, latitude, longitude, device.Id)
return err
}
func (self DB) ListDevicesForUser(user string) ([]Device, error) {
res, err := self.connection.Query(
`select id, user, name, endpoint, latitude, longitude, timestamp
from devices where user=?`, user)
if err != nil {
return nil, err
}
devices := make([]Device, 0)
for res.Next() {
d := Device{}
err = res.Scan(&d.Id, &d.User, &d.Name, &d.Endpoint, &d.Latitude,
&d.Longitude, &d.Timestamp)
if err != nil {
return nil, err
}
devices = append(devices, d)
}
return devices, nil
}