all repos — honk @ b0e8e2aa0717201b7b13dbe7d4d41b046ef528f1

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