comparison database.c @ 79:52029a52a385

pasterd: revert using ktemplate
author David Demelier <markand@malikania.fr>
date Fri, 17 Mar 2023 07:43:20 +0100
parents 9bfe5ce3cc45
children 94dcca86e5cc
comparison
equal deleted inserted replaced
78:9bfe5ce3cc45 79:52029a52a385
22 #include <string.h> 22 #include <string.h>
23 23
24 #include <sqlite3.h> 24 #include <sqlite3.h>
25 25
26 #include "database.h" 26 #include "database.h"
27 #include "json-util.h"
28 #include "log.h" 27 #include "log.h"
28 #include "paste.h"
29 #include "util.h" 29 #include "util.h"
30 30
31 #include "sql/clear.h" 31 #include "sql/clear.h"
32 #include "sql/get.h" 32 #include "sql/get.h"
33 #include "sql/init.h" 33 #include "sql/init.h"
34 #include "sql/insert.h" 34 #include "sql/insert.h"
35 #include "sql/recents.h" 35 #include "sql/recents.h"
36 #include "sql/search.h" 36 #include "sql/search.h"
37 37
38 #define ID_MAX (12 + 1) 38 #define CHAR(sql) (const char *)(sql)
39 39
40 static sqlite3 *db; 40 static sqlite3 *db;
41 41
42 static inline json_t * 42 static char *
43 convert(sqlite3_stmt *stmt) 43 dup(const unsigned char *s)
44 { 44 {
45 return json_pack("{ss ss ss ss ss sI si si}", 45 return estrdup(s ? (const char *)(s) : "");
46 "id", sqlite3_column_text(stmt, 0), 46 }
47 "title", sqlite3_column_text(stmt, 1), 47
48 "author", sqlite3_column_text(stmt, 2), 48 static void
49 "language", sqlite3_column_text(stmt, 3), 49 convert(sqlite3_stmt *stmt, struct paste *paste)
50 "code", sqlite3_column_text(stmt, 4), 50 {
51 "timestamp", (json_int_t)sqlite3_column_int64(stmt, 5), 51 paste->id = dup(sqlite3_column_text(stmt, 0));
52 "visible", sqlite3_column_int(stmt, 6), 52 paste->title = dup(sqlite3_column_text(stmt, 1));
53 "duration", sqlite3_column_int(stmt, 7) 53 paste->author = dup(sqlite3_column_text(stmt, 2));
54 ); 54 paste->language = dup(sqlite3_column_text(stmt, 3));
55 paste->code = dup(sqlite3_column_text(stmt, 4));
56 paste->timestamp = sqlite3_column_int64(stmt, 5);
57 paste->visible = sqlite3_column_int(stmt, 6);
58 paste->duration = sqlite3_column_int64(stmt, 7);
55 } 59 }
56 60
57 static int 61 static int
58 exists(const char *id) 62 exists(const char *id)
59 { 63 {
60 assert(id); 64 assert(id);
61 65
62 sqlite3_stmt *stmt = NULL; 66 sqlite3_stmt *stmt = NULL;
63 int ret = 1; 67 int ret = 1;
64 68
65 if (sqlite3_prepare(db, sql_get, -1, &stmt, NULL) == SQLITE_OK) { 69 if (sqlite3_prepare(db, CHAR(sql_get), -1, &stmt, NULL) == SQLITE_OK) {
66 sqlite3_bind_text(stmt, 1, id, -1, NULL); 70 sqlite3_bind_text(stmt, 1, id, -1, NULL);
67 ret = sqlite3_step(stmt) == SQLITE_ROW; 71 ret = sqlite3_step(stmt) == SQLITE_ROW;
68 sqlite3_finalize(stmt); 72 sqlite3_finalize(stmt);
69 } 73 }
70 74
71 return ret; 75 return ret;
72 } 76 }
73 77
74 static const char * 78 static const char *
75 create_id(char *id) 79 create_id(void)
76 { 80 {
77 static const char table[] = "abcdefghijklmnopqrstuvwxyz1234567890"; 81 static const char table[] = "abcdefghijklmnopqrstuvwxyz1234567890";
78 82 static char id[12];
79 for (int i = 0; i < ID_MAX; ++i) 83
84 for (size_t i = 0; i < sizeof (id); ++i)
80 id[i] = table[rand() % (sizeof (table) - 1)]; 85 id[i] = table[rand() % (sizeof (table) - 1)];
81 86
82 id[ID_MAX - 1] = 0; 87 return id;
83 } 88 }
84 89
85 static int 90 static int
86 set_id(json_t *paste) 91 set_id(struct paste *paste)
87 { 92 {
93 assert(paste);
94
95 paste->id = NULL;
96
88 /* 97 /*
89 * Avoid infinite loop, we only try to create a new id in 30 steps. 98 * Avoid infinite loop, we only try to create a new id in 30 steps.
90 * 99 *
91 * On error, the function `exist` returns true to indicate we should 100 * On error, the function `exist` returns true to indicate we should
92 * not try to save with that id. 101 * not try to save with that id.
93 */ 102 */
94 int tries = 0; 103 int tries = 0;
95 char id[ID_MAX];
96 104
97 do { 105 do {
98 create_id(id); 106 free(paste->id);
99 } while (++tries < 30 && exists(id)); 107 paste->id = estrdup(create_id());
100 108 } while (++tries < 30 && exists(paste->id));
101 if (tries >= 30) 109
102 return -1; 110 return tries < 30 ? 0 : -1;
103
104 json_object_set_new(paste, "id", json_string(id));
105
106 return 0;
107 } 111 }
108 112
109 int 113 int
110 database_open(const char *path) 114 database_open(const char *path)
111 { 115 {
119 } 123 }
120 124
121 /* Wait for 30 seconds to lock the database. */ 125 /* Wait for 30 seconds to lock the database. */
122 sqlite3_busy_timeout(db, 30000); 126 sqlite3_busy_timeout(db, 30000);
123 127
124 if (sqlite3_exec(db, sql_init, NULL, NULL, NULL) != SQLITE_OK) { 128 if (sqlite3_exec(db, CHAR(sql_init), NULL, NULL, NULL) != SQLITE_OK) {
125 log_warn("database: unable to initialize %s: %s", path, sqlite3_errmsg(db)); 129 log_warn("database: unable to initialize %s: %s", path, sqlite3_errmsg(db));
126 return -1; 130 return -1;
127 } 131 }
128 132
129 return 0; 133 return 0;
130 } 134 }
131 135
132 json_t * 136 int
133 database_recents(size_t limit) 137 database_recents(struct paste *pastes, size_t *max)
134 { 138 {
135 json_t *array = NULL; 139 assert(pastes);
140 assert(max);
141
136 sqlite3_stmt *stmt = NULL; 142 sqlite3_stmt *stmt = NULL;
137 size_t i = 0; 143 size_t i = 0;
138 144
145 memset(pastes, 0, *max * sizeof (struct paste));
139 log_debug("database: accessing most recents"); 146 log_debug("database: accessing most recents");
140 147
141 if (sqlite3_prepare(db, sql_recents, -1, &stmt, NULL) != SQLITE_OK || 148 if (sqlite3_prepare(db, CHAR(sql_recents), -1, &stmt, NULL) != SQLITE_OK ||
142 sqlite3_bind_int64(stmt, 1, limit) != SQLITE_OK) 149 sqlite3_bind_int64(stmt, 1, *max) != SQLITE_OK)
143 goto sqlite_err; 150 goto sqlite_err;
144 151
145 array = json_array(); 152 for (; i < *max && sqlite3_step(stmt) == SQLITE_ROW; ++i)
146 153 convert(stmt, &pastes[i]);
147 for (; i < limit && sqlite3_step(stmt) == SQLITE_ROW; ++i)
148 json_array_append_new(array, convert(stmt));
149 154
150 log_debug("database: found %zu pastes", i); 155 log_debug("database: found %zu pastes", i);
151 sqlite3_finalize(stmt); 156 sqlite3_finalize(stmt);
152 157 *max = i;
153 return array; 158
159 return 0;
154 160
155 sqlite_err: 161 sqlite_err:
156 log_warn("database: error (recents): %s\n", sqlite3_errmsg(db)); 162 log_warn("database: error (recents): %s\n", sqlite3_errmsg(db));
157 163
158 if (stmt) 164 if (stmt)
159 sqlite3_finalize(stmt); 165 sqlite3_finalize(stmt);
160 166
161 return NULL; 167 *max = 0;
162 } 168
163 169 return -1;
164 json_t * 170 }
165 database_get(const char *id) 171
166 { 172 int
173 database_get(struct paste *paste, const char *id)
174 {
175 assert(paste);
167 assert(id); 176 assert(id);
168 177
169 json_t *object = NULL;
170 sqlite3_stmt* stmt = NULL; 178 sqlite3_stmt* stmt = NULL;
171 179 int found = -1;
172 log_debug("database: accessing paste with uuid: %s", id); 180
173 181 memset(paste, 0, sizeof (struct paste));
174 if (sqlite3_prepare(db, sql_get, -1, &stmt, NULL) != SQLITE_OK || 182 log_debug("database: accessing paste with id: %s", id);
183
184 if (sqlite3_prepare(db, CHAR(sql_get), -1, &stmt, NULL) != SQLITE_OK ||
175 sqlite3_bind_text(stmt, 1, id, -1, NULL) != SQLITE_OK) 185 sqlite3_bind_text(stmt, 1, id, -1, NULL) != SQLITE_OK)
176 goto sqlite_err; 186 goto sqlite_err;
177 187
178 switch (sqlite3_step(stmt)) { 188 switch (sqlite3_step(stmt)) {
179 case SQLITE_ROW: 189 case SQLITE_ROW:
180 object = convert(stmt); 190 convert(stmt, paste);
191 found = 0;
181 break; 192 break;
182 case SQLITE_MISUSE: 193 case SQLITE_MISUSE:
183 case SQLITE_ERROR: 194 case SQLITE_ERROR:
184 goto sqlite_err; 195 goto sqlite_err;
185 default: 196 default:
186 break; 197 break;
187 } 198 }
188 199
189 sqlite3_finalize(stmt); 200 sqlite3_finalize(stmt);
190 201
191 return object; 202 return found;
192 203
193 sqlite_err: 204 sqlite_err:
194 if (stmt) 205 if (stmt)
195 sqlite3_finalize(stmt); 206 sqlite3_finalize(stmt);
196 207
197 log_warn("database: error (get): %s", sqlite3_errmsg(db)); 208 log_warn("database: error (get): %s", sqlite3_errmsg(db));
198 209
199 return NULL; 210 return -1;
200 } 211 }
201 212
202 int 213 int
203 database_insert(json_t *paste) 214 database_insert(struct paste *paste)
204 { 215 {
205 assert(paste); 216 assert(paste);
206 217
207 sqlite3_stmt *stmt = NULL; 218 sqlite3_stmt *stmt = NULL;
208 219
210 221
211 if (sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) { 222 if (sqlite3_exec(db, "BEGIN EXCLUSIVE TRANSACTION", NULL, NULL, NULL) != SQLITE_OK) {
212 log_warn("database: could not lock database: %s", sqlite3_errmsg(db)); 223 log_warn("database: could not lock database: %s", sqlite3_errmsg(db));
213 return -1; 224 return -1;
214 } 225 }
215
216 if (set_id(paste) < 0) { 226 if (set_id(paste) < 0) {
217 log_warn("database: unable to randomize unique identifier"); 227 log_warn("database: unable to randomize unique identifier");
218 sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL); 228 sqlite3_exec(db, "END TRANSACTION", NULL, NULL, NULL);
219 return -1; 229 return -1;
220 } 230 }
221 231 if (sqlite3_prepare(db, CHAR(sql_insert), -1, &stmt, NULL) != SQLITE_OK)
222 if (sqlite3_prepare(db, sql_insert, -1, &stmt, NULL) != SQLITE_OK) 232 goto sqlite_err;
223 goto sqlite_err; 233
224 234 sqlite3_bind_text(stmt, 1, paste->id, -1, SQLITE_STATIC);
225 sqlite3_bind_text(stmt, 1, ju_get_string(paste, "id"), -1, SQLITE_STATIC); 235 sqlite3_bind_text(stmt, 2, paste->title, -1, SQLITE_STATIC);
226 sqlite3_bind_text(stmt, 2, ju_get_string(paste, "title"), -1, SQLITE_STATIC); 236 sqlite3_bind_text(stmt, 3, paste->author, -1, SQLITE_STATIC);
227 sqlite3_bind_text(stmt, 3, ju_get_string(paste, "author"), -1, SQLITE_STATIC); 237 sqlite3_bind_text(stmt, 4, paste->language, -1, SQLITE_STATIC);
228 sqlite3_bind_text(stmt, 4, ju_get_string(paste, "language"), -1, SQLITE_STATIC); 238 sqlite3_bind_text(stmt, 5, paste->code, -1, SQLITE_STATIC);
229 sqlite3_bind_text(stmt, 5, ju_get_string(paste, "code"), -1, SQLITE_STATIC); 239 sqlite3_bind_int(stmt, 6, paste->visible);
230 sqlite3_bind_int(stmt, 6, ju_get_bool(paste, "visible")); 240 sqlite3_bind_int64(stmt, 7, paste->duration);
231 sqlite3_bind_int64(stmt, 7, ju_get_int(paste, "duration"));
232 241
233 if (sqlite3_step(stmt) != SQLITE_DONE) 242 if (sqlite3_step(stmt) != SQLITE_DONE)
234 goto sqlite_err; 243 goto sqlite_err;
235 244
236 sqlite3_exec(db, "COMMIT", NULL, NULL, NULL); 245 sqlite3_exec(db, "COMMIT", NULL, NULL, NULL);
237 sqlite3_finalize(stmt); 246 sqlite3_finalize(stmt);
238 247
239 log_info("database: new paste (%s) from %s expires in one %lld seconds", 248 log_info("database: new paste (%s) from %s expires in one %lld seconds",
240 ju_get_string(paste, "id"), 249 paste->id, paste->author, paste->duration);
241 ju_get_string(paste, "author"),
242 ju_get_int(paste, "duration")
243 );
244 250
245 return 0; 251 return 0;
246 252
247 sqlite_err: 253 sqlite_err:
248 log_warn("database: error (insert): %s", sqlite3_errmsg(db)); 254 log_warn("database: error (insert): %s", sqlite3_errmsg(db));
249 sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL); 255 sqlite3_exec(db, "ROLLBACK", NULL, NULL, NULL);
250 256
251 if (stmt) 257 if (stmt)
252 sqlite3_finalize(stmt); 258 sqlite3_finalize(stmt);
253 259
254 /* Make sure it is not used anymore. */ 260 free(paste->id);
255 json_object_del(paste, "id"); 261 paste->id = NULL;
256 262
257 return 0; 263 return -1;
258 } 264 }
259 265
260 json_t * 266 int
261 database_search(size_t limit, 267 database_search(struct paste *pastes,
268 size_t *max,
262 const char *title, 269 const char *title,
263 const char *author, 270 const char *author,
264 const char *language) 271 const char *language)
265 { 272 {
266 json_t *array = NULL; 273 assert(pastes);
274 assert(max);
275
267 sqlite3_stmt *stmt = NULL; 276 sqlite3_stmt *stmt = NULL;
268 size_t i = 0; 277 size_t i = 0;
269 278
270 log_debug("database: searching title=%s, author=%s, language=%s", 279 log_debug("database: searching title=%s, author=%s, language=%s",
271 title ? title : "", 280 title ? title : "",
272 author ? author : "", 281 author ? author : "",
273 language ? language : ""); 282 language ? language : "");
274 283
284 memset(pastes, 0, *max * sizeof (struct paste));
285
275 /* Select everything if not specified. */ 286 /* Select everything if not specified. */
276 title = title ? title : "%"; 287 title = title ? title : "%";
277 author = author ? author : "%"; 288 author = author ? author : "%";
278 language = language ? language : "%"; 289 language = language ? language : "%";
279 290
280 if (sqlite3_prepare(db, sql_search, -1, &stmt, NULL) != SQLITE_OK) 291 if (sqlite3_prepare(db, CHAR(sql_search), -1, &stmt, NULL) != SQLITE_OK)
281 goto sqlite_err; 292 goto sqlite_err;
282 if (sqlite3_bind_text(stmt, 1, title, -1, NULL) != SQLITE_OK) 293 if (sqlite3_bind_text(stmt, 1, title, -1, NULL) != SQLITE_OK)
283 goto sqlite_err; 294 goto sqlite_err;
284 if (sqlite3_bind_text(stmt, 2, author, -1, NULL) != SQLITE_OK) 295 if (sqlite3_bind_text(stmt, 2, author, -1, NULL) != SQLITE_OK)
285 goto sqlite_err; 296 goto sqlite_err;
286 if (sqlite3_bind_text(stmt, 3, language, -1, NULL) != SQLITE_OK) 297 if (sqlite3_bind_text(stmt, 3, language, -1, NULL) != SQLITE_OK)
287 goto sqlite_err; 298 goto sqlite_err;
288 if (sqlite3_bind_int64(stmt, 4, limit) != SQLITE_OK) 299 if (sqlite3_bind_int64(stmt, 4, *max) != SQLITE_OK)
289 goto sqlite_err; 300 goto sqlite_err;
290 301
291 array = json_array(); 302 for (; i < *max && sqlite3_step(stmt) == SQLITE_ROW; ++i)
292 303 convert(stmt, &pastes[i]);
293 for (; i < limit && sqlite3_step(stmt) == SQLITE_ROW; ++i)
294 json_array_append_new(array, convert(stmt));
295 304
296 log_debug("database: found %zu pastes", i); 305 log_debug("database: found %zu pastes", i);
297 sqlite3_finalize(stmt); 306 sqlite3_finalize(stmt);
298 307 *max = i;
299 return array; 308
309 return 0;
300 310
301 sqlite_err: 311 sqlite_err:
302 log_warn("database: error (search): %s\n", sqlite3_errmsg(db)); 312 log_warn("database: error (search): %s\n", sqlite3_errmsg(db));
303 313
304 if (stmt) 314 if (stmt)
305 sqlite3_finalize(stmt); 315 sqlite3_finalize(stmt);
306 316
307 return NULL; 317 *max = 0;
318
319 return -1;
308 } 320 }
309 321
310 void 322 void
311 database_clear(void) 323 database_clear(void)
312 { 324 {
313 log_debug("database: clearing deprecated pastes"); 325 log_debug("database: clearing deprecated pastes");
314 326
315 if (sqlite3_exec(db, sql_clear, NULL, NULL, NULL) != SQLITE_OK) 327 if (sqlite3_exec(db, CHAR(sql_clear), NULL, NULL, NULL) != SQLITE_OK)
316 log_warn("database: error (clear): %s\n", sqlite3_errmsg(db)); 328 log_warn("database: error (clear): %s\n", sqlite3_errmsg(db));
317 } 329 }
318 330
319 void 331 void
320 database_finish(void) 332 database_finish(void)