]> jfr.im git - irc/quakenet/newserv.git/blob - dbapi2/dbapi2.c
DBAPI2: add support for NULL values to string format.
[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 DBAPIConn *dbapi2open(const char *provider, const char *database) {
155 int i, found = -1;
156 DBAPIConn *db;
157 DBAPIProvider *p;
158
159 if(provider) {
160 for(i=0;i<MAX_PROVIDERS;i++) {
161 if(providerobjs[i] && !strcmp(provider, providerobjs[i]->__providerdata->name)) {
162 found = i;
163 break;
164 }
165 }
166 if(found == -1) {
167 Error("dbapi2", ERR_WARNING, "Couldn't find forced database provider %s", provider);
168 return NULL;
169 }
170 } else {
171 for(i=0;i<MAX_PROVIDERS;i++) {
172 if(providerobjs[i]) {
173 found = i;
174 break;
175 }
176 }
177
178 if(found == -1) {
179 Error("dbapi2", ERR_WARNING, "No database providers found.");
180 return NULL;
181 }
182 }
183
184 p = providerobjs[found];
185
186 db = calloc(1, sizeof(DBAPIConn));
187 if(!db)
188 return NULL;
189
190 db->close = dbclose;
191 db->query = dbsafequery;
192 db->createtable = dbsafecreatetable;
193 db->squery = dbsafesimplequery;
194 db->loadtable = dbloadtable;
195 db->escapestring = p->escapestring;
196 db->tablename = p->tablename;
197 db->unsafequery = dbunsafequery;
198 db->unsafesquery = dbunsafesimplequery;
199 db->unsafecreatetable = dbunsafecreatetable;
200
201 db->__query = p->query;
202 db->__close = p->close;
203 db->__quotestring = p->quotestring;
204 db->__createtable = p->createtable;
205 db->__loadtable = p->loadtable;
206
207 strlcpy(db->name, database, DBNAME_LEN);
208
209 db->handle = p->new(db);
210 if(!db->handle) {
211 free(db);
212 Error("dbapi2", ERR_WARNING, "Unable to initialise database %s, provider: %s", database, p->__providerdata->name);
213 return NULL;
214 }
215
216 Error("dbapi2", ERR_INFO, "Database %s opened with provider %s.", database, p->__providerdata->name);
217
218 return db;
219 }
220
221 static void dbvsnprintf(const DBAPIConn *db, char *buf, size_t size, const char *format, const char *types, va_list ap) {
222 StringBuf b;
223 const char *p;
224 static char convbuf[VSNPF_MAXARGS][VSNPF_MAXARGLEN+10];
225 int arg, argcount;
226
227 if(size == 0)
228 return;
229
230 {
231 int i;
232
233 int d;
234 char *s;
235 double g;
236 unsigned int u;
237 size_t l;
238 int fallthrough;
239 time_t t;
240 unsigned long ul;
241 long _l;
242
243 for(i=0;i<VSNPF_MAXARGS;i++)
244 convbuf[i][0] = '\0';
245
246 argcount=0;
247 for(;*types;types++) {
248 char *cb = convbuf[argcount];
249
250 if(argcount++ >= VSNPF_MAXARGS) {
251 /* calls exit(0) */
252 Error("dbapi2", ERR_STOP, "Maximum arguments reached in dbvsnprintf, format: '%s', database: %s", format, db->name);
253 }
254
255 fallthrough = 0;
256 switch(*types) {
257 case 's':
258 s = va_arg(ap, char *);
259 if(s)
260 l = strlen(s);
261 fallthrough = 1;
262
263 /* falling through */
264 case 'S':
265 if(!fallthrough) {
266 s = va_arg(ap, char *);
267 l = va_arg(ap, size_t);
268 }
269
270 if(!s) {
271 strlcpy(cb, "NULL", sizeof(convbuf[0]));
272 } else if((l > (VSNPF_MAXARGLEN / 2)) || !db->__quotestring(db, cb, sizeof(convbuf[0]), s, l)) {
273 /* now... this is a guess, but we should catch it most of the time */
274 Error("dbapi2", ERR_STOP, "Long string truncated, format: '%s', database: %s", format, db->name);
275 l = VSNPF_MAXARGLEN;
276 }
277
278 break;
279 case 'R':
280 s = va_arg(ap, char *);
281
282 strlcpy(cb, s, sizeof(convbuf[0]));
283 break;
284 case 'T':
285 s = va_arg(ap, char *);
286
287 strlcpy(cb, db->tablename(db, s), sizeof(convbuf[0]));
288 break;
289 case 'd':
290 d = va_arg(ap, int);
291 snprintf(cb, VSNPF_MAXARGLEN, "%d", d);
292 break;
293 case 'u':
294 u = va_arg(ap, unsigned int);
295 snprintf(cb, VSNPF_MAXARGLEN, "%u", u);
296 break;
297 case 't':
298 t = va_arg(ap, time_t);
299 snprintf(cb, VSNPF_MAXARGLEN, "%jd", (intmax_t)t);
300 break;
301 case 'D':
302 _l = va_arg(ap, long);
303 snprintf(cb, VSNPF_MAXARGLEN, "%ld", _l);
304 break;
305 case 'U':
306 ul = va_arg(ap, unsigned long);
307 snprintf(cb, VSNPF_MAXARGLEN, "%lu", ul);
308 break;
309 case 'g':
310 g = va_arg(ap, double);
311 snprintf(cb, VSNPF_MAXARGLEN, "%.1f", g);
312 break;
313 default:
314 /* calls exit(0) */
315 Error("dbapi2", ERR_STOP, "Bad format specifier '%c' supplied in dbvsnprintf, format: '%s', database: %s", *types, format, db->name);
316 }
317 }
318 }
319
320 sbinit(&b, buf, size);
321
322 for(arg=0,p=format;*p;p++) {
323 if(*p != '?') {
324 if(!sbaddchar(&b, *p))
325 break;
326 continue;
327 }
328
329 if(arg >= argcount)
330 Error("dbapi2", ERR_STOP, "Gone over number of arguments in dbvsnprintf, format: '%s', database: %s", format, db->name);
331
332 if(!sbaddstr(&b, convbuf[arg]))
333 Error("dbapi2", ERR_STOP, "Possible truncation in dbvsnprintf, format: '%s', database: %s", format, db->name);
334
335 arg++;
336 }
337
338 sbterminate(&b);
339 }