all repos — honk @ c54c1ec2ea1824915c806f6e1621d0dba9d8e042

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