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