all repos — honk @ 5f581e0167c9b9f9d661c0fdf529a88e1ed551e0

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