all repos — honk @ 27931774809051fb985782e0271da58e3911c851

my fork of honk

database.go (view raw)

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