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