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