terom@44: terom@44: #include "evsql.h" terom@44: #include "lib/log.h" terom@44: #include "lib/signals.h" terom@44: #include "lib/misc.h" terom@44: terom@45: #include terom@46: #include terom@45: #include terom@45: terom@44: #define CONNINFO_DEFAULT "dbname=dbfs port=5433" terom@44: terom@46: struct evsql_test_ctx { terom@46: struct evsql *db; terom@46: struct evsql_trans *trans; terom@46: }; terom@46: terom@46: // forward-declare terom@46: void query_send (struct evsql *db, struct evsql_trans *trans); terom@46: terom@46: terom@46: void query_timer (int fd, short what, void *arg) { terom@46: struct evsql *db = arg; terom@46: terom@46: INFO("[evsql_test.timer] *tick*"); terom@46: terom@46: query_send(db, NULL); terom@46: } terom@46: terom@46: void query_start (struct event_base *base, struct evsql *db) { terom@46: static struct event ev; terom@46: struct timeval tv = { 5, 0 }; terom@46: terom@46: evperiodic_assign(&ev, base, &tv, &query_timer, db); terom@46: event_add(&ev, &tv); terom@46: terom@46: INFO("[evsql_test.timer_start] started timer"); terom@46: } terom@46: terom@45: void query_results (struct evsql_result *result, void *arg) { terom@46: struct evsql *db = arg; terom@44: uint32_t val; terom@44: terom@46: (void) db; terom@46: terom@44: static struct evsql_result_info result_info = { terom@44: 0, { terom@44: { EVSQL_FMT_BINARY, EVSQL_TYPE_UINT32 }, terom@44: { 0, 0 } terom@44: } terom@44: }; terom@44: terom@45: // begin terom@45: assert(evsql_result_begin(&result_info, result) == 0); terom@44: terom@45: // one row terom@45: assert(evsql_result_next(result, terom@45: &val terom@45: ) > 0); terom@45: terom@45: // print terom@45: INFO("[evsql_test.results] got result: %p: val=%lu", result, (unsigned long) val); terom@45: terom@45: // done terom@45: evsql_result_end(result); terom@44: } terom@44: terom@45: void query_send (struct evsql *db, struct evsql_trans *trans) { terom@44: struct evsql_query *query = NULL; terom@45: static int query_id = 0; terom@44: terom@44: static struct evsql_query_info query_info = { terom@44: .sql = "SELECT $1::int4 + 5", terom@44: terom@44: .params = { terom@44: { EVSQL_FMT_BINARY, EVSQL_TYPE_UINT32 }, terom@44: { 0, 0 } terom@44: } terom@44: }; terom@44: terom@44: // query terom@46: if ((query = evsql_query_exec(db, trans, &query_info, query_results, db, terom@45: (uint32_t) ++query_id terom@46: )) == NULL) terom@46: WARNING("evsql_query_exec failed"); terom@45: terom@45: INFO("[evsql_test.query_send] enqueued query, trans=%p: %p: %d", trans, query, query_id); terom@44: } terom@44: terom@46: void trans_insert_result (struct evsql_result *res, void *arg) { terom@46: struct evsql_test_ctx *ctx = arg; terom@46: err_t err; terom@46: terom@46: // the result info terom@46: uint32_t id; terom@46: const char *str; terom@46: terom@46: static struct evsql_result_info result_info = { terom@46: 0, { terom@46: { EVSQL_FMT_BINARY, EVSQL_TYPE_UINT32 }, terom@46: { EVSQL_FMT_BINARY, EVSQL_TYPE_STRING }, terom@46: { 0, 0 } terom@46: } terom@46: }; terom@46: terom@46: // begin terom@46: if ((err = evsql_result_begin(&result_info, res))) terom@46: EFATAL(err, "query failed%s", err == EIO ? evsql_result_error(res) : ""); terom@46: terom@46: INFO("[evsql_test.insert] got %zu rows:", evsql_result_rows(res)); terom@46: terom@46: // iterate over rows terom@46: while ((err = evsql_result_next(res, &id, &str)) > 0) { terom@46: INFO("\t%-4lu %s", (unsigned long) id, str); terom@46: } terom@46: terom@46: if (err) terom@46: EFATAL(err, "evsql_result_next failed"); terom@46: terom@46: INFO("\t(done)"); terom@46: terom@46: // done terom@46: evsql_result_end(res); terom@46: } terom@46: terom@46: void trans_insert (struct evsql_test_ctx *ctx) { terom@46: struct evsql_query *query = NULL; terom@46: terom@46: // the query info terom@46: static struct evsql_query_info query_info = { terom@46: .sql = "INSERT INTO evsql_test (str) VALUES ($1::varchar), ($2::varchar) RETURNING id, str", terom@46: terom@46: .params = { terom@46: { EVSQL_FMT_BINARY, EVSQL_TYPE_STRING }, terom@46: { EVSQL_FMT_BINARY, EVSQL_TYPE_STRING }, terom@46: { 0, 0 } terom@46: } terom@46: }; terom@46: terom@46: // run the query terom@46: assert((query = evsql_query_exec(ctx->db, ctx->trans, &query_info, trans_insert_result, ctx, terom@46: (const char *) "row A", terom@46: (const char *) "row B" terom@46: )) != NULL); terom@46: terom@46: INFO("[evsql_test.insert] enqueued query: %p", query); terom@46: } terom@46: terom@45: void trans_create_result (struct evsql_result *res, void *arg) { terom@46: struct evsql_test_ctx *ctx = arg; terom@46: terom@45: // check terom@45: if (evsql_result_check(res)) terom@45: FATAL("query failed: %s", evsql_result_error(res)); terom@45: terom@45: INFO("[evsql_test.create_result] table created succesfully: %p", res); terom@45: terom@46: // free result terom@45: evsql_result_free(res); terom@46: terom@46: // insert terom@46: trans_insert(ctx); terom@45: } terom@45: terom@46: void trans_create_query (struct evsql_test_ctx *ctx) { terom@45: struct evsql_query *query = NULL; terom@45: terom@45: // the query info terom@45: static struct evsql_query_info query_info = { terom@46: .sql = "CREATE TEMPORARY TABLE evsql_test ( id serial4, str varchar(32) DEFAULT 'foobar' ) ON COMMIT DROP", terom@45: terom@45: .params = { terom@46: // { EVSQL_FMT_BINARY, EVSQL_TYPE_STRING }, terom@45: { 0, 0, } terom@45: } terom@45: }; terom@45: terom@45: // run the query terom@46: assert((query = evsql_query_exec(ctx->db, ctx->trans, &query_info, trans_create_result, ctx terom@46: // (const char *) "foobar" terom@45: )) != NULL); terom@45: terom@45: INFO("[evsql_test.trans_create_query] enqueued query: %p", query); terom@45: } terom@45: terom@45: void trans_error (struct evsql_trans *trans, void *arg) { terom@46: struct evsql_test_ctx *ctx = arg; terom@45: terom@45: FATAL("[evsql_test.trans_error] failure: %s", evsql_trans_error(trans)); terom@45: } terom@45: terom@45: void trans_ready (struct evsql_trans *trans, void *arg) { terom@46: struct evsql_test_ctx *ctx = arg; terom@45: terom@45: INFO("[evsql_test.trans_ready] ready"); terom@45: terom@46: trans_create_query(ctx); terom@45: } terom@45: terom@45: void trans_done (struct evsql_trans *trans, void *arg) { terom@46: struct evsql_test_ctx *ctx = arg; terom@45: terom@45: INFO("[evsql_test.trans_done] done"); terom@45: } terom@45: terom@46: void begin_transaction (struct evsql_test_ctx *ctx) { terom@46: assert((ctx->trans = evsql_trans(ctx->db, EVSQL_TRANS_DEFAULT, terom@45: &trans_error, &trans_ready, &trans_done, terom@46: ctx terom@45: )) != NULL); terom@45: terom@45: INFO("[evsql_test.begin_trans] created transaction"); terom@45: } terom@45: terom@45: int main (int argc, char **argv) { terom@46: struct evsql_test_ctx ctx; terom@44: struct event_base *ev_base = NULL; terom@44: struct signals *signals = NULL; terom@44: terom@44: const char *db_conninfo; terom@44: terom@44: // parse args terom@44: db_conninfo = CONNINFO_DEFAULT; terom@44: terom@44: // init libevent terom@44: if ((ev_base = event_base_new()) == NULL) terom@44: ERROR("event_base_new"); terom@44: terom@44: // setup signals terom@44: if ((signals = signals_default(ev_base)) == NULL) terom@44: ERROR("signals_default"); terom@44: terom@44: // setup evsql terom@46: if ((ctx.db = evsql_new_pq(ev_base, db_conninfo, NULL, NULL)) == NULL) terom@44: ERROR("evsql_new_pq"); terom@44: terom@45: // send query terom@46: query_send(ctx.db, NULL); terom@45: terom@45: // being transaction terom@46: begin_transaction(&ctx); terom@45: terom@45: // send query terom@46: query_send(ctx.db, NULL); terom@46: terom@46: // start query timer terom@46: query_start(ev_base, ctx.db); terom@45: terom@44: // run libevent terom@46: INFO("[evsql_test.main] running libevent loop"); terom@44: terom@44: if (event_base_dispatch(ev_base)) terom@44: PERROR("event_base_dispatch"); terom@44: terom@44: // clean shutdown terom@44: terom@44: error : terom@46: if (ctx.db) { terom@44: /* evsql_close(db); */ terom@44: } terom@44: terom@44: if (signals) terom@44: signals_free(signals); terom@44: terom@44: if (ev_base) terom@44: event_base_free(ev_base); terom@44: terom@44: } terom@44: