all repos — honk @ 6123289ef702ef4b0ff6e2b2120db52b3e6190b2

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	db := opendatabase()
345	tx, err := db.Begin()
346	if err != nil {
347		log.Printf("can't begin tx: %s", err)
348		return err
349	}
350
351	res, err := tx.Stmt(stmtSaveHonk).Exec(h.UserID, h.What, h.Honker, h.XID, h.RID, dt, h.URL,
352		aud, h.Noise, h.Convoy, h.Whofore, h.Format, h.Precis,
353		h.Oonker, h.Flags)
354	if err == nil {
355		h.ID, _ = res.LastInsertId()
356		err = saveextras(tx, h)
357	}
358	if err == nil {
359		err = tx.Commit()
360	} else {
361		tx.Rollback()
362	}
363	if err != nil {
364		log.Printf("error saving honk: %s", err)
365	}
366	return err
367}
368
369func updatehonk(h *Honk) error {
370	old := getxonk(h.UserID, h.XID)
371	oldrev := OldRevision{Precis: old.Precis, Noise: old.Noise}
372	dt := h.Date.UTC().Format(dbtimeformat)
373
374	db := opendatabase()
375	tx, err := db.Begin()
376	if err != nil {
377		log.Printf("can't begin tx: %s", err)
378		return err
379	}
380
381	err = deleteextras(tx, h.ID)
382	if err == nil {
383		_, err = tx.Stmt(stmtUpdateHonk).Exec(h.Precis, h.Noise, h.Format, dt, h.ID)
384	}
385	if err == nil {
386		err = saveextras(tx, h)
387	}
388	if err == nil {
389		var j string
390		j, err = jsonify(&oldrev)
391		if err == nil {
392			_, err = tx.Stmt(stmtSaveMeta).Exec(old.ID, "oldrev", j)
393		}
394		if err != nil {
395			log.Printf("error saving oldrev: %s", err)
396		}
397	}
398	if err == nil {
399		err = tx.Commit()
400	} else {
401		tx.Rollback()
402	}
403	if err != nil {
404		log.Printf("error updating honk %d: %s", h.ID, err)
405	}
406	return err
407}
408
409func deletehonk(honkid int64) error {
410	db := opendatabase()
411	tx, err := db.Begin()
412	if err != nil {
413		log.Printf("can't begin tx: %s", err)
414		return err
415	}
416
417	err = deleteextras(tx, honkid)
418	if err == nil {
419		_, err = tx.Stmt(stmtDeleteMeta).Exec(honkid, "nonsense")
420	}
421	if err == nil {
422		_, err = tx.Stmt(stmtDeleteHonk).Exec(honkid)
423	}
424	if err == nil {
425		err = tx.Commit()
426	} else {
427		tx.Rollback()
428	}
429	if err != nil {
430		log.Printf("error deleting honk %d: %s", honkid, err)
431	}
432	return err
433}
434
435func saveextras(tx *sql.Tx, h *Honk) error {
436	for _, d := range h.Donks {
437		_, err := tx.Stmt(stmtSaveDonk).Exec(h.ID, d.FileID)
438		if err != nil {
439			log.Printf("error saving donk: %s", err)
440			return err
441		}
442	}
443	for _, o := range h.Onts {
444		_, err := tx.Stmt(stmtSaveOnt).Exec(strings.ToLower(o), h.ID)
445		if err != nil {
446			log.Printf("error saving ont: %s", err)
447			return err
448		}
449	}
450	if p := h.Place; p != nil {
451		j, err := jsonify(p)
452		if err == nil {
453			_, err = tx.Stmt(stmtSaveMeta).Exec(h.ID, "place", j)
454		}
455		if err != nil {
456			log.Printf("error saving place: %s", err)
457			return err
458		}
459	}
460	if t := h.Time; t != nil {
461		j, err := jsonify(t)
462		if err == nil {
463			_, err = tx.Stmt(stmtSaveMeta).Exec(h.ID, "time", j)
464		}
465		if err != nil {
466			log.Printf("error saving time: %s", err)
467			return err
468		}
469	}
470	return nil
471}
472
473func deleteextras(tx *sql.Tx, honkid int64) error {
474	_, err := tx.Stmt(stmtDeleteDonks).Exec(honkid)
475	if err != nil {
476		return err
477	}
478	_, err = tx.Stmt(stmtDeleteOnts).Exec(honkid)
479	if err != nil {
480		return err
481	}
482	_, err = tx.Stmt(stmtDeleteMeta).Exec(honkid, "oldrev")
483	if err != nil {
484		return err
485	}
486	return nil
487}
488
489func jsonify(what interface{}) (string, error) {
490	var buf bytes.Buffer
491	e := json.NewEncoder(&buf)
492	e.SetEscapeHTML(false)
493	e.SetIndent("", "")
494	err := e.Encode(what)
495	return buf.String(), err
496}
497
498func unjsonify(s string, dest interface{}) error {
499	d := json.NewDecoder(strings.NewReader(s))
500	err := d.Decode(dest)
501	return err
502}
503
504func cleanupdb(arg string) {
505	db := opendatabase()
506	days, err := strconv.Atoi(arg)
507	var sqlargs []interface{}
508	var where string
509	if err != nil {
510		honker := arg
511		expdate := time.Now().UTC().Add(-3 * 24 * time.Hour).Format(dbtimeformat)
512		where = "dt < ? and whofore = 0 and honker = ?"
513		sqlargs = append(sqlargs, expdate)
514		sqlargs = append(sqlargs, honker)
515	} else {
516		expdate := time.Now().UTC().Add(-time.Duration(days) * 24 * time.Hour).Format(dbtimeformat)
517		where = "dt < ? and whofore = 0 and convoy not in (select convoy from honks where whofore = 2 or whofore = 3)"
518		sqlargs = append(sqlargs, expdate)
519	}
520	doordie(db, "delete from honks where "+where, sqlargs...)
521	doordie(db, "delete from donks where honkid not in (select honkid from honks)")
522	doordie(db, "delete from onts where honkid not in (select honkid from honks)")
523	doordie(db, "delete from honkmeta where honkid not in (select honkid from honks)")
524
525	doordie(db, "delete from filemeta where fileid not in (select fileid from donks)")
526	for _, u := range allusers() {
527		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)
528	}
529
530	filexids := make(map[string]bool)
531	blobdb := openblobdb()
532	rows, err := blobdb.Query("select xid from filedata")
533	if err != nil {
534		log.Fatal(err)
535	}
536	for rows.Next() {
537		var xid string
538		err = rows.Scan(&xid)
539		if err != nil {
540			log.Fatal(err)
541		}
542		filexids[xid] = true
543	}
544	rows.Close()
545	rows, err = db.Query("select xid from filemeta")
546	for rows.Next() {
547		var xid string
548		err = rows.Scan(&xid)
549		if err != nil {
550			log.Fatal(err)
551		}
552		delete(filexids, xid)
553	}
554	rows.Close()
555	tx, err := blobdb.Begin()
556	if err != nil {
557		log.Fatal(err)
558	}
559	for xid, _ := range filexids {
560		_, err = tx.Exec("delete from filedata where xid = ?", xid)
561		if err != nil {
562			log.Fatal(err)
563		}
564	}
565	err = tx.Commit()
566	if err != nil {
567		log.Fatal(err)
568	}
569}
570
571var stmtHonkers, stmtDubbers, stmtSaveHonker, stmtUpdateFlavor, stmtUpdateHonker *sql.Stmt
572var stmtAnyXonk, stmtOneXonk, stmtPublicHonks, stmtUserHonks, stmtHonksByCombo, stmtHonksByConvoy *sql.Stmt
573var stmtHonksByOntology, stmtHonksForUser, stmtHonksForMe, stmtSaveDub, stmtHonksByXonker *sql.Stmt
574var stmtHonksBySearch, stmtHonksByHonker, stmtSaveHonk, stmtWhatAbout *sql.Stmt
575var stmtOneBonk, stmtFindZonk, stmtFindXonk, stmtSaveDonk *sql.Stmt
576var stmtFindFile, stmtGetFileData, stmtSaveFileData, stmtSaveFile *sql.Stmt
577var stmtAddDoover, stmtGetDoovers, stmtLoadDoover, stmtZapDoover, stmtOneHonker *sql.Stmt
578var stmtThumbBiters, stmtDeleteHonk, stmtDeleteDonks, stmtDeleteOnts, stmtSaveZonker *sql.Stmt
579var stmtGetZonkers, stmtRecentHonkers, stmtGetXonker, stmtSaveXonker, stmtDeleteXonker *sql.Stmt
580var stmtSelectOnts, stmtSaveOnt, stmtUpdateFlags, stmtClearFlags *sql.Stmt
581var stmtHonksForUserFirstClass, stmtSaveMeta, stmtDeleteMeta, stmtUpdateHonk *sql.Stmt
582var stmtGetFilters, stmtSaveFilter, stmtDeleteFilter *sql.Stmt
583
584func preparetodie(db *sql.DB, s string) *sql.Stmt {
585	stmt, err := db.Prepare(s)
586	if err != nil {
587		log.Fatalf("error %s: %s", err, s)
588	}
589	return stmt
590}
591
592func prepareStatements(db *sql.DB) {
593	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")
594	stmtSaveHonker = preparetodie(db, "insert into honkers (userid, name, xid, flavor, combos) values (?, ?, ?, ?, ?)")
595	stmtUpdateFlavor = preparetodie(db, "update honkers set flavor = ? where userid = ? and xid = ? and flavor = ?")
596	stmtUpdateHonker = preparetodie(db, "update honkers set name = ?, combos = ? where honkerid = ? and userid = ?")
597	stmtOneHonker = preparetodie(db, "select xid from honkers where name = ? and userid = ?")
598	stmtDubbers = preparetodie(db, "select honkerid, userid, name, xid, flavor from honkers where userid = ? and flavor = 'dub'")
599
600	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 "
601	limit := " order by honks.honkid desc limit 250"
602	butnotthose := " and convoy not in (select name from zonkers where userid = ? and wherefore = 'zonvoy' order by zonkerid desc limit 100)"
603	stmtOneXonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ?")
604	stmtAnyXonk = preparetodie(db, selecthonks+"where xid = ? order by honks.honkid asc")
605	stmtOneBonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ? and what = 'bonk' and whofore = 2")
606	stmtPublicHonks = preparetodie(db, selecthonks+"where whofore = 2 and dt > ?"+limit)
607	stmtUserHonks = preparetodie(db, selecthonks+"where (whofore = 2 or whofore = ?) and username = ? and dt > ?"+limit)
608	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)
609	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)
610	stmtHonksForMe = preparetodie(db, selecthonks+"where honks.userid = ? and dt > ? and whofore = 1"+butnotthose+limit)
611	stmtHonksByHonker = preparetodie(db, selecthonks+"join honkers on (honkers.xid = honks.honker or honkers.xid = honks.oonker) where honks.userid = ? and honkers.name = ?"+butnotthose+limit)
612	stmtHonksByXonker = preparetodie(db, selecthonks+" where honks.userid = ? and (honker = ? or oonker = ?)"+butnotthose+limit)
613	stmtHonksByCombo = preparetodie(db, selecthonks+"join honkers on honkers.xid = honks.honker where honks.userid = ? and honkers.combos like ?"+butnotthose+limit)
614	stmtHonksBySearch = preparetodie(db, selecthonks+"where honks.userid = ? and noise like ?"+limit)
615	stmtHonksByConvoy = preparetodie(db, selecthonks+"where (honks.userid = ? or (? = -1 and whofore = 2)) and convoy = ?"+limit)
616	stmtHonksByOntology = preparetodie(db, selecthonks+"join onts on honks.honkid = onts.honkid where onts.ontology = ? and (honks.userid = ? or (? = -1 and honks.whofore = 2))"+limit)
617
618	stmtSaveMeta = preparetodie(db, "insert into honkmeta (honkid, genus, json) values (?, ?, ?)")
619	stmtDeleteMeta = preparetodie(db, "delete from honkmeta where honkid = ? and genus <> ?")
620	stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
621	stmtDeleteHonk = preparetodie(db, "delete from honks where honkid = ?")
622	stmtUpdateHonk = preparetodie(db, "update honks set precis = ?, noise = ?, format = ?, dt = ? where honkid = ?")
623	stmtSaveOnt = preparetodie(db, "insert into onts (ontology, honkid) values (?, ?)")
624	stmtDeleteOnts = preparetodie(db, "delete from onts where honkid = ?")
625	stmtSaveDonk = preparetodie(db, "insert into donks (honkid, fileid) values (?, ?)")
626	stmtDeleteDonks = preparetodie(db, "delete from donks where honkid = ?")
627	stmtSaveFile = preparetodie(db, "insert into filemeta (xid, name, description, url, media, local) values (?, ?, ?, ?, ?, ?)")
628	blobdb := openblobdb()
629	stmtSaveFileData = preparetodie(blobdb, "insert into filedata (xid, media, content) values (?, ?, ?)")
630	stmtGetFileData = preparetodie(blobdb, "select media, content from filedata where xid = ?")
631	stmtFindXonk = preparetodie(db, "select honkid from honks where userid = ? and xid = ?")
632	stmtFindFile = preparetodie(db, "select fileid, xid from filemeta where url = ? and local = 1")
633	stmtWhatAbout = preparetodie(db, "select userid, username, displayname, about, pubkey, options from users where username = ?")
634	stmtSaveDub = preparetodie(db, "insert into honkers (userid, name, xid, flavor) values (?, ?, ?, ?)")
635	stmtAddDoover = preparetodie(db, "insert into doovers (dt, tries, username, rcpt, msg) values (?, ?, ?, ?, ?)")
636	stmtGetDoovers = preparetodie(db, "select dooverid, dt from doovers")
637	stmtLoadDoover = preparetodie(db, "select tries, username, rcpt, msg from doovers where dooverid = ?")
638	stmtZapDoover = preparetodie(db, "delete from doovers where dooverid = ?")
639	stmtThumbBiters = preparetodie(db, "select userid, name, wherefore from zonkers")
640	stmtFindZonk = preparetodie(db, "select zonkerid from zonkers where userid = ? and name = ? and wherefore = 'zonk'")
641	stmtGetZonkers = preparetodie(db, "select zonkerid, name, wherefore from zonkers where userid = ? and wherefore <> 'zonk'")
642	stmtSaveZonker = preparetodie(db, "insert into zonkers (userid, name, wherefore) values (?, ?, ?)")
643	stmtGetXonker = preparetodie(db, "select info from xonkers where name = ? and flavor = ?")
644	stmtSaveXonker = preparetodie(db, "insert into xonkers (name, info, flavor) values (?, ?, ?)")
645	stmtDeleteXonker = preparetodie(db, "delete from xonkers where name = ? and flavor = ?")
646	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")
647	stmtUpdateFlags = preparetodie(db, "update honks set flags = flags | ? where honkid = ?")
648	stmtClearFlags = preparetodie(db, "update honks set flags = flags & ~ ? where honkid = ?")
649	stmtSelectOnts = preparetodie(db, "select distinct(ontology) from onts join honks on onts.honkid = honks.honkid where (honks.userid = ? or honks.whofore = 2)")
650	stmtGetFilters = preparetodie(db, "select hfcsid, json from hfcs where userid = ?")
651	stmtSaveFilter = preparetodie(db, "insert into hfcs (userid, json) values (?, ?)")
652	stmtDeleteFilter = preparetodie(db, "delete from hfcs where userid = ? and hfcsid = ?")
653}