all repos — honk @ 4486057adcd0805c66e1e1a29f06c466508be110

my fork of honk

database.go (view raw)

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