db_updater: Put parentheses back
[merlin.git] / test-dbwrap.c
blob0d62074c47c05399e16c58d667a6cf6f8735f537
1 #define _GNU_SOURCE 1 /* for vasprintf */
3 #include "db_wrap.h"
4 #include <assert.h>
6 #include <stdio.h> /*printf()*/
7 #include <stdlib.h> /*getenv(), atexit()*/
8 #include <string.h> /* strlen() */
9 #include <inttypes.h> /* PRIuXX macros */
10 #include <stdbool.h>
11 #define MARKER printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); printf
12 #define FIXME(X) MARKER("FIXME: " X)
14 static struct {
15 db_wrap_conn_params mysql;
16 db_wrap_conn_params sqlite3;
17 } ConnParams = {
18 db_wrap_conn_params_empty_m,
19 db_wrap_conn_params_empty_m,
23 static struct {
24 bool useTempTables;
25 bool testMySQL;
26 bool testSQLite3;
27 } ThisApp = {
28 true/*useTempTables*/,
29 false/*testMySQL*/,
30 false/*testSQLite3*/,
33 static void show_errinfo_impl(db_wrap *wr, int rc, unsigned int line)
35 if (0 != rc) {
36 char const *errStr = NULL;
37 int dbErrCode = 0;
38 wr->api->error_info(wr, &errStr, NULL, &dbErrCode);
39 MARKER("line #%u, DB driver error info: db_wrap rc=%d, back-end error code=%d [%s]\n",
40 line, rc, dbErrCode, errStr);
43 #define show_errinfo(WR,RC) show_errinfo_impl(WR, RC, __LINE__)
45 static void test_dbwrap_generic(char const *driver, db_wrap *wr)
47 MARKER("Running generic tests: [%s]\n", driver);
49 const bool isMysql = (NULL != strstr(driver, "mysql"));
50 const bool isSqlite = (NULL != strstr(driver, "sqlite"));
52 if (isSqlite == isMysql) {
53 // THIS IS ONLY HERE TO AVOID 'UNUSED VARIABLE' WARNINGS!
56 #define TABLE_DEF \
57 "table t(vint integer, vdbl float(12), vstr varchar(32))"
58 char const *sql = NULL;
59 if (ThisApp.useTempTables) {
60 sql = ("create temporary " TABLE_DEF);
61 } else {
62 sql = "create " TABLE_DEF;
65 #undef TABLE_DEF
66 assert(NULL != sql);
67 db_wrap_result *res = NULL;
68 int rc;
70 #if 0
71 rc = wr->api->query_result(wr, sql, strlen(sql), &res);
72 show_errinfo(wr, rc);
73 assert(0 == rc);
74 assert(NULL != res);
75 //MARKER("dbi_wrap_result@%p, dbi_result@%p\n",(void const *)res, res->impl.data);
76 rc = res->api->finalize(res);
77 show_errinfo(wr, rc);
78 assert(0 == rc);
79 res = NULL;
80 #else
81 rc = db_wrap_query_exec(wr, sql, strlen(sql));
82 show_errinfo(wr, rc);
83 assert(0 == rc);
84 #endif
86 int i;
87 const size_t count = 10;
88 char const *strVal = "hi, world";
89 for (i = 1; i <= count; ++i) {
90 char *q = NULL;
91 rc = asprintf(&q, "insert into t (vint, vdbl, vstr) values(%d,%2.1lf,'%s')",
92 i, (i * 1.1), strVal);
93 assert(rc > 0);
94 assert(q);
95 res = NULL;
96 rc = wr->api->query_result(wr, q, strlen(q), &res);
97 show_errinfo(wr, rc);
98 //MARKER("Query rc=[%d] [%s]\n",rc, q);
99 free(q);
100 assert(0 == rc);
101 assert(NULL != res);
102 rc = res->api->finalize(res);
103 assert(0 == rc);
104 show_errinfo(wr, rc);
105 res = NULL;
108 sql =
109 "select * from t order by vint desc"
111 res = NULL;
112 rc = wr->api->query_result(wr, sql, strlen(sql), &res);
113 assert(0 == rc);
114 assert(NULL != res);
115 //assert(res->impl.data == db_wrap_dbi_result(res));
117 /* ensure that stepping acts as expected. */
118 size_t gotCount = 0;
119 while (0 == (rc = res->api->step(res))) {
120 ++gotCount;
121 if (1 == gotCount) {
122 size_t sz = 0;
123 char const *strCheck = NULL;
124 char *strCP = NULL;
125 /** The following two blocks must behave equivalently, except for
126 how they copy (or not) the underlying string ... */
127 #if 1
128 rc = res->api->get_string_ndx(res, 2, &strCheck, &sz);
129 #else
130 rc = db_wrap_result_string_copy_ndx(res, 2, &strCP, &sz);
131 strCheck = strCP;
132 #endif
133 assert(0 == rc);
134 assert(sz > 0);
135 assert(0 == strcmp(strCheck, strVal));
136 /*MARKER("Read string [%s]\n",strCheck);*/
137 if (NULL != strCP) { free(strCP); }
140 assert(gotCount == count);
141 assert(DB_WRAP_E_DONE == rc);
142 res->api->finalize(res);
145 res = NULL;
146 rc = wr->api->query_result(wr, sql, strlen(sql), &res);
147 assert(0 == rc);
148 assert(NULL != res);
149 while (0 == res->api->step(res)) {}
150 size_t rowCount = 0;
151 res->api->num_rows(res, &rowCount);
152 res->api->finalize(res);
153 MARKER("Row count=%u, expecting %u. sql=[%s]\n", (unsigned int)rowCount, (unsigned int)count, sql);
154 assert(rowCount == count);
157 // FIXME: add reset() to the result API.
160 Now try fetching some values...
164 const bool doCountTest = (NULL == strstr(driver, "sqlite"));
165 if (!doCountTest) {
166 MARKER("WARNING: skipping count(*) test because the libdbi sqlite driver apparently doesn't handle the numeric type properly!\n");
167 } else {
168 sql = "select count(*) as C from t";
169 res = NULL;
170 rc = wr->api->query_result(wr, sql, strlen(sql), &res);
171 assert(0 == rc);
172 assert(NULL != res);
173 rc = res->api->step(res);
174 assert(0 == rc);
175 typedef int64_t CountType;
176 CountType ival = -1;
177 rc =
178 //res->api->get_int32_ndx(res, 0, &ival)
179 res->api->get_int64_ndx(res, 0, &ival)
181 DAMN: the libdbi impls behave differently here: on some platforms
182 count(*) will be (u)int32 and on some 64. The libdbi drivers
183 are horribly pedantic here and require that we know exactly how
184 big the integer is.
186 http://www.mail-archive.com/libdbi-users@lists.sourceforge.net/msg00126.html
188 This particular test works for me on mysql but not sqlite3. It also
189 possibly fails on mysql 32-bit (untested).
192 MARKER("Select COUNT(*)/step/fetch rc=%d, ival=%ld, expecting=%ld\n", rc, (long)ival, (long)gotCount);
193 assert(0 == rc);
194 assert(ival == gotCount);
195 res->api->finalize(res);
196 res = NULL;
197 //assert( res->impl.data == db_wrap_dbi_result(res) );
201 if (!isSqlite) {
203 FIXME: get-double appears to be broken at the libdbi level for sqlite.
205 char const *dblSql = "select vdbl from t order by vint desc limit 1";
207 // not yet working. don't yet know why
208 double doubleGet = -1.0;
209 rc = db_wrap_query_double(wr, dblSql, strlen(dblSql), &doubleGet);
210 if (0 != rc) {
211 char const *errStr = NULL;
212 int driverRc = 0;
213 wr->api->error_info(wr, &errStr, NULL, &driverRc);
214 MARKER("doubleGet: rc=%d, driverRc=%d (%s), val=%lf\n", rc, driverRc, errStr, doubleGet);
216 assert(0 == rc);
217 assert(11.0 == doubleGet);
218 } else {
219 MARKER("WARNING: the fetch-double test has been disabled!\n");
222 sql =
223 "select * from t order by vint desc"
224 //"select count(*) as C from t"
226 res = NULL;
227 rc = wr->api->query_result(wr, sql, strlen(sql), &res);
228 assert(0 == rc);
229 assert(NULL != res);
231 int32_t intGet = -1;
232 const int32_t intExpect = count;
234 #if 1
235 #if 0
236 dbi_result dbires = db_wrap_dbi_result(res);
237 assert(dbi_result_next_row(dbires));
238 intGet = dbi_result_get_int_idx(dbires, 1);
239 #elif 0
240 dbi_result dbires = (dbi_result)res->impl.data;
241 assert(dbi_result_next_row(dbires));
242 intGet = dbi_result_get_int_idx(dbires, 1);
243 #else
244 rc = res->api->step(res);
245 assert(0 == rc);
246 rc = res->api->get_int32_ndx(res, 0, &intGet);
247 assert(0 == rc);
248 #endif
249 //MARKER("got int=%d, expected=%d\n",intGet, intExpect);
250 assert(intGet == intExpect);
251 #endif
252 rc = res->api->finalize(res);
253 assert(0 == rc);
255 intGet = -1;
256 rc = db_wrap_query_int32(wr, sql, strlen(sql), &intGet);
257 assert(0 == rc);
258 assert(intGet == intExpect);
260 int64_t int64Get = -1;
261 rc = db_wrap_query_int64(wr, sql, strlen(sql), &int64Get);
262 assert(0 == rc);
263 assert(intGet == (int)int64Get);
268 static void test_mysql_1(void)
270 #if ! DB_WRAP_CONFIG_ENABLE_LIBDBI
271 assert(0 && "ERROR: dbi:mysql support not compiled in!");
272 #else
273 db_wrap *wr = NULL;
274 int rc = db_wrap_driver_init("dbi:mysql", &ConnParams.mysql, &wr);
275 assert(0 == rc);
276 assert(wr);
277 rc = wr->api->connect(wr);
278 assert(0 == rc);
280 char *sqlCP = NULL;
281 char const *sql = "hi, 'world'";
282 size_t const sz = strlen(sql);
283 size_t const sz2 = wr->api->sql_quote(wr, sql, sz, &sqlCP);
284 assert(0 != sz2);
285 assert(sz != sz2);
286 /* ACHTUNG: what libdbi does here with the escaping is NOT SQL STANDARD. */
287 assert(0 == strcmp("'hi, \\'world\\''", sqlCP));
288 rc = wr->api->free_string(wr, sqlCP);
289 assert(0 == rc);
291 test_dbwrap_generic("dbi:mysql", wr);
293 rc = wr->api->finalize(wr);
294 assert(0 == rc);
295 #endif
298 static void test_sqlite_1(void)
300 #if ! DB_WRAP_CONFIG_ENABLE_LIBDBI
301 assert(0 && "ERROR: dbi:sqlite3 support not compiled in!");
302 #else
303 db_wrap *wr = NULL;
304 int rc = db_wrap_driver_init("dbi:sqlite3", &ConnParams.sqlite3, &wr);
305 assert(0 == rc);
306 assert(wr);
307 char const *dbdir = getenv("PWD");
308 rc = wr->api->option_set(wr, "sqlite3_dbdir", dbdir);
309 assert(0 == rc);
310 rc = wr->api->connect(wr);
311 assert(0 == rc);
312 char const *errmsg = NULL;
313 int dbErrno = 0;
314 rc = wr->api->error_info(wr, &errmsg, NULL, &dbErrno);
315 assert(0 == rc);
316 assert(NULL == errmsg);
318 char *sqlCP = NULL;
319 char const *sql = "hi, 'world'";
320 size_t const sz = strlen(sql);
321 size_t const sz2 = wr->api->sql_quote(wr, sql, sz, &sqlCP);
322 assert(0 != sz2);
323 assert(sz != sz2);
324 assert(0 == strcmp("'hi, ''world'''", sqlCP));
325 rc = wr->api->free_string(wr, sqlCP);
326 assert(0 == rc);
328 sql = NULL;
329 rc = wr->api->option_get(wr, "sqlite3_dbdir", &sql);
330 assert(0 == rc);
331 assert(0 == strcmp(sql, dbdir));
333 test_dbwrap_generic("dbi:sqlite3", wr);
336 rc = wr->api->finalize(wr);
337 assert(0 == rc);
338 #endif
341 static void show_help(char const *appname)
343 printf("Usage:\n\t%s [-s] [-m] [-t]\n", appname);
344 puts("Options:");
345 puts("\t-t = use non-temporary tables for tests. Will fail if the tables already exist.");
346 puts("\t-m = enables mysql test.");
347 puts("\t-s = enables sqlite3 test.");
348 puts("\t-h HOSTNAME = sets remote host name for some tests.");
349 putchar('\n');
352 int main(int argc, char const **argv)
354 int i;
355 int testCount = 0;
356 char const *dbhost = "localhost";
357 for (i = 1; i < argc; ++i) {
358 char const *arg = argv[i];
359 if (0 == strcmp("-t", arg)) {
360 ThisApp.useTempTables = false;
361 continue;
362 } else if (0 == strcmp("-s", arg)) {
363 ThisApp.testSQLite3 = true;
364 ++testCount;
365 continue;
366 } else if (0 == strcmp("-m", arg)) {
367 ThisApp.testMySQL = true;
368 ++testCount;
369 continue;
370 } else if (0 == strcmp("-h", arg)) {
371 dbhost = argv[++i];
372 continue;
373 } else if ((0 == strcmp("-?", arg))
374 || (0 == strcmp("--help", arg))) {
375 show_help(argv[0]);
376 return 1;
380 if (testCount < 1) {
381 puts("No test options specified!");
382 show_help(argv[0]);
383 return 1;
386 ConnParams.mysql.host = dbhost;
387 ConnParams.mysql.port = 3306;
388 ConnParams.mysql.username = "merlin";
389 ConnParams.mysql.password = "merlin";
390 ConnParams.mysql.dbname = "merlin";
393 ConnParams.sqlite3.dbname = "merlin.sqlite";
395 if (ThisApp.testMySQL) { test_mysql_1(); }
396 if (ThisApp.testSQLite3) { test_sqlite_1(); }
397 MARKER("If you got this far, it worked.\n");
398 return 0;