all repos — honk @ 49dc0802d5e134c9dd377864879d958b597ea2a5

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, false)
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, true)
531	if err == nil {
532		_, err = tx.Stmt(stmtDeleteHonk).Exec(honkid)
533	}
534	if err == nil {
535		err = tx.Commit()
536	} else {
537		tx.Rollback()
538	}
539	if err != nil {
540		log.Printf("error deleting honk %d: %s", honkid, err)
541	}
542	return err
543}
544
545func saveextras(tx *sql.Tx, h *Honk) error {
546	for _, d := range h.Donks {
547		_, err := tx.Stmt(stmtSaveDonk).Exec(h.ID, d.FileID)
548		if err != nil {
549			log.Printf("error saving donk: %s", err)
550			return err
551		}
552	}
553	for _, o := range h.Onts {
554		_, err := tx.Stmt(stmtSaveOnt).Exec(strings.ToLower(o), h.ID)
555		if err != nil {
556			log.Printf("error saving ont: %s", err)
557			return err
558		}
559	}
560	if p := h.Place; p != nil {
561		j, err := jsonify(p)
562		if err == nil {
563			_, err = tx.Stmt(stmtSaveMeta).Exec(h.ID, "place", j)
564		}
565		if err != nil {
566			log.Printf("error saving place: %s", err)
567			return err
568		}
569	}
570	if t := h.Time; t != nil {
571		j, err := jsonify(t)
572		if err == nil {
573			_, err = tx.Stmt(stmtSaveMeta).Exec(h.ID, "time", j)
574		}
575		if err != nil {
576			log.Printf("error saving time: %s", err)
577			return err
578		}
579	}
580	return nil
581}
582
583func deleteextras(tx *sql.Tx, honkid int64, everything bool) error {
584	_, err := tx.Stmt(stmtDeleteDonks).Exec(honkid)
585	if err != nil {
586		return err
587	}
588	_, err = tx.Stmt(stmtDeleteOnts).Exec(honkid)
589	if err != nil {
590		return err
591	}
592	if everything {
593		_, err = tx.Stmt(stmtDeleteAllMeta).Exec(honkid)
594	} else {
595		_, err = tx.Stmt(stmtDeleteSomeMeta).Exec(honkid)
596	}
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 *sql.Stmt
696var stmtSaveMeta, stmtDeleteAllMeta, stmtDeleteSomeMeta, stmtUpdateHonk *sql.Stmt
697var stmtHonksISaved, stmtGetFilters, stmtSaveFilter, stmtDeleteFilter *sql.Stmt
698var stmtGetTracks *sql.Stmt
699
700func preparetodie(db *sql.DB, s string) *sql.Stmt {
701	stmt, err := db.Prepare(s)
702	if err != nil {
703		log.Fatalf("error %s: %s", err, s)
704	}
705	return stmt
706}
707
708func prepareStatements(db *sql.DB) {
709	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")
710	stmtSaveHonker = preparetodie(db, "insert into honkers (userid, name, xid, flavor, combos, owner) values (?, ?, ?, ?, ?, ?)")
711	stmtUpdateFlavor = preparetodie(db, "update honkers set flavor = ? where userid = ? and xid = ? and name = ? and flavor = ?")
712	stmtUpdateHonker = preparetodie(db, "update honkers set name = ?, combos = ? where honkerid = ? and userid = ?")
713	stmtOneHonker = preparetodie(db, "select xid from honkers where name = ? and userid = ?")
714	stmtDubbers = preparetodie(db, "select honkerid, userid, name, xid, flavor from honkers where userid = ? and flavor = 'dub'")
715	stmtNamedDubbers = preparetodie(db, "select honkerid, userid, name, xid, flavor from honkers where userid = ? and name = ? and flavor = 'dub'")
716
717	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 "
718	limit := " order by honks.honkid desc limit 250"
719	smalllimit := " order by honks.honkid desc limit ?"
720	butnotthose := " and convoy not in (select name from zonkers where userid = ? and wherefore = 'zonvoy' order by zonkerid desc limit 100)"
721	stmtOneXonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ?")
722	stmtAnyXonk = preparetodie(db, selecthonks+"where xid = ? order by honks.honkid asc")
723	stmtOneBonk = preparetodie(db, selecthonks+"where honks.userid = ? and xid = ? and what = 'bonk' and whofore = 2")
724	stmtPublicHonks = preparetodie(db, selecthonks+"where whofore = 2 and dt > ?"+smalllimit)
725	stmtEventHonks = preparetodie(db, selecthonks+"where (whofore = 2 or honks.userid = ?) and what = 'event'"+smalllimit)
726	stmtUserHonks = preparetodie(db, selecthonks+"where honks.honkid > ? and (whofore = 2 or whofore = ?) and username = ? and dt > ?"+smalllimit)
727	myhonkers := " and honker in (select xid from honkers where userid = ? and (flavor = 'sub' or flavor = 'peep' or flavor = 'presub') and combos not like '% - %')"
728	stmtHonksForUser = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ?"+myhonkers+butnotthose+limit)
729	stmtHonksForUserFirstClass = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ? and (what <> 'tonk')"+myhonkers+butnotthose+limit)
730	stmtHonksForMe = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and dt > ? and whofore = 1"+butnotthose+limit)
731	stmtHonksISaved = preparetodie(db, selecthonks+"where honks.honkid > ? and honks.userid = ? and flags & 4 order by honks.honkid desc")
732	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)
733	stmtHonksByXonker = preparetodie(db, selecthonks+" where honks.honkid > ? and honks.userid = ? and (honker = ? or oonker = ?)"+butnotthose+limit)
734	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)
735	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)
736	stmtHonksByConvoy = preparetodie(db, selecthonks+"where honks.honkid > ? and (honks.userid = ? or (? = -1 and whofore = 2)) and convoy = ?"+limit)
737	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)
738
739	stmtSaveMeta = preparetodie(db, "insert into honkmeta (honkid, genus, json) values (?, ?, ?)")
740	stmtDeleteAllMeta = preparetodie(db, "delete from honkmeta where honkid = ?")
741	stmtDeleteSomeMeta = preparetodie(db, "delete from honkmeta where honkid = ? and genus not in ('oldrev')")
742	stmtSaveHonk = preparetodie(db, "insert into honks (userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")
743	stmtDeleteHonk = preparetodie(db, "delete from honks where honkid = ?")
744	stmtUpdateHonk = preparetodie(db, "update honks set precis = ?, noise = ?, format = ?, whofore = ?, dt = ? where honkid = ?")
745	stmtSaveOnt = preparetodie(db, "insert into onts (ontology, honkid) values (?, ?)")
746	stmtDeleteOnts = preparetodie(db, "delete from onts where honkid = ?")
747	stmtSaveDonk = preparetodie(db, "insert into donks (honkid, fileid) values (?, ?)")
748	stmtDeleteDonks = preparetodie(db, "delete from donks where honkid = ?")
749	stmtSaveFile = preparetodie(db, "insert into filemeta (xid, name, description, url, media, local) values (?, ?, ?, ?, ?, ?)")
750	blobdb := openblobdb()
751	stmtSaveFileData = preparetodie(blobdb, "insert into filedata (xid, media, content) values (?, ?, ?)")
752	stmtGetFileData = preparetodie(blobdb, "select media, content from filedata where xid = ?")
753	stmtFindXonk = preparetodie(db, "select honkid from honks where userid = ? and xid = ?")
754	stmtFindFile = preparetodie(db, "select fileid, xid from filemeta where url = ? and local = 1")
755	stmtUserByName = preparetodie(db, "select userid, username, displayname, about, pubkey, seckey, options from users where username = ? and userid > 0")
756	stmtUserByNumber = preparetodie(db, "select userid, username, displayname, about, pubkey, seckey, options from users where userid = ?")
757	stmtSaveDub = preparetodie(db, "insert into honkers (userid, name, xid, flavor) values (?, ?, ?, ?)")
758	stmtAddDoover = preparetodie(db, "insert into doovers (dt, tries, userid, rcpt, msg) values (?, ?, ?, ?, ?)")
759	stmtGetDoovers = preparetodie(db, "select dooverid, dt from doovers")
760	stmtLoadDoover = preparetodie(db, "select tries, userid, rcpt, msg from doovers where dooverid = ?")
761	stmtZapDoover = preparetodie(db, "delete from doovers where dooverid = ?")
762	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")
763	stmtFindZonk = preparetodie(db, "select zonkerid from zonkers where userid = ? and name = ? and wherefore = 'zonk'")
764	stmtGetZonkers = preparetodie(db, "select zonkerid, name, wherefore from zonkers where userid = ? and wherefore <> 'zonk'")
765	stmtSaveZonker = preparetodie(db, "insert into zonkers (userid, name, wherefore) values (?, ?, ?)")
766	stmtGetXonker = preparetodie(db, "select info from xonkers where name = ? and flavor = ?")
767	stmtSaveXonker = preparetodie(db, "insert into xonkers (name, info, flavor, dt) values (?, ?, ?, ?)")
768	stmtDeleteXonker = preparetodie(db, "delete from xonkers where name = ? and flavor = ? and dt < ?")
769	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")
770	stmtUpdateFlags = preparetodie(db, "update honks set flags = flags | ? where honkid = ?")
771	stmtClearFlags = preparetodie(db, "update honks set flags = flags & ~ ? where honkid = ?")
772	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")
773	stmtGetFilters = preparetodie(db, "select hfcsid, json from hfcs where userid = ?")
774	stmtSaveFilter = preparetodie(db, "insert into hfcs (userid, json) values (?, ?)")
775	stmtDeleteFilter = preparetodie(db, "delete from hfcs where userid = ? and hfcsid = ?")
776	stmtGetTracks = preparetodie(db, "select fetches from tracks where xid = ?")
777}