all repos — honk @ 46f0e2e9ddf057478ec8d26caf02f66d94cb7dbe

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