]> jfr.im git - irc/quakenet/newserv.git/blob - sqlite/sqlite.c
merge
[irc/quakenet/newserv.git] / sqlite / sqlite.c
1 /*
2 * SQLite module
3 */
4
5 #include <stdlib.h>
6 #include <stdio.h>
7 #include <stdarg.h>
8 #include <string.h>
9
10 #define __USE_POSIX199309
11 #include <time.h>
12
13 #include "../core/config.h"
14 #include "../core/error.h"
15 #include "../core/events.h"
16 #include "../core/hooks.h"
17 #include "../lib/version.h"
18 #include "../lib/strlfunc.h"
19 #include "../core/nsmalloc.h"
20 #include "../core/schedule.h"
21
22 #define BUILDING_DBAPI
23 #include "../dbapi/dbapi.h"
24
25 #include "sqlite.h"
26
27 MODULE_VERSION("");
28
29 static int dbconnected = 0;
30 static struct sqlite3 *conn;
31 static SQLiteModuleIdentifier modid;
32
33 struct sqlitequeue {
34 sqlite3_stmt *statement;
35 SQLiteQueryHandler handler;
36 void *tag;
37 int identifier;
38 struct sqlitequeue *next;
39 };
40
41 static struct sqlitequeue *head, *tail;
42 static int queuesize;
43 static void *processsched;
44 static int inited;
45
46 #define SYNC_MODE "OFF"
47
48 static void sqlitequeueprocessor(void *arg);
49 static void dbstatus(int hooknum, void *arg);
50
51 void _init(void) {
52 sstring *dbfile;
53 int rc;
54
55 dbfile = getcopyconfigitem("sqlite", "file", "newserv.db", 100);
56
57 if(!dbfile) {
58 Error("sqlite", ERR_ERROR, "Unable to get config settings.");
59 return;
60 }
61
62 processsched = schedulerecurring(time(NULL), 0, 1, &sqlitequeueprocessor, NULL);
63 if(!processsched) {
64 Error("sqlite", ERR_ERROR, "Unable to schedule query processor.");
65 freesstring(dbfile);
66 return;
67 }
68
69
70 if(sqlite3_initialize() != SQLITE_OK) {
71 Error("sqlite", ERR_ERROR, "Unable to initialise sqlite");
72 return;
73 }
74 sqlite3_config(SQLITE_CONFIG_SINGLETHREAD);
75
76 inited = 1;
77
78 rc = sqlite3_open(dbfile->content, &conn);
79 freesstring(dbfile);
80
81 if(rc) {
82 Error("sqlite", ERR_ERROR, "Unable to connect to database: %s", sqlite3_errmsg(conn));
83 deleteschedule(processsched, &sqlitequeueprocessor, NULL);
84 return;
85 }
86
87 dbconnected = 1;
88
89 sqliteasyncqueryf(0, NULL, NULL, 0, "PRAGMA synchronous=" SYNC_MODE ";");
90 registerhook(HOOK_CORE_STATSREQUEST, dbstatus);
91 }
92
93 void _fini(void) {
94 struct sqlitequeue *q, *nq;
95
96 if(sqliteconnected()) {
97 deregisterhook(HOOK_CORE_STATSREQUEST, dbstatus);
98 deleteschedule(processsched, &sqlitequeueprocessor, NULL);
99
100 /* we assume every module that's being unloaded
101 * has us as a dependency and will have cleaned up
102 * their queries by using freeid..
103 */
104 for(q=head;q;q=nq) {
105 nq = q->next;
106 sqlite3_finalize(q->statement);
107 nsfree(POOL_SQLITE, q);
108 }
109
110 sqlite3_close(conn);
111
112 dbconnected = 0;
113 }
114
115 if(inited) {
116 sqlite3_shutdown();
117 inited = 0;
118 }
119
120 nscheckfreeall(POOL_SQLITE);
121 }
122
123 /* busy processing is done externally as that varies depending on what you are... */
124 static void processstatement(int rc, sqlite3_stmt *s, SQLiteQueryHandler handler, void *tag, char *querybuf) {
125 if(handler) { /* the handler deals with the cleanup */
126 SQLiteResult *r;
127
128 if((rc != SQLITE_ROW) && (rc != SQLITE_DONE)) {
129 Error("sqlite", ERR_WARNING, "SQL error %d: %s (query: %s)", rc, sqlite3_errmsg(conn), querybuf);
130 handler(NULL, tag);
131 return;
132 }
133
134 r = (SQLiteResult *)nsmalloc(POOL_SQLITE, sizeof(SQLiteResult));
135 r->r = s;
136 r->first = 1;
137 if(rc == SQLITE_ROW) {
138 r->final = 0;
139 } else if(rc == SQLITE_DONE) {
140 r->final = 1;
141 }
142 handler(r, tag);
143 } else {
144 if(rc == SQLITE_ROW) {
145 Error("sqlite", ERR_WARNING, "Unhandled data from query: %s", querybuf);
146 } else if(rc != SQLITE_DONE) {
147 Error("sqlite", ERR_WARNING, "SQL error %d: %s (query: %s)", rc, sqlite3_errmsg(conn), querybuf);
148 }
149 sqlite3_finalize(s);
150 }
151 }
152
153 static void pushqueue(sqlite3_stmt *s, int identifier, SQLiteQueryHandler handler, void *tag) {
154 struct sqlitequeue *q = (struct sqlitequeue *)nsmalloc(POOL_SQLITE, sizeof(struct sqlitequeue));
155
156 q->identifier = identifier;
157 q->handler = handler;
158 q->tag = tag;
159 q->next = NULL;
160 q->statement = s;
161
162 if(!tail) {
163 head = q;
164 } else {
165 tail->next = q;
166 }
167 tail = q;
168 queuesize++;
169 }
170
171 static struct sqlitequeue *peekqueue(void) {
172 return head;
173 }
174
175 /* a weird pop that doesn't return the value */
176 static void popqueue(void) {
177 struct sqlitequeue *q;
178 if(!head)
179 return;
180
181 q = head;
182 if(head == tail) {
183 head = NULL;
184 tail = NULL;
185 } else {
186 head = head->next;
187 }
188
189 nsfree(POOL_SQLITE, q);
190 queuesize--;
191 return;
192 }
193
194 void sqliteasyncqueryf(int identifier, SQLiteQueryHandler handler, void *tag, int flags, char *format, ...) {
195 char querybuf[8192];
196 int len;
197 int rc;
198 sqlite3_stmt *s;
199 va_list va;
200
201 if(!sqliteconnected())
202 return;
203
204 va_start(va, format);
205 len = vsnprintf(querybuf, sizeof(querybuf), format, va);
206 va_end(va);
207
208 rc = sqlite3_prepare(conn, querybuf, -1, &s, NULL);
209 if(rc != SQLITE_OK) {
210 if(flags != DB_CREATE)
211 Error("sqlite", ERR_WARNING, "SQL error %d: %s (query: %s)", rc, sqlite3_errmsg(conn), querybuf);
212 if(handler)
213 handler(NULL, tag);
214 return;
215 }
216
217 if(head) { /* stuff already queued */
218 pushqueue(s, identifier, handler, tag);
219 return;
220 }
221
222 rc = sqlite3_step(s);
223 if(rc == SQLITE_BUSY) {
224 pushqueue(s, identifier, handler, tag);
225 return;
226 }
227
228 processstatement(rc, s, handler, tag, querybuf);
229 }
230
231 int sqliteconnected(void) {
232 return dbconnected;
233 }
234
235 size_t sqliteescapestring(char *buf, char *src, unsigned int len) {
236 unsigned int i;
237 char *p, *d;
238
239 for(p=src,d=buf,i=0;i<len;i++,p++) {
240 if(*p == '\'')
241 *d++ = *p;
242 *d++ = *p;
243 }
244 *d = '\0';
245
246 return d - buf;
247 }
248
249 SQLiteResult *sqlitegetresult(SQLiteConn *r) {
250 return r;
251 }
252
253 int sqlitefetchrow(SQLiteResult *r) {
254 int rc, v;
255 struct timespec t;
256
257 if(!r || !r->r || r->final)
258 return 0;
259
260 if(r->first) { /* we've extracted the first row already */
261 r->first = 0;
262 return 1;
263 }
264
265 t.tv_sec = 0;
266 for(;;) {
267 rc = sqlite3_step(r->r);
268 if(rc != SQLITE_BUSY)
269 break;
270
271 v = rand() % 50 + 50;
272 t.tv_nsec = v * 1000000;
273 Error("sqlite", ERR_WARNING, "SQLite is busy, retrying in %fs...", (double)v / 1000);
274 nanosleep(&t, NULL);
275 }
276
277 if(rc == SQLITE_DONE) {
278 r->final = 1;
279 return 0;
280 }
281
282 if(rc != SQLITE_ROW) {
283 Error("sqlite", ERR_WARNING, "SQL error %d: %s", rc, sqlite3_errmsg(conn));
284 r->final = 1;
285 return 0;
286 }
287
288 return 1;
289 }
290
291 void sqliteclear(SQLiteResult *r) {
292 if(!r)
293 return;
294
295 if(r->r)
296 sqlite3_finalize(r->r);
297
298 nsfree(POOL_SQLITE, r);
299 }
300
301 int sqlitequerysuccessful(SQLiteResult *r) {
302 if(r && r->r)
303 return 1;
304
305 return 0;
306 }
307
308 struct sqlitetableloader {
309 SQLiteQueryHandler init, data, fini;
310 void *tag;
311 char tablename[];
312 };
313
314 static void loadtablerows(SQLiteConn *c, void *tag) {
315 struct sqlitetableloader *t = (struct sqlitetableloader *)tag;
316
317 if(!c) { /* pqsql doesnt call the handlers so we don't either */
318 nsfree(POOL_SQLITE, t);
319 return;
320 }
321
322 /* the handlers do all the checking and cleanup */
323 if(t->init)
324 (t->init)(NULL, t->tag);
325
326 (t->data)(c, t->tag);
327
328 if(t->fini)
329 (t->fini)(NULL, t->tag);
330
331 nsfree(POOL_SQLITE, t);
332 }
333
334 static void loadtablecount(SQLiteConn *c, void *tag) {
335 struct sqlitetableloader *t = (struct sqlitetableloader *)tag;
336 SQLiteResult *r = NULL;
337
338 if(!c) { /* unloaded */
339 nsfree(POOL_SQLITE, t);
340 return;
341 }
342
343 if(!(r = sqlitegetresult(c)) || !sqlitefetchrow(r)) {
344 Error("sqlite", ERR_ERROR, "Error getting row count for %s.", t->tablename);
345 nsfree(POOL_SQLITE, t);
346
347 if(r)
348 sqliteclear(r);
349 return;
350 }
351
352 Error("sqlite", ERR_INFO, "Found %s entries in table %s, loading...", (char *)sqlite3_column_text(r->r, 0), t->tablename);
353 sqliteclear(r);
354
355 sqliteasyncqueryf(0, loadtablerows, t, 0, "SELECT * FROM %s", t->tablename);
356 }
357
358 void sqliteloadtable(char *tablename, SQLiteQueryHandler init, SQLiteQueryHandler data, SQLiteQueryHandler fini, void *tag) {
359 struct sqlitetableloader *t;
360 int len;
361
362 if(!sqliteconnected())
363 return;
364
365 len = strlen(tablename);
366
367 t = (struct sqlitetableloader *)nsmalloc(POOL_SQLITE, sizeof(struct sqlitetableloader) + len + 1);
368 memcpy(t->tablename, tablename, len + 1);
369 t->init = init;
370 t->data = data;
371 t->fini = fini;
372 t->tag = tag;
373
374 sqliteasyncqueryf(0, loadtablecount, t, 0, "SELECT COUNT(*) FROM %s", tablename);
375 }
376
377 void sqliteattach(char *schema) {
378 sqliteasyncqueryf(0, NULL, NULL, 0, "ATTACH DATABASE '%s.db' AS %s", schema, schema);
379 sqliteasyncqueryf(0, NULL, NULL, 0, "PRAGMA %s.synchronous=" SYNC_MODE, schema);
380 }
381
382 void sqlitedetach(char *schema) {
383 sqliteasyncqueryf(0, NULL, NULL, 0, "DETACH DATABASE %s", schema);
384 }
385
386 int sqlitegetid(void) {
387 modid++;
388 if(modid == 0)
389 modid = 1;
390
391 return modid;
392 }
393
394 void sqlitefreeid(int id) {
395 struct sqlitequeue *q, *pq;
396 if(id == 0)
397 return;
398
399 for(pq=NULL,q=head;q;) {
400 if(q->identifier == id) {
401 if(pq) {
402 pq->next = q->next;
403 if(q == tail)
404 tail = pq;
405 } else { /* head */
406 head = q->next;
407 if(q == tail)
408 tail = NULL;
409 }
410 sqlite3_finalize(q->statement);
411
412 q->handler(NULL, q->tag);
413 nsfree(POOL_SQLITE, q);
414
415 queuesize--;
416 if(pq) {
417 q = pq->next;
418 } else {
419 q = head;
420 }
421 } else {
422 pq = q;
423 q = q->next;
424 }
425 }
426 }
427
428 static void sqlitequeueprocessor(void *arg) {
429 struct sqlitequeue *q = peekqueue();
430
431 while(q) {
432 int rc = sqlite3_step(q->statement);
433 if(rc == SQLITE_BUSY)
434 return;
435
436 processstatement(rc, q->statement, q->handler, q->tag, "??");
437 popqueue();
438
439 q = peekqueue();
440 }
441 }
442
443 static void dbstatus(int hooknum, void *arg) {
444 if((long)arg > 10) {
445 char message[100];
446
447 snprintf(message, sizeof(message), "SQLite : %6d queries queued.", queuesize);
448 triggerhook(HOOK_CORE_STATSREPLY, message);
449 }
450 }
451