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