all repos — honk @ b8baa2f32d97b62764e84d59d0f2ca7b969475c3

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