all repos — honk @ 79ff28a540ebb53a1b3c56e8baeddd46f2b7c8fa

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