database.go (view raw)
1//
2// Copyright (c) 2019 Ted Unangst <tedu@tedunangst.com>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15
16package main
17
18import (
19 "bytes"
20 "database/sql"
21 "encoding/json"
22 "fmt"
23 "log"
24 "strconv"
25 "strings"
26 "time"
27
28 "humungus.tedunangst.com/r/webs/login"
29)
30
31func butwhatabout(name string) (*WhatAbout, error) {
32 row := stmtWhatAbout.QueryRow(name)
33 var user WhatAbout
34 var options string
35 err := row.Scan(&user.ID, &user.Name, &user.Display, &user.About, &user.Key, &options)
36 user.URL = fmt.Sprintf("https://%s/%s/%s", serverName, userSep, user.Name)
37 user.SkinnyCSS = strings.Contains(options, " skinny ")
38 return &user, err
39}
40
41func gethonkers(userid int64) []*Honker {
42 rows, err := stmtHonkers.Query(userid)
43 if err != nil {
44 log.Printf("error querying honkers: %s", err)
45 return nil
46 }
47 defer rows.Close()
48 var honkers []*Honker
49 for rows.Next() {
50 var f Honker
51 var combos string
52 err = rows.Scan(&f.ID, &f.UserID, &f.Name, &f.XID, &f.Flavor, &combos)
53 f.Combos = strings.Split(strings.TrimSpace(combos), " ")
54 if err != nil {
55 log.Printf("error scanning honker: %s", err)
56 return nil
57 }
58 honkers = append(honkers, &f)
59 }
60 return honkers
61}
62
63func getdubs(userid int64) []*Honker {
64 rows, err := stmtDubbers.Query(userid)
65 if err != nil {
66 log.Printf("error querying dubs: %s", err)
67 return nil
68 }
69 defer rows.Close()
70 var honkers []*Honker
71 for rows.Next() {
72 var f Honker
73 err = rows.Scan(&f.ID, &f.UserID, &f.Name, &f.XID, &f.Flavor)
74 if err != nil {
75 log.Printf("error scanning honker: %s", err)
76 return nil
77 }
78 honkers = append(honkers, &f)
79 }
80 return honkers
81}
82
83func allusers() []login.UserInfo {
84 var users []login.UserInfo
85 rows, _ := opendatabase().Query("select userid, username from users")
86 defer rows.Close()
87 for rows.Next() {
88 var u login.UserInfo
89 rows.Scan(&u.UserID, &u.Username)
90 users = append(users, u)
91 }
92 return users
93}
94
95func getxonk(userid int64, xid string) *Honk {
96 row := stmtOneXonk.QueryRow(userid, xid)
97 return scanhonk(row)
98}
99
100func getbonk(userid int64, xid string) *Honk {
101 row := stmtOneBonk.QueryRow(userid, xid)
102 return scanhonk(row)
103}
104
105func getpublichonks() []*Honk {
106 dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
107 rows, err := stmtPublicHonks.Query(dt)
108 return getsomehonks(rows, err)
109}
110func gethonksbyuser(name string, includeprivate bool) []*Honk {
111 dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
112 whofore := 2
113 if includeprivate {
114 whofore = 3
115 }
116 rows, err := stmtUserHonks.Query(whofore, name, dt)
117 return getsomehonks(rows, err)
118}
119func gethonksforuser(userid int64) []*Honk {
120 dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
121 rows, err := stmtHonksForUser.Query(userid, dt, userid, userid)
122 return getsomehonks(rows, err)
123}
124func gethonksforuserfirstclass(userid int64) []*Honk {
125 dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
126 rows, err := stmtHonksForUserFirstClass.Query(userid, dt, userid, userid)
127 return getsomehonks(rows, err)
128}
129func gethonksforme(userid int64) []*Honk {
130 dt := time.Now().UTC().Add(-7 * 24 * time.Hour).Format(dbtimeformat)
131 rows, err := stmtHonksForMe.Query(userid, dt, userid)
132 return getsomehonks(rows, err)
133}
134func gethonksbyhonker(userid int64, honker string) []*Honk {
135 rows, err := stmtHonksByHonker.Query(userid, honker, userid)
136 return getsomehonks(rows, err)
137}
138func gethonksbyxonker(userid int64, xonker string) []*Honk {
139 rows, err := stmtHonksByXonker.Query(userid, xonker, xonker, userid)
140 return getsomehonks(rows, err)
141}
142func gethonksbycombo(userid int64, combo string) []*Honk {
143 combo = "% " + combo + " %"
144 rows, err := stmtHonksByCombo.Query(userid, combo, userid)
145 return getsomehonks(rows, err)
146}
147func gethonksbyconvoy(userid int64, convoy string) []*Honk {
148 rows, err := stmtHonksByConvoy.Query(userid, userid, convoy)
149 honks := getsomehonks(rows, err)
150 for i, j := 0, len(honks)-1; i < j; i, j = i+1, j-1 {
151 honks[i], honks[j] = honks[j], honks[i]
152 }
153 return honks
154}
155func gethonksbysearch(userid int64, q string) []*Honk {
156 q = "%" + q + "%"
157 rows, err := stmtHonksBySearch.Query(userid, q)
158 honks := getsomehonks(rows, err)
159 return honks
160}
161func gethonksbyontology(userid int64, name string) []*Honk {
162 rows, err := stmtHonksByOntology.Query(name, userid, userid)
163 honks := getsomehonks(rows, err)
164 return honks
165}
166
167func getsomehonks(rows *sql.Rows, err error) []*Honk {
168 if err != nil {
169 log.Printf("error querying honks: %s", err)
170 return nil
171 }
172 defer rows.Close()
173 var honks []*Honk
174 for rows.Next() {
175 h := scanhonk(rows)
176 if h != nil {
177 honks = append(honks, h)
178 }
179 }
180 rows.Close()
181 donksforhonks(honks)
182 return honks
183}
184
185type RowLike interface {
186 Scan(dest ...interface{}) error
187}
188
189func scanhonk(row RowLike) *Honk {
190 h := new(Honk)
191 var dt, aud string
192 err := row.Scan(&h.ID, &h.UserID, &h.Username, &h.What, &h.Honker, &h.Oonker, &h.XID, &h.RID,
193 &dt, &h.URL, &aud, &h.Noise, &h.Precis, &h.Format, &h.Convoy, &h.Whofore, &h.Flags)
194 if err != nil {
195 if err != sql.ErrNoRows {
196 log.Printf("error scanning honk: %s", err)
197 }
198 return nil
199 }
200 h.Date, _ = time.Parse(dbtimeformat, dt)
201 h.Audience = strings.Split(aud, " ")
202 h.Public = !keepitquiet(h.Audience)
203 return h
204}
205
206func donksforhonks(honks []*Honk) {
207 db := opendatabase()
208 var ids []string
209 hmap := make(map[int64]*Honk)
210 for _, h := range honks {
211 ids = append(ids, fmt.Sprintf("%d", h.ID))
212 hmap[h.ID] = h
213 }
214 // grab donks
215 q := fmt.Sprintf("select honkid, donks.fileid, xid, name, description, url, media, local from donks join filemeta on donks.fileid = filemeta.fileid where honkid in (%s)", strings.Join(ids, ","))
216 rows, err := db.Query(q)
217 if err != nil {
218 log.Printf("error querying donks: %s", err)
219 return
220 }
221 defer rows.Close()
222 for rows.Next() {
223 var hid int64
224 var d Donk
225 err = rows.Scan(&hid, &d.FileID, &d.XID, &d.Name, &d.Desc, &d.URL, &d.Media, &d.Local)
226 if err != nil {
227 log.Printf("error scanning donk: %s", err)
228 continue
229 }
230 h := hmap[hid]
231 h.Donks = append(h.Donks, &d)
232 }
233 rows.Close()
234
235 // grab onts
236 q = fmt.Sprintf("select honkid, ontology from onts where honkid in (%s)", strings.Join(ids, ","))
237 rows, err = db.Query(q)
238 if err != nil {
239 log.Printf("error querying onts: %s", err)
240 return
241 }
242 defer rows.Close()
243 for rows.Next() {
244 var hid int64
245 var o string
246 err = rows.Scan(&hid, &o)
247 if err != nil {
248 log.Printf("error scanning donk: %s", err)
249 continue
250 }
251 h := hmap[hid]
252 h.Onts = append(h.Onts, o)
253 }
254 rows.Close()
255 // grab meta
256 q = fmt.Sprintf("select honkid, genus, json from honkmeta where honkid in (%s)", strings.Join(ids, ","))
257 rows, err = db.Query(q)
258 if err != nil {
259 log.Printf("error querying honkmeta: %s", err)
260 return
261 }
262 defer rows.Close()
263 for rows.Next() {
264 var hid int64
265 var genus, j string
266 err = rows.Scan(&hid, &genus, &j)
267 if err != nil {
268 log.Printf("error scanning honkmeta: %s", err)
269 continue
270 }
271 h := hmap[hid]
272 switch genus {
273 case "place":
274 p := new(Place)
275 err = unjsonify(j, p)
276 if err != nil {
277 log.Printf("error parsing place: %s", err)
278 continue
279 }
280 h.Place = p
281 case "oldrev":
282 default:
283 log.Printf("unknown meta genus: %s", genus)
284 }
285 }
286 rows.Close()
287}
288
289func savefile(xid string, name string, desc string, url string, media string, local bool, data []byte) (int64, error) {
290 res, err := stmtSaveFile.Exec(xid, name, desc, url, media, local)
291 if err != nil {
292 return 0, err
293 }
294 fileid, _ := res.LastInsertId()
295 if local {
296 _, err = stmtSaveFileData.Exec(xid, media, data)
297 if err != nil {
298 return 0, err
299 }
300 }
301 return fileid, nil
302}
303
304func savehonk(h *Honk) error {
305 dt := h.Date.UTC().Format(dbtimeformat)
306 aud := strings.Join(h.Audience, " ")
307
308 res, err := stmtSaveHonk.Exec(h.UserID, h.What, h.Honker, h.XID, h.RID, dt, h.URL,
309 aud, h.Noise, h.Convoy, h.Whofore, h.Format, h.Precis,
310 h.Oonker, h.Flags)
311 if err != nil {
312 log.Printf("err saving honk: %s", err)
313 return err
314 }
315 h.ID, _ = res.LastInsertId()
316 err = saveextras(h)
317 return err
318}
319
320func saveextras(h *Honk) error {
321 for _, d := range h.Donks {
322 _, err := stmtSaveDonk.Exec(h.ID, d.FileID)
323 if err != nil {
324 log.Printf("err saving donk: %s", err)
325 return err
326 }
327 }
328 for _, o := range h.Onts {
329 _, err := stmtSaveOnt.Exec(strings.ToLower(o), h.ID)
330 if err != nil {
331 log.Printf("error saving ont: %s", err)
332 return err
333 }
334 }
335 if p := h.Place; p != nil {
336 j, err := jsonify(p)
337 if err != nil {
338 _, err = stmtSaveMeta.Exec(h.ID, "place", j)
339 }
340 if err != nil {
341 log.Printf("error saving place: %s", err)
342 return err
343 }
344 }
345 if p := h.Time; p != nil {
346 j, err := jsonify(p)
347 if err != nil {
348 _, err = stmtSaveMeta.Exec(h.ID, "time", j)
349 }
350 if err != nil {
351 log.Printf("error saving time: %s", err)
352 return err
353 }
354 }
355
356 return nil
357}
358
359func deleteextras(honkid int64) {
360 _, err := stmtDeleteDonks.Exec(honkid)
361 if err != nil {
362 log.Printf("error deleting: %s", err)
363 }
364 _, err = stmtDeleteOnts.Exec(honkid)
365 if err != nil {
366 log.Printf("error deleting: %s", err)
367 }
368 _, err = stmtDeleteMeta.Exec(honkid, "oldrev")
369 if err != nil {
370 log.Printf("error deleting: %s", err)
371 }
372}
373
374func deletehonk(honkid int64) {
375 deleteextras(honkid)
376 _, err := stmtDeleteHonk.Exec(honkid)
377 if err != nil {
378 log.Printf("error deleting: %s", err)
379 }
380}
381
382func jsonify(what interface{}) (string, error) {
383 var buf bytes.Buffer
384 e := json.NewEncoder(&buf)
385 e.SetEscapeHTML(false)
386 e.SetIndent("", "")
387 err := e.Encode(what)
388 return buf.String(), err
389}
390
391func unjsonify(s string, dest interface{}) error {
392 d := json.NewDecoder(strings.NewReader(s))
393 err := d.Decode(dest)
394 return err
395}
396
397func updatehonk(h *Honk) {
398 old := getxonk(h.UserID, h.XID)
399 oldrev := OldRevision{Precis: old.Precis, Noise: old.Noise}
400
401 deleteextras(h.ID)
402
403 dt := h.Date.UTC().Format(dbtimeformat)
404 stmtUpdateHonk.Exec(h.Precis, h.Noise, h.Format, dt, h.ID)
405
406 saveextras(h)
407 j, err := jsonify(&oldrev)
408 if err != nil {
409 log.Printf("error jsonify oldrev: %s", err)
410 return
411 }
412 _, err = stmtSaveMeta.Exec(old.ID, "oldrev", j)
413 if err != nil {
414 log.Printf("error saving oldrev: %s", err)
415 return
416 }
417}
418
419func cleanupdb(arg string) {
420 db := opendatabase()
421 days, err := strconv.Atoi(arg)
422 var sqlargs []interface{}
423 var where string
424 if err != nil {
425 honker := arg
426 expdate := time.Now().UTC().Add(-3 * 24 * time.Hour).Format(dbtimeformat)
427 where = "dt < ? and whofore = 0 and honker = ?"
428 sqlargs = append(sqlargs, expdate)
429 sqlargs = append(sqlargs, honker)
430 } else {
431 expdate := time.Now().UTC().Add(-time.Duration(days) * 24 * time.Hour).Format(dbtimeformat)
432 where = "dt < ? and whofore = 0 and convoy not in (select convoy from honks where whofore = 2 or whofore = 3)"
433 sqlargs = append(sqlargs, expdate)
434 }
435 doordie(db, "delete from honks where "+where, sqlargs...)
436 doordie(db, "delete from donks where honkid not in (select honkid from honks)")
437 doordie(db, "delete from onts where honkid not in (select honkid from honks)")
438 doordie(db, "delete from honkmeta where honkid not in (select honkid from honks)")
439
440 doordie(db, "delete from filemeta where fileid not in (select fileid from donks)")
441 for _, u := range allusers() {
442 doordie(db, "delete from zonkers where userid = ? and wherefore = 'zonvoy' and zonkerid < (select zonkerid from zonkers where userid = ? and wherefore = 'zonvoy' order by zonkerid desc limit 1 offset 200)", u.UserID, u.UserID)
443 }
444
445 filexids := make(map[string]bool)
446 blobdb := openblobdb()
447 rows, err := blobdb.Query("select xid from filedata")
448 if err != nil {
449 log.Fatal(err)
450 }
451 for rows.Next() {
452 var xid string
453 err = rows.Scan(&xid)
454 if err != nil {
455 log.Fatal(err)
456 }
457 filexids[xid] = true
458 }
459 rows.Close()
460 rows, err = db.Query("select xid from filemeta")
461 for rows.Next() {
462 var xid string
463 err = rows.Scan(&xid)
464 if err != nil {
465 log.Fatal(err)
466 }
467 delete(filexids, xid)
468 }
469 rows.Close()
470 tx, err := blobdb.Begin()
471 if err != nil {
472 log.Fatal(err)
473 }
474 for xid, _ := range filexids {
475 _, err = tx.Exec("delete from filedata where xid = ?", xid)
476 if err != nil {
477 log.Fatal(err)
478 }
479 }
480 err = tx.Commit()
481 if err != nil {
482 log.Fatal(err)
483 }
484}
485
486var stmtHonkers, stmtDubbers, stmtSaveHonker, stmtUpdateFlavor, stmtUpdateCombos *sql.Stmt
487var stmtOneXonk, stmtPublicHonks, stmtUserHonks, stmtHonksByCombo, stmtHonksByConvoy *sql.Stmt
488var stmtHonksByOntology, stmtHonksForUser, stmtHonksForMe, stmtSaveDub, stmtHonksByXonker *sql.Stmt
489var stmtHonksBySearch, stmtHonksByHonker, stmtSaveHonk, stmtWhatAbout *sql.Stmt
490var stmtOneBonk, stmtFindZonk, stmtFindXonk, stmtSaveDonk *sql.Stmt
491var stmtFindFile, stmtGetFileData, stmtSaveFileData, stmtSaveFile *sql.Stmt
492var stmtAddDoover, stmtGetDoovers, stmtLoadDoover, stmtZapDoover, stmtOneHonker *sql.Stmt
493var stmtThumbBiters, stmtDeleteHonk, stmtDeleteDonks, stmtDeleteOnts, stmtSaveZonker *sql.Stmt
494var stmtGetZonkers, stmtRecentHonkers, stmtGetXonker, stmtSaveXonker, stmtDeleteXonker *sql.Stmt
495var stmtSelectOnts, stmtSaveOnt, stmtUpdateFlags, stmtClearFlags *sql.Stmt
496var stmtHonksForUserFirstClass, stmtSaveMeta, stmtDeleteMeta, stmtUpdateHonk *sql.Stmt
497
498func preparetodie(db *sql.DB, s string) *sql.Stmt {
499 stmt, err := db.Prepare(s)
500 if err != nil {
501 log.Fatalf("error %s: %s", err, s)
502 }
503 return stmt
504}
505
506func prepareStatements(db *sql.DB) {
507 stmtHonkers = preparetodie(db, "select honkerid, userid, name, xid, flavor, combos from honkers where userid = ? and (flavor = 'sub' or flavor = 'peep' or flavor = 'unsub') order by name")
508 stmtSaveHonker = preparetodie(db, "insert into honkers (userid, name, xid, flavor, combos) values (?, ?, ?, ?, ?)")
509 stmtUpdateFlavor = preparetodie(db, "update honkers set flavor = ? where userid = ? and xid = ? and flavor = ?")
510 stmtUpdateCombos = preparetodie(db, "update honkers set combos = ? where honkerid = ? and userid = ?")
511 stmtOneHonker = preparetodie(db, "select xid from honkers where name = ? and userid = ?")
512 stmtDubbers = preparetodie(db, "select honkerid, userid, name, xid, flavor from honkers where userid = ? and flavor = 'dub'")
513
514 selecthonks := "select honks.honkid, honks.userid, username, what, honker, oonker, honks.xid, rid, dt, url, audience, noise, precis, format, convoy, whofore, flags from honks join users on honks.userid = users.userid "
515 limit := " order by honks.honkid desc limit 250"
516 butnotthose := " and convoy not in (select name from zonkers where userid = ? and wherefore = 'zonvoy' order by zonkerid desc limit 100)"
517 stmtOneXonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ?")
518 stmtOneBonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ? and what = 'bonk' and whofore = 2")
519 stmtPublicHonks = preparetodie(db, selecthonks+"where whofore = 2 and dt > ?"+limit)
520 stmtUserHonks = preparetodie(db, selecthonks+"where (whofore = 2 or whofore = ?) and username = ? and dt > ?"+limit)
521 stmtHonksForUser = preparetodie(db, selecthonks+"where honks.userid = ? and dt > ? and honker in (select xid from honkers where userid = ? and flavor = 'sub' and combos not like '% - %')"+butnotthose+limit)
522 stmtHonksForUserFirstClass = preparetodie(db, selecthonks+"where honks.userid = ? and dt > ? and (what <> 'tonk') and honker in (select xid from honkers where userid = ? and flavor = 'sub' and combos not like '% - %')"+butnotthose+limit)
523 stmtHonksForMe = preparetodie(db, selecthonks+"where honks.userid = ? and dt > ? and whofore = 1"+butnotthose+limit)
524 stmtHonksByHonker = preparetodie(db, selecthonks+"join honkers on (honkers.xid = honks.honker or honkers.xid = honks.oonker) where honks.userid = ? and honkers.name = ?"+butnotthose+limit)
525 stmtHonksByXonker = preparetodie(db, selecthonks+" where honks.userid = ? and (honker = ? or oonker = ?)"+butnotthose+limit)
526 stmtHonksByCombo = preparetodie(db, selecthonks+"join honkers on honkers.xid = honks.honker where honks.userid = ? and honkers.combos like ?"+butnotthose+limit)
527 stmtHonksBySearch = preparetodie(db, selecthonks+"where honks.userid = ? and noise like ?"+limit)
528 stmtHonksByConvoy = preparetodie(db, selecthonks+"where (honks.userid = ? or (? = -1 and whofore = 2)) and convoy = ?"+limit)
529 stmtHonksByOntology = preparetodie(db, selecthonks+"join onts on honks.honkid = onts.honkid where onts.ontology = ? and (honks.userid = ? or (? = -1 and honks.whofore = 2))"+limit)
530
531 stmtSaveMeta = preparetodie(db, "insert into honkmeta (honkid, genus, json) values (?, ?, ?)")
532 stmtDeleteMeta = preparetodie(db, "delete from honkmeta where honkid = ? and genus <> ?")
533 stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
534 stmtDeleteHonk = preparetodie(db, "delete from honks where honkid = ?")
535 stmtUpdateHonk = preparetodie(db, "update honks set precis = ?, noise = ?, format = ?, dt = ? where honkid = ?")
536 stmtSaveOnt = preparetodie(db, "insert into onts (ontology, honkid) values (?, ?)")
537 stmtDeleteOnts = preparetodie(db, "delete from onts where honkid = ?")
538 stmtSaveDonk = preparetodie(db, "insert into donks (honkid, fileid) values (?, ?)")
539 stmtDeleteDonks = preparetodie(db, "delete from donks where honkid = ?")
540 stmtSaveFile = preparetodie(db, "insert into filemeta (xid, name, description, url, media, local) values (?, ?, ?, ?, ?, ?)")
541 blobdb := openblobdb()
542 stmtSaveFileData = preparetodie(blobdb, "insert into filedata (xid, media, content) values (?, ?, ?)")
543 stmtGetFileData = preparetodie(blobdb, "select media, content from filedata where xid = ?")
544 stmtFindXonk = preparetodie(db, "select honkid from honks where userid = ? and xid = ?")
545 stmtFindFile = preparetodie(db, "select fileid from filemeta where url = ? and local = 1")
546 stmtWhatAbout = preparetodie(db, "select userid, username, displayname, about, pubkey, options from users where username = ?")
547 stmtSaveDub = preparetodie(db, "insert into honkers (userid, name, xid, flavor) values (?, ?, ?, ?)")
548 stmtAddDoover = preparetodie(db, "insert into doovers (dt, tries, username, rcpt, msg) values (?, ?, ?, ?, ?)")
549 stmtGetDoovers = preparetodie(db, "select dooverid, dt from doovers")
550 stmtLoadDoover = preparetodie(db, "select tries, username, rcpt, msg from doovers where dooverid = ?")
551 stmtZapDoover = preparetodie(db, "delete from doovers where dooverid = ?")
552 stmtThumbBiters = preparetodie(db, "select userid, name, wherefore from zonkers")
553 stmtFindZonk = preparetodie(db, "select zonkerid from zonkers where userid = ? and name = ? and wherefore = 'zonk'")
554 stmtGetZonkers = preparetodie(db, "select zonkerid, name, wherefore from zonkers where userid = ? and wherefore <> 'zonk'")
555 stmtSaveZonker = preparetodie(db, "insert into zonkers (userid, name, wherefore) values (?, ?, ?)")
556 stmtGetXonker = preparetodie(db, "select info from xonkers where name = ? and flavor = ?")
557 stmtSaveXonker = preparetodie(db, "insert into xonkers (name, info, flavor) values (?, ?, ?)")
558 stmtDeleteXonker = preparetodie(db, "delete from xonkers where name = ? and flavor = ?")
559 stmtRecentHonkers = preparetodie(db, "select distinct(honker) from honks where userid = ? and honker not in (select xid from honkers where userid = ? and flavor = 'sub') order by honkid desc limit 100")
560 stmtUpdateFlags = preparetodie(db, "update honks set flags = flags | ? where honkid = ?")
561 stmtClearFlags = preparetodie(db, "update honks set flags = flags & ~ ? where honkid = ?")
562 stmtSelectOnts = preparetodie(db, "select distinct(ontology) from onts join honks on onts.honkid = honks.honkid where (honks.userid = ? or honks.whofore = 2)")
563}