all repos — honk @ 07fc8d94e48b0f6a64b3868eab0fa382791ce7ff

my fork of honk

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