backupdb.go (view raw)
1//
2// Copyright (c) 2020 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 "database/sql"
20 "fmt"
21 "os"
22 "time"
23
24 "strings"
25)
26
27func qordie(db *sql.DB, s string, args ...interface{}) *sql.Rows {
28 rows, err := db.Query(s, args...)
29 if err != nil {
30 elog.Fatalf("can't query %s: %s", s, err)
31 }
32 return rows
33}
34
35func scanordie(rows *sql.Rows, args ...interface{}) {
36 err := rows.Scan(args...)
37 if err != nil {
38 elog.Fatalf("can't scan: %s", err)
39 }
40}
41
42func svalbard(dirname string) {
43 err := os.Mkdir(dirname, 0700)
44 if err != nil && !os.IsExist(err) {
45 elog.Fatalf("can't create directory: %s", dirname)
46 }
47 now := time.Now().Unix()
48 backupdbname := fmt.Sprintf("%s/honk-%d.db", dirname, now)
49 backup, err := sql.Open("sqlite3", backupdbname)
50 if err != nil {
51 elog.Fatalf("can't open backup database")
52 }
53 _, err = backup.Exec("PRAGMA journal_mode=WAL")
54 for _, line := range strings.Split(sqlSchema, ";") {
55 _, err = backup.Exec(line)
56 if err != nil {
57 elog.Fatal(err)
58 return
59 }
60 }
61 tx, err := backup.Begin()
62 if err != nil {
63 elog.Fatal(err)
64 }
65 orig := opendatabase()
66 rows := qordie(orig, "select userid, username, hash, displayname, about, pubkey, seckey, options from users")
67 for rows.Next() {
68 var userid int64
69 var username, hash, displayname, about, pubkey, seckey, options string
70 scanordie(rows, &userid, &username, &hash, &displayname, &about, &pubkey, &seckey, &options)
71 doordie(tx, "insert into users (userid, username, hash, displayname, about, pubkey, seckey, options) values (?, ?, ?, ?, ?, ?, ?, ?)", userid, username, hash, displayname, about, pubkey, seckey, options)
72 }
73 rows.Close()
74
75 rows = qordie(orig, "select honkerid, userid, name, xid, flavor, combos, owner, meta, folxid from honkers")
76 for rows.Next() {
77 var honkerid, userid int64
78 var name, xid, flavor, combos, owner, meta, folxid string
79 scanordie(rows, &honkerid, &userid, &name, &xid, &flavor, &combos, &owner, &meta, &folxid)
80 doordie(tx, "insert into honkers (honkerid, userid, name, xid, flavor, combos, owner, meta, folxid) values (?, ?, ?, ?, ?, ?, ?, ?, ?)", honkerid, userid, name, xid, flavor, combos, owner, meta, folxid)
81 }
82 rows.Close()
83
84 rows = qordie(orig, "select convoy from honks where flags & 4 or whofore = 2 or whofore = 3")
85 convoys := make(map[string]bool)
86 for rows.Next() {
87 var convoy string
88 scanordie(rows, &convoy)
89 convoys[convoy] = true
90 }
91 rows.Close()
92
93 honkids := make(map[int64]bool)
94 for c := range convoys {
95 rows = qordie(orig, "select honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, plain from honks where convoy = ?", c)
96 for rows.Next() {
97 var honkid, userid int64
98 var what, honker, xid, rid, dt, url, audience, noise, convoy, plain string
99 var whofore int64
100 var format, precis, oonker string
101 var flags int64
102 scanordie(rows, &honkid, &userid, &what, &honker, &xid, &rid, &dt, &url, &audience, &noise, &convoy, &whofore, &format, &precis, &oonker, &flags, &plain)
103 honkids[honkid] = true
104 doordie(tx, "insert into honks (honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, plain) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", honkid, userid, what, honker, xid, rid, dt, url, audience, noise, convoy, whofore, format, precis, oonker, flags, plain)
105 }
106 rows.Close()
107 }
108 fileids := make(map[int64]bool)
109 for h := range honkids {
110 rows = qordie(orig, "select honkid, chonkid, fileid from donks where honkid = ?", h)
111 for rows.Next() {
112 var honkid, chonkid, fileid int64
113 scanordie(rows, &honkid, &chonkid, &fileid)
114 fileids[fileid] = true
115 doordie(tx, "insert into donks (honkid, chonkid, fileid) values (?, ?, ?)", honkid, chonkid, fileid)
116 }
117 rows.Close()
118 rows = qordie(orig, "select ontology, honkid from onts where honkid = ?", h)
119 for rows.Next() {
120 var ontology string
121 var honkid int64
122 scanordie(rows, &ontology, &honkid)
123 doordie(tx, "insert into onts (ontology, honkid) values (?, ?)", ontology, honkid)
124 }
125 rows.Close()
126 rows = qordie(orig, "select honkid, genus, json from honkmeta where honkid = ?", h)
127 for rows.Next() {
128 var honkid int64
129 var genus, json string
130 scanordie(rows, &honkid, &genus, &json)
131 doordie(tx, "insert into honkmeta (honkid, genus, json) values (?, ?, ?)", honkid, genus, json)
132 }
133 rows.Close()
134 }
135 chonkids := make(map[int64]bool)
136 rows = qordie(orig, "select chonkid, userid, xid, who, target, dt, noise, format from chonks")
137 for rows.Next() {
138 var chonkid, userid int64
139 var xid, who, target, dt, noise, format string
140 scanordie(rows, &chonkid, &userid, &xid, &who, &target, &dt, &noise, &format)
141 chonkids[chonkid] = true
142 doordie(tx, "insert into chonks (chonkid, userid, xid, who, target, dt, noise, format) values (?, ?, ?, ?, ?, ?, ?, ?)", chonkid, userid, xid, who, target, dt, noise, format)
143 }
144 rows.Close()
145 for c := range chonkids {
146 rows = qordie(orig, "select honkid, chonkid, fileid from donks where chonkid = ?", c)
147 for rows.Next() {
148 var honkid, chonkid, fileid int64
149 scanordie(rows, &honkid, &chonkid, &fileid)
150 fileids[fileid] = true
151 doordie(tx, "insert into donks (honkid, chonkid, fileid) values (?, ?, ?)", honkid, chonkid, fileid)
152 }
153 rows.Close()
154 }
155 filexids := make(map[string]bool)
156 for f := range fileids {
157 rows = qordie(orig, "select fileid, xid, name, description, url, media, local from filemeta where fileid = ?", f)
158 for rows.Next() {
159 var fileid int64
160 var xid, name, description, url, media string
161 var local int64
162 scanordie(rows, &fileid, &xid, &name, &description, &url, &media, &local)
163 filexids[xid] = true
164 doordie(tx, "insert into filemeta (fileid, xid, name, description, url, media, local) values (?, ?, ?, ?, ?, ?, ?)", fileid, xid, name, description, url, media, local)
165 }
166 rows.Close()
167 }
168
169 rows = qordie(orig, "select key, value from config")
170 for rows.Next() {
171 var key string
172 var value interface{}
173 scanordie(rows, &key, &value)
174 doordie(tx, "insert into config (key, value) values (?, ?)", key, value)
175 }
176
177 err = tx.Commit()
178 if err != nil {
179 elog.Fatalf("can't commit backp: %s", err)
180 }
181 backup.Close()
182
183 backupblobname := fmt.Sprintf("%s/blob-%d.db", dirname, now)
184 blob, err := sql.Open("sqlite3", backupblobname)
185 if err != nil {
186 elog.Fatalf("can't open backup blob database")
187 }
188 _, err = blob.Exec("PRAGMA journal_mode=WAL")
189 doordie(blob, "create table filedata (xid text, media text, hash text, content blob)")
190 doordie(blob, "create index idx_filexid on filedata(xid)")
191 doordie(blob, "create index idx_filehash on filedata(hash)")
192 tx, err = blob.Begin()
193 if err != nil {
194 elog.Fatalf("can't start transaction: %s", err)
195 }
196 origblob := openblobdb()
197 for x := range filexids {
198 rows = qordie(origblob, "select xid, media, hash, content from filedata where xid = ?", x)
199 for rows.Next() {
200 var xid, media, hash string
201 var content sql.RawBytes
202 scanordie(rows, &xid, &media, &hash, &content)
203 doordie(tx, "insert into filedata (xid, media, hash, content) values (?, ?, ?, ?)", xid, media, hash, content)
204 }
205 rows.Close()
206 }
207
208 err = tx.Commit()
209 if err != nil {
210 elog.Fatalf("can't commit blobs: %s", err)
211 }
212 blob.Close()
213}