all repos — honk @ b58c58f116394620117e4301458282e301429b2b

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