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