]> jfr.im git - irc/quakenet/newserv.git/blob - dbapi2/dbapi2.c
LUA: port luadb to dbapi2 to drop postgres dependency
[irc/quakenet/newserv.git] / dbapi2 / dbapi2.c
1 #define MAX_PROVIDERS 10
2 #define PROVIDER_NAME_LEN 100
3 #define QUERYBUFLEN 8192*2
4
5 #define VSNPF_MAXARGS 20
6 #define VSNPF_MAXARGLEN 2048
7
8 #include <string.h>
9 #include <stdlib.h>
10 #include <stdarg.h>
11 #include <time.h>
12 #include <stdint.h>
13
14 #include "../core/error.h"
15 #include "../lib/strlfunc.h"
16 #include "../lib/stringbuf.h"
17 #include "../lib/version.h"
18 #include "dbapi2.h"
19
20 MODULE_VERSION("");
21
22 struct DBAPIProviderData {
23 char name[PROVIDER_NAME_LEN+1];
24 };
25
26 static DBAPIProvider *providerobjs[MAX_PROVIDERS];
27 static struct DBAPIProviderData providerdata[MAX_PROVIDERS];
28
29 static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap);
30
31 void _init(void) {
32 memset(providerobjs, 0, sizeof(providerobjs));
33 }
34
35 void _fini(void) {
36 /* everything should be unregistered already */
37 }
38
39 int registerdbprovider(const char *name, DBAPIProvider *provider) {
40 int i;
41
42 for(i=0;i<MAX_PROVIDERS;i++) {
43 if(providerobjs[i])
44 continue;
45
46 providerobjs[i] = provider;
47 providerobjs[i]->__providerdata = &providerdata[i];
48
49 strlcpy(providerobjs[i]->__providerdata->name, name, PROVIDER_NAME_LEN);
50
51 Error("dbapi2", ERR_INFO, "Database API registered: %s", name);
52
53 return i;
54 }
55
56 Error("dbapi2", ERR_WARNING, "No remaining space for new database provider: %s", name);
57 return -1;
58 }
59
60 void deregisterdbprovider(int handle) {
61 if(handle < 0)
62 return;
63
64 providerobjs[handle] = NULL;
65 }
66
67 static void dbclose(DBAPIConn *db) {
68 db->__close(db);
69 free((DBAPIConn *)db);
70 }
71
72 static void dbunsafequery(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, ...) {
73 va_list ap;
74 char buf[QUERYBUFLEN];
75 size_t ret;
76
77 va_start(ap, format);
78 ret = vsnprintf(buf, sizeof(buf), format, ap);
79 va_end(ap);
80
81 if(ret >= sizeof(buf))
82 Error("dbapi2", ERR_STOP, "Query truncated in dbunsafequery, format: '%s', database: %s", format, db->name);
83
84 db->__query(db, cb, data, buf);
85 }
86
87 static void dbunsafecreatetable(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, ...) {
88 va_list ap;
89 char buf[QUERYBUFLEN];
90 size_t ret;
91
92 va_start(ap, format);
93 ret = vsnprintf(buf, sizeof(buf), format, ap);
94 va_end(ap);
95
96 if(ret >= sizeof(buf))
97 Error("dbapi2", ERR_STOP, "Query truncated in dbunsafecreatetable, format: '%s', database: %s", format, db->name);
98
99 db->__createtable(db, cb, data, buf);
100 }
101
102 static void dbunsafesimplequery(const DBAPIConn *db, const char *format, ...) {
103 va_list ap;
104 char buf[QUERYBUFLEN];
105 size_t ret;
106
107 va_start(ap, format);
108 ret = vsnprintf(buf, sizeof(buf), format, ap);
109 va_end(ap);
110
111 if(ret >= sizeof(buf))
112 Error("dbapi2", ERR_STOP, "Query truncated in dbunsafequery, format: '%s', database: %s", format, db->name);
113
114 db->__query(db, NULL, NULL, buf);
115 }
116
117 static void dbsafequery(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, const char *types, ...) {
118 va_list ap;
119 char buf[QUERYBUFLEN];
120
121 va_start(ap, types);
122 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
123 va_end(ap);
124
125 db->__query(db, cb, data, buf);
126 }
127
128 static void dbsafecreatetable(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *format, const char *types, ...) {
129 va_list ap;
130 char buf[QUERYBUFLEN];
131
132 va_start(ap, types);
133 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
134 va_end(ap);
135
136 db->__createtable(db, cb, data, buf);
137 }
138
139 static void dbsafesimplequery(const DBAPIConn *db, const char *format, const char *types, ...) {
140 va_list ap;
141 char buf[QUERYBUFLEN];
142
143 va_start(ap, types);
144 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
145 va_end(ap);
146
147 db->__query(db, NULL, NULL, buf);
148 }
149
150 static void dbloadtable(const DBAPIConn *db, DBAPIQueryCallback init, DBAPIQueryCallback data, DBAPIQueryCallback fini, DBAPIUserData tag, const char *tablename) {
151 db->__loadtable(db, init, data, fini, tag, db->tablename(db, tablename));
152 }
153
154 static void dbcall(const DBAPIConn *db, DBAPIQueryCallback cb, DBAPIUserData data, const char *function, const char *format, const char *types, ...) {
155 va_list ap;
156 char buf[QUERYBUFLEN];
157
158 va_start(ap, types);
159 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
160 va_end(ap);
161
162 db->__call(db, cb, data, function, buf);
163 }
164
165 static void dbsimplecall(const DBAPIConn *db, const char *function, const char *format, const char *types, ...) {
166 va_list ap;
167 char buf[QUERYBUFLEN];
168
169 va_start(ap, types);
170 dbvsnprintf(db, buf, sizeof(buf), format, types, ap);
171 va_end(ap);
172
173 db->__call(db, NULL, NULL, function, buf);
174 }
175
176 DBAPIConn *dbapi2open(const char *provider, const char *database) {
177 int i, found = -1;
178 DBAPIConn *db;
179 DBAPIProvider *p;
180
181 if(provider) {
182 for(i=0;i<MAX_PROVIDERS;i++) {
183 if(providerobjs[i] && !strcmp(provider, providerobjs[i]->__providerdata->name)) {
184 found = i;
185 break;
186 }
187 }
188 if(found == -1) {
189 Error("dbapi2", ERR_WARNING, "Couldn't find forced database provider %s", provider);
190 return NULL;
191 }
192 } else {
193 for(i=0;i<MAX_PROVIDERS;i++) {
194 if(providerobjs[i]) {
195 found = i;
196 break;
197 }
198 }
199
200 if(found == -1) {
201 Error("dbapi2", ERR_WARNING, "No database providers found.");
202 return NULL;
203 }
204 }
205
206 p = providerobjs[found];
207
208 db = calloc(1, sizeof(DBAPIConn));
209 if(!db)
210 return NULL;
211
212 db->close = dbclose;
213 db->query = dbsafequery;
214 db->createtable = dbsafecreatetable;
215 db->squery = dbsafesimplequery;
216 db->loadtable = dbloadtable;
217 db->escapestring = p->escapestring;
218 db->tablename = p->tablename;
219 db->unsafequery = dbunsafequery;
220 db->unsafesquery = dbunsafesimplequery;
221 db->unsafecreatetable = dbunsafecreatetable;
222 db->call = dbcall;
223 db->scall = dbsimplecall;
224
225 db->__query = p->query;
226 db->__close = p->close;
227 db->__quotestring = p->quotestring;
228 db->__createtable = p->createtable;
229 db->__loadtable = p->loadtable;
230 db->__call = p->call;
231
232 strlcpy(db->name, database, DBNAME_LEN);
233
234 db->handle = p->new(db);
235 if(!db->handle) {
236 free(db);
237 Error("dbapi2", ERR_WARNING, "Unable to initialise database %s, provider: %s", database, p->__providerdata->name);
238 return NULL;
239 }
240
241 Error("dbapi2", ERR_INFO, "Database %s opened with provider %s.", database, p->__providerdata->name);
242
243 return db;
244 }
245
246 static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap) {
247 StringBuf b;
248 const char *p;
249 static char convbuf[VSNPF_MAXARGS][VSNPF_MAXARGLEN+10];
250 int arg, argcount;
251
252 if(size == 0)
253 return;
254
255 {
256 int i;
257
258 int d;
259 char *s;
260 double g;
261 unsigned int u;
262 size_t l;
263 int fallthrough;
264 time_t t;
265 unsigned long ul;
266 long _l;
267
268 for(i=0;i<VSNPF_MAXARGS;i++)
269 convbuf[i][0] = '\0';
270
271 argcount=0;
272 for(;*types;types++) {
273 char *cb = convbuf[argcount];
274
275 if(argcount++ >= VSNPF_MAXARGS) {
276 /* calls exit(0) */
277 Error("dbapi2", ERR_STOP, "Maximum arguments reached in dbvsnprintf, format: '%s', database: %s", format, db->name);
278 }
279
280 fallthrough = 0;
281 switch(*types) {
282 case 's':
283 s = va_arg(ap, char *);
284 if(s)
285 l = strlen(s);
286 fallthrough = 1;
287
288 /* falling through */
289 case 'S':
290 if(!fallthrough) {
291 s = va_arg(ap, char *);
292 l = va_arg(ap, size_t);
293 }
294
295 if(!s) {
296 strlcpy(cb, "NULL", sizeof(convbuf[0]));
297 } else if((l > (VSNPF_MAXARGLEN / 2)) || !db->__quotestring(db, cb, sizeof(convbuf[0]), s, l)) {
298 /* now... this is a guess, but we should catch it most of the time */
299 Error("dbapi2", ERR_STOP, "Long string truncated, format: '%s', database: %s", format, db->name);
300 l = VSNPF_MAXARGLEN;
301 }
302
303 break;
304 case 'R':
305 s = va_arg(ap, char *);
306
307 strlcpy(cb, s, sizeof(convbuf[0]));
308 break;
309 case 'T':
310 s = va_arg(ap, char *);
311
312 strlcpy(cb, db->tablename(db, s), sizeof(convbuf[0]));
313 break;
314 case 'd':
315 d = va_arg(ap, int);
316 snprintf(cb, VSNPF_MAXARGLEN, "%d", d);
317 break;
318 case 'u':
319 u = va_arg(ap, unsigned int);
320 snprintf(cb, VSNPF_MAXARGLEN, "%u", u);
321 break;
322 case 't':
323 t = va_arg(ap, time_t);
324 snprintf(cb, VSNPF_MAXARGLEN, "%jd", (intmax_t)t);
325 break;
326 case 'D':
327 _l = va_arg(ap, long);
328 snprintf(cb, VSNPF_MAXARGLEN, "%ld", _l);
329 break;
330 case 'U':
331 ul = va_arg(ap, unsigned long);
332 snprintf(cb, VSNPF_MAXARGLEN, "%lu", ul);
333 break;
334 case 'g':
335 g = va_arg(ap, double);
336 snprintf(cb, VSNPF_MAXARGLEN, "%.1f", g);
337 break;
338 default:
339 /* calls exit(0) */
340 Error("dbapi2", ERR_STOP, "Bad format specifier '%c' supplied in dbvsnprintf, format: '%s', database: %s", *types, format, db->name);
341 }
342 }
343 }
344
345 sbinit(&b, buf, size);
346
347 for(arg=0,p=format;*p;p++) {
348 if (*p == '\\' && *(p + 1) == '?')
349 continue;
350
351 if((p != format && *(p - 1) == '\\') || *p != '?') {
352 if(!sbaddchar(&b, *p))
353 break;
354 continue;
355 }
356
357 if(arg >= argcount)
358 Error("dbapi2", ERR_STOP, "Gone over number of arguments in dbvsnprintf, format: '%s', database: %s", format, db->name);
359
360 if(!sbaddstr(&b, convbuf[arg]))
361 Error("dbapi2", ERR_STOP, "Possible truncation in dbvsnprintf, format: '%s', database: %s", format, db->name);
362
363 arg++;
364 }
365
366 sbterminate(&b);
367 }