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