1 #define _GNU_SOURCE 1 /* for vasprintf */
6 #include <stdio.h> /*printf()*/
7 #include <stdlib.h> /*getenv(), atexit()*/
8 #include <string.h> /* strlen() */
9 #include <inttypes.h> /* PRIuXX macros */
11 #define MARKER printf("MARKER: %s:%d:%s():\t",__FILE__,__LINE__,__func__); printf
12 #define FIXME(X) MARKER("FIXME: " X)
15 db_wrap_conn_params mysql
;
16 db_wrap_conn_params sqlite3
;
18 db_wrap_conn_params_empty_m
,
19 db_wrap_conn_params_empty_m
,
28 true/*useTempTables*/,
33 static void show_errinfo_impl(db_wrap
*wr
, int rc
, unsigned int line
)
36 char const *errStr
= NULL
;
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!
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
);
62 sql
= "create " TABLE_DEF
;
67 db_wrap_result
*res
= NULL
;
71 rc
= wr
->api
->query_result(wr
, sql
, strlen(sql
), &res
);
75 //MARKER("dbi_wrap_result@%p, dbi_result@%p\n",(void const *)res, res->impl.data);
76 rc
= res
->api
->finalize(res
);
81 rc
= db_wrap_query_exec(wr
, sql
, strlen(sql
));
87 const size_t count
= 10;
88 char const *strVal
= "hi, world";
89 for (i
= 1; i
<= count
; ++i
) {
91 rc
= asprintf(&q
, "insert into t (vint, vdbl, vstr) values(%d,%2.1lf,'%s')",
92 i
, (i
* 1.1), strVal
);
96 rc
= wr
->api
->query_result(wr
, q
, strlen(q
), &res
);
98 //MARKER("Query rc=[%d] [%s]\n",rc, q);
102 rc
= res
->api
->finalize(res
);
104 show_errinfo(wr
, rc
);
109 "select * from t order by vint desc"
112 rc
= wr
->api
->query_result(wr
, sql
, strlen(sql
), &res
);
115 //assert(res->impl.data == db_wrap_dbi_result(res));
117 /* ensure that stepping acts as expected. */
119 while (0 == (rc
= res
->api
->step(res
))) {
123 char const *strCheck
= NULL
;
125 /** The following two blocks must behave equivalently, except for
126 how they copy (or not) the underlying string ... */
128 rc
= res
->api
->get_string_ndx(res
, 2, &strCheck
, &sz
);
130 rc
= db_wrap_result_string_copy_ndx(res
, 2, &strCP
, &sz
);
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
);
146 rc
= wr
->api
->query_result(wr
, sql
, strlen(sql
), &res
);
149 while (0 == res
->api
->step(res
)) {}
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"));
166 MARKER("WARNING: skipping count(*) test because the libdbi sqlite driver apparently doesn't handle the numeric type properly!\n");
168 sql
= "select count(*) as C from t";
170 rc
= wr
->api
->query_result(wr
, sql
, strlen(sql
), &res
);
173 rc
= res
->api
->step(res
);
175 typedef int64_t CountType
;
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
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
);
194 assert(ival
== gotCount
);
195 res
->api
->finalize(res
);
197 //assert( res->impl.data == db_wrap_dbi_result(res) );
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
);
211 char const *errStr
= NULL
;
213 wr
->api
->error_info(wr
, &errStr
, NULL
, &driverRc
);
214 MARKER("doubleGet: rc=%d, driverRc=%d (%s), val=%lf\n", rc
, driverRc
, errStr
, doubleGet
);
217 assert(11.0 == doubleGet
);
219 MARKER("WARNING: the fetch-double test has been disabled!\n");
223 "select * from t order by vint desc"
224 //"select count(*) as C from t"
227 rc
= wr
->api
->query_result(wr
, sql
, strlen(sql
), &res
);
232 const int32_t intExpect
= count
;
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);
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);
244 rc
= res
->api
->step(res
);
246 rc
= res
->api
->get_int32_ndx(res
, 0, &intGet
);
249 //MARKER("got int=%d, expected=%d\n",intGet, intExpect);
250 assert(intGet
== intExpect
);
252 rc
= res
->api
->finalize(res
);
256 rc
= db_wrap_query_int32(wr
, sql
, strlen(sql
), &intGet
);
258 assert(intGet
== intExpect
);
260 int64_t int64Get
= -1;
261 rc
= db_wrap_query_int64(wr
, sql
, strlen(sql
), &int64Get
);
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!");
274 int rc
= db_wrap_driver_init("dbi:mysql", &ConnParams
.mysql
, &wr
);
277 rc
= wr
->api
->connect(wr
);
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
);
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
);
291 test_dbwrap_generic("dbi:mysql", wr
);
293 rc
= wr
->api
->finalize(wr
);
298 static void test_sqlite_1(void)
300 #if ! DB_WRAP_CONFIG_ENABLE_LIBDBI
301 assert(0 && "ERROR: dbi:sqlite3 support not compiled in!");
304 int rc
= db_wrap_driver_init("dbi:sqlite3", &ConnParams
.sqlite3
, &wr
);
307 char const *dbdir
= getenv("PWD");
308 rc
= wr
->api
->option_set(wr
, "sqlite3_dbdir", dbdir
);
310 rc
= wr
->api
->connect(wr
);
312 char const *errmsg
= NULL
;
314 rc
= wr
->api
->error_info(wr
, &errmsg
, NULL
, &dbErrno
);
316 assert(NULL
== errmsg
);
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
);
324 assert(0 == strcmp("'hi, ''world'''", sqlCP
));
325 rc
= wr
->api
->free_string(wr
, sqlCP
);
329 rc
= wr
->api
->option_get(wr
, "sqlite3_dbdir", &sql
);
331 assert(0 == strcmp(sql
, dbdir
));
333 test_dbwrap_generic("dbi:sqlite3", wr
);
336 rc
= wr
->api
->finalize(wr
);
341 static void show_help(char const *appname
)
343 printf("Usage:\n\t%s [-s] [-m] [-t]\n", appname
);
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.");
352 int main(int argc
, char const **argv
)
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;
362 } else if (0 == strcmp("-s", arg
)) {
363 ThisApp
.testSQLite3
= true;
366 } else if (0 == strcmp("-m", arg
)) {
367 ThisApp
.testMySQL
= true;
370 } else if (0 == strcmp("-h", arg
)) {
373 } else if ((0 == strcmp("-?", arg
))
374 || (0 == strcmp("--help", arg
))) {
381 puts("No test options specified!");
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");