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