--- a/Makefile Wed Nov 19 19:06:10 2008 +0200
+++ b/Makefile Thu Nov 20 01:16:24 2008 +0200
@@ -1,5 +1,5 @@
LIBEVENT_PATH = ../libs/libevent-dev
-LIBFUSE_PATH = ../libs/libfuse-2.7.4
+LIBFUSE_PATH = ../opt
LIBRARY_PATHS = -L${LIBEVENT_PATH}/lib -L${LIBFUSE_PATH}/lib
INCLUDE_PATHS = -I${LIBEVENT_PATH}/include -I${LIBFUSE_PATH}/include
--- a/src/dbfs/interrupt.c Wed Nov 19 19:06:10 2008 +0200
+++ b/src/dbfs/interrupt.c Thu Nov 20 01:16:24 2008 +0200
@@ -18,15 +18,20 @@
// abort query
evsql_query_abort(NULL, query);
+
+ // error the req
+ if ((err = -fuse_reply_err(req, EINTR)))
+ EWARNING(err, "fuse_reply_err");
/*
* Due to a locking bug in libfuse (at least 2.7.4), we can't call fuse_reply_err from the interrupt function, so we must
* schedule after this function returns.
- */
+ * /
tv.tv_sec = 0;
tv.tv_usec = 0;
if (event_base_once(ctx->ev_base, -1, EV_TIMEOUT, _dbfs_interrupt_reply, req, &tv))
PWARNING("event_base_once failed, dropping req reply: %p", req);
+ */
}
void _dbfs_interrupt_ctx (struct fuse_req *req, void *ctx_ptr) {
--- a/src/evsql.h Wed Nov 19 19:06:10 2008 +0200
+++ b/src/evsql.h Thu Nov 20 01:16:24 2008 +0200
@@ -10,6 +10,8 @@
#include <postgresql/libpq-fe.h>
#include <event2/event.h>
+#include <lib/err.h>
+
/*
* The generic context handle
*/
@@ -62,13 +64,34 @@
EVSQL_TYPE_UINT64,
};
-struct evsql_item {
+struct evsql_item_binary {
+ const char *ptr;
+ size_t len;
+};
+
+/*
+ * Metadata about the type of an item
+ */
+struct evsql_item_info {
// format
enum evsql_item_format format;
// type
enum evsql_item_type type;
+ // flags
+ struct evsql_item_flags {
+ uint8_t null_ok : 1;
+ } flags;
+};
+
+/*
+ * The type and value of an item
+ */
+struct evsql_item {
+ // "header"
+ struct evsql_item_info info;
+
// pointer to the raw databytes. Set to NULL to indicate SQL-NULL
const char *bytes;
@@ -85,39 +108,39 @@
/*
* Query info, similar to prepared statements
+ *
+ * Contains the literal SQL query and the types of the arguments
*/
struct evsql_query_info {
// the SQL query itself
const char *sql;
// the list of items
- struct evsql_item params[];
+ struct evsql_item_info params[];
+};
+
+/*
+ * Contains the query parameter types and their values
+ */
+struct evsql_query_params {
+ // result format
+ enum evsql_item_format result_format;
+
+ // list of params
+ struct evsql_item list[];
};
/*
* Result info
+ *
+ * Contains the types of the result columns
*/
struct evsql_result_info {
- int padding;
+ // XXX: put something useful here?
+ int _unused;
// the list of fields
- struct evsql_item columns[];
-};
-
-
-/*
- * Result type
- */
-struct evsql_result_info {
- struct evsql *evsql;
- struct evsql_trans *trans;
-
- int error;
-
- union evsql_result {
- // libpq
- PGresult *pq;
- } result;
+ struct evsql_item_info columns[];
};
/*
@@ -127,7 +150,7 @@
*
* Use the evsql_result_* functions to manipulate the results.
*/
-typedef void (*evsql_query_cb)(const struct evsql_result_info *res, void *arg);
+typedef void (*evsql_query_cb)(struct evsql_result *res, void *arg);
/*
* Callback for handling global-level errors.
@@ -187,7 +210,19 @@
/*
* Same as evsql_query, but uses the SQL-level support for binding parameters.
*/
-struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans, const char *command, const struct evsql_query_params *params, evsql_query_cb query_fn, void *cb_arg);
+struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans,
+ const char *command, const struct evsql_query_params *params,
+ evsql_query_cb query_fn, void *cb_arg
+);
+
+/*
+ * Execute the given query_info, using the parameter list in query_info to resolve the given variable arugments
+ */
+struct evsql_query *evsql_query_exec (struct evsql *evsql, struct evsql_trans *trans,
+ const struct evsql_query_info *query_info,
+ evsql_query_cb query_fn, void *cb_arg,
+ ...
+);
/*
* Abort a query, the query callback will not be called, the query and any possible results will be discarded.
@@ -246,29 +281,38 @@
*/
// get error message associated with function
-const char *evsql_result_error (const struct evsql_result_info *res);
+const char *evsql_result_error (const struct evsql_result *res);
+
+/*
+ * Iterator-based interface.
+ *
+ * Call result_begin to check for errors, then result_next to fetch rows, and finally result_end to release.
+ */
+err_t evsql_result_begin (struct evsql_result_info *info, struct evsql_result *res);
+int evsql_result_next (struct evsql_result *res, ...);
+void evsql_result_end (struct evsql_result *res);
// number of rows in the result
-size_t evsql_result_rows (const struct evsql_result_info *res);
+size_t evsql_result_rows (const struct evsql_result *res);
// number of columns in the result
-size_t evsql_result_cols (const struct evsql_result_info *res);
+size_t evsql_result_cols (const struct evsql_result *res);
// number of affected rows for UPDATE/INSERT
-size_t evsql_result_affected (const struct evsql_result_info *res);
+size_t evsql_result_affected (const struct evsql_result *res);
// fetch the raw binary value from a result set, and return it via ptr
// if size is nonzero, check that the size of the field data matches
-int evsql_result_binary (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok);
-int evsql_result_string (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, int nullok);
+int evsql_result_binary (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok);
+int evsql_result_string (const struct evsql_result *res, size_t row, size_t col, const char **ptr, int nullok);
// fetch certain kinds of values from a binary result set
-int evsql_result_uint16 (const struct evsql_result_info *res, size_t row, size_t col, uint16_t *uval, int nullok);
-int evsql_result_uint32 (const struct evsql_result_info *res, size_t row, size_t col, uint32_t *uval, int nullok);
-int evsql_result_uint64 (const struct evsql_result_info *res, size_t row, size_t col, uint64_t *uval, int nullok);
+int evsql_result_uint16 (const struct evsql_result *res, size_t row, size_t col, uint16_t *uval, int nullok);
+int evsql_result_uint32 (const struct evsql_result *res, size_t row, size_t col, uint32_t *uval, int nullok);
+int evsql_result_uint64 (const struct evsql_result *res, size_t row, size_t col, uint64_t *uval, int nullok);
// release the result set, freeing its memory
-void evsql_result_free (const struct evsql_result_info *res);
+void evsql_result_free (struct evsql_result *res);
/*
* Close a connection. Callbacks for waiting queries will not be run.
--- a/src/evsql/evsql.c Wed Nov 19 19:06:10 2008 +0200
+++ b/src/evsql/evsql.c Thu Nov 20 01:16:24 2008 +0200
@@ -731,7 +731,7 @@
}
/*
- * Validate and allocate the basic stuff for a new query.
+ * Internal query functions
*/
static struct evsql_query *_evsql_query_new (struct evsql *evsql, struct evsql_trans *trans, evsql_query_cb query_fn, void *cb_arg) {
struct evsql_query *query = NULL;
@@ -755,12 +755,6 @@
return NULL;
}
-/*
- * Handle a new query.
- *
- * For transactions this will associate the query and then execute it, otherwise this will either find an idle
- * connection and send the query, or enqueue it.
- */
static int _evsql_query_enqueue (struct evsql *evsql, struct evsql_trans *trans, struct evsql_query *query, const char *command) {
// transaction queries are handled differently
if (trans) {
@@ -818,106 +812,6 @@
return -1;
}
-struct evsql_query *evsql_query (struct evsql *evsql, struct evsql_trans *trans, const char *command, evsql_query_cb query_fn, void *cb_arg) {
- struct evsql_query *query = NULL;
-
- // alloc new query
- if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL)
- goto error;
-
- // just execute the command string directly
- if (_evsql_query_enqueue(evsql, trans, query, command))
- goto error;
-
- // ok
- return query;
-
-error:
- _evsql_query_free(query);
-
- return NULL;
-}
-
-struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans, const char *command, const struct evsql_query_params *params, evsql_query_cb query_fn, void *cb_arg) {
- struct evsql_query *query = NULL;
- const struct evsql_query_param *param;
- int idx;
-
- // alloc new query
- if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL)
- goto error;
-
- // count the params
- for (param = params->list; param->type; param++)
- query->params.count++;
-
- // allocate the vertical storage for the parameters
- if (0
-
- || !(query->params.types = calloc(query->params.count, sizeof(Oid)))
- || !(query->params.values = calloc(query->params.count, sizeof(char *)))
- || !(query->params.lengths = calloc(query->params.count, sizeof(int)))
- || !(query->params.formats = calloc(query->params.count, sizeof(int)))
- )
- ERROR("calloc");
-
- // transform
- for (param = params->list, idx = 0; param->type; param++, idx++) {
- // `set for NULLs, otherwise not
- query->params.types[idx] = param->data_raw ? 0 : EVSQL_PQ_ARBITRARY_TYPE_OID;
-
- // values
- query->params.values[idx] = param->data_raw;
-
- // lengths
- query->params.lengths[idx] = param->length;
-
- // formats, binary if length is nonzero, but text for NULLs
- query->params.formats[idx] = param->length && param->data_raw ? 1 : 0;
- }
-
- // result format
- switch (params->result_fmt) {
- case EVSQL_FMT_TEXT:
- query->params.result_format = 0; break;
-
- case EVSQL_FMT_BINARY:
- query->params.result_format = 1; break;
-
- default:
- FATAL("params.result_fmt: %d", params->result_fmt);
- }
-
- // execute it
- if (_evsql_query_enqueue(evsql, trans, query, command))
- goto error;
-
-#ifdef DEBUG_ENABLED
- // debug it?
- DEBUG("evsql.%p: enqueued query=%p on trans=%p", evsql, query, trans);
- evsql_query_debug(command, params);
-#endif /* DEBUG_ENABLED */
-
- // ok
- return query;
-
-error:
- _evsql_query_free(query);
-
- return NULL;
-}
-
-void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query) {
- assert(query);
-
- if (trans) {
- // must be the right query
- assert(trans->query == query);
- }
-
- // just strip the callback and wait for it to complete as normal
- query->cb_fn = NULL;
-}
void _evsql_trans_commit_res (const struct evsql_result_info *res, void *arg) {
(void) arg;
--- a/src/evsql/evsql.h Wed Nov 19 19:06:10 2008 +0200
+++ b/src/evsql/evsql.h Thu Nov 20 01:16:24 2008 +0200
@@ -106,7 +106,7 @@
char *command;
// possible query params
- struct evsql_query_param_info {
+ struct evsql_query_params_pq {
int count;
Oid *types;
@@ -123,13 +123,22 @@
// our position in the query list
TAILQ_ENTRY(evsql_query) entry;
+};
- // the result
+// the result
+struct evsql_result {
+ int error;
+
union {
- PGresult *evpq;
+ PGresult *pq;
} result;
+
+ // result_* state
+ struct evsql_result_info *info;
+ size_t row_offset;
};
+
// maximum length for a 'BEGIN TRANSACTION ...' query
#define EVSQL_QUERY_BEGIN_BUF 512
@@ -137,4 +146,30 @@
// 16 = bool in 8.3
#define EVSQL_PQ_ARBITRARY_TYPE_OID 16
+/*
+ * Core query-submission interface.
+ *
+ * This performs some error-checking on the trans, allocates the evsql_query and does some basic initialization.
+ *
+ * This does not actually enqueue the query anywhere, no reference is stored anywhere.
+ *
+ * Returns the new evsql_query on success, NULL on failure.
+ */
+static struct evsql_query *_evsql_query_new (struct evsql *evsql, struct evsql_trans *trans, evsql_query_cb query_fn, void *cb_arg);
+
+/*
+ * Begin processing the given query, which should now be fully filled out.
+ *
+ * If trans is given, it MUST be idle, and the query will be executed. Otherwise, it will either be executed directly
+ * or enqueued for future execution.
+ *
+ * Returns zero on success, nonzero on failure.
+ */
+static int _evsql_query_enqueue (struct evsql *evsql, struct evsql_trans *trans, struct evsql_query *query, const char *command);
+
+/*
+ * Validate and allocate the basic stuff for a new query.
+ */
+
+
#endif /* EVSQL_INTERNAL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/evsql/query.c Thu Nov 20 01:16:24 2008 +0200
@@ -0,0 +1,242 @@
+
+#include "evsql.h"
+
+/*
+ * Initialize params->types/values/lengths/formats, params->count, params->result_format based on the given args
+ */
+static int _evsql_query_params_init_pq (struct evsql_query_params_pq *params, size_t param_count, enum evsql_item_format result_format) {
+ // set count
+ params->count = param_count;
+
+ // allocate vertical storage for the parameters
+ if (0
+
+ || !(query->params.types = calloc(query->params.count, sizeof(Oid)))
+ || !(query->params.values = calloc(query->params.count, sizeof(char *)))
+ || !(query->params.lengths = calloc(query->params.count, sizeof(int)))
+ || !(query->params.formats = calloc(query->params.count, sizeof(int)))
+ )
+ ERROR("calloc");
+
+ // result format
+ switch (result_format) {
+ case EVSQL_FMT_TEXT:
+ params.result_format = 0; break;
+
+ case EVSQL_FMT_BINARY:
+ params.result_format = 1; break;
+
+ default:
+ FATAL("params.result_fmt: %d", result_format);
+ }
+
+ // good
+ return 0;
+
+error:
+ return -1;
+}
+
+struct evsql_query *evsql_query (struct evsql *evsql, struct evsql_trans *trans, const char *command, evsql_query_cb query_fn, void *cb_arg) {
+ struct evsql_query *query = NULL;
+
+ // alloc new query
+ if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL)
+ goto error;
+
+ // just execute the command string directly
+ if (_evsql_query_enqueue(evsql, trans, query, command))
+ goto error;
+
+ // ok
+ return query;
+
+error:
+ _evsql_query_free(query);
+
+ return NULL;
+}
+
+struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans,
+ const char *command, const struct evsql_query_params *params,
+ evsql_query_cb query_fn, void *cb_arg
+) {
+ struct evsql_query *query = NULL;
+ const struct evsql_item *param;
+ size_t count = 0, idx;
+
+ // alloc new query
+ if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL)
+ goto error;
+
+ // count the params
+ for (param = params->list; param->info.type; param++)
+ count++;
+
+ // initialize params
+ evsql_query_params_init_pq(&query->params, count, params->result_format);
+
+ // transform
+ for (param = params->list, idx = 0; param->info.type; param++, idx++) {
+ // `set for NULLs, otherwise not
+ query->params.types[idx] = param->bytes ? 0 : EVSQL_PQ_ARBITRARY_TYPE_OID;
+
+ // values
+ query->params.values[idx] = param->bytes;
+
+ // lengths
+ query->params.lengths[idx] = param->length;
+
+ // formats, binary if length is nonzero, but text for NULLs
+ query->params.formats[idx] = param->length && param->bytes ? 1 : 0;
+ }
+
+ // execute it
+ if (_evsql_query_enqueue(evsql, trans, query, command))
+ goto error;
+
+#ifdef DEBUG_ENABLED
+ // debug it?
+ DEBUG("evsql.%p: enqueued query=%p on trans=%p", evsql, query, trans);
+ evsql_query_debug(command, params);
+#endif /* DEBUG_ENABLED */
+
+ // ok
+ return query;
+
+error:
+ _evsql_query_free(query);
+
+ return NULL;
+}
+
+struct evsql_query *evsql_query_exec (struct evsql *evsql, struct evsql_trans *trans,
+ const struct evsql_query_info *query_info,
+ evsql_query_cb query_fn, void *cb_arg,
+ ...
+) {
+ va_list vargs;
+ struct evsql_query *query = NULL;
+ const struct evsql_item_info *param;
+ size_t count = 0, idx;
+ err_t err = 1;
+
+ // varargs
+ va_start(vargs, cb_arg);
+
+ // alloc new query
+ if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL)
+ goto error;
+
+ // count the params
+ for (param = query_info->params; param->type; param++)
+ count++;
+
+ // initialize params
+ evsql_query_params_init_pq(&query->params, count, EVSQL_FMT_BINARY);
+
+ // transform
+ for (param = params->params, idx = 0; param->info.type; param++, idx++) {
+ // default type to 0 (implicit)
+ query->params.types[idx] = 0;
+
+ // default format to binary
+ query->params.formats[idx] = EVSQL_FMT_BINARY;
+
+ // consume argument
+ switch (param->info.type) {
+ case EVSQL_TYPE_NULL_: {
+ // explicit type + text fmt
+ query->params.types[idx] = EVSQL_PQ_ARBITRARY_TYPE_OID;
+ query->params.values[idx] = NULL;
+ query->params.lengths[idx] = 0;
+ query->params.formats[idx] = EVSQL_FMT_TEXT;
+ } break;
+
+ case EVSQL_TYPE_BINARY: {
+ struct evsql_item_binary item = va_arg(vargs, struct evsql_item_binary);
+
+ // value + explicit len
+ query->params.values[idx] = item->ptr;
+ query->params.lengths[idx] = item->len;
+ } break;
+
+ case EVSQL_TYPE_STRING: {
+ const char *str = va_arg(vargs, const char *);
+
+ // value + automatic length, text format
+ query->params.values[idx] = str;
+ query->params.lengths[idx] = 0;
+ query->params.formats[idx] = EVSQL_FMT_TEXT;
+ } break;
+
+ case EVSQL_TYPE_UINT16: {
+ uint16_t uval = va_arg(vargs, uint16_t);
+
+ if (uval != (int16_t) uval)
+ ERROR("param $%zu: uint16 overflow: %d", idx + 1, uval);
+
+ // network-byte-order value + explicit len
+ query->params.values[idx] = htons(uval);
+ query->params.lengths[idx] = sizeof(uint16_t);
+ } break;
+
+ case EVSQL_TYPE_UINT32: {
+ uint32_t uval = va_arg(vargs, uint32_t);
+
+ if (uval != (int32_t) uval)
+ ERROR("param $%zu: uint32 overflow: %ld", idx + 1, uval);
+
+ // network-byte-order value + explicit len
+ query->params.values[idx] = htonl(uval);
+ query->params.lengths[idx] = sizeof(uint32_t);
+ } break;
+
+ case EVSQL_TYPE_UINT64: {
+ uint64_t uval = va_arg(vargs, uint64_t);
+
+ if (uval != (int64_t) uval)
+ ERROR("param $%zu: uint16 overflow: %lld", idx + 1, uval);
+
+ // network-byte-order value + explicit len
+ query->params.values[idx] = htonq(uval);
+ query->params.lengths[idx] = sizeof(uint64_t);
+ } break;
+
+ default:
+ FATAL("param $%zu: invalid type: %d", idx + 1, param->info.type);
+ }
+ }
+
+ // execute it
+ if (_evsql_query_enqueue(evsql, trans, query, command))
+ goto error;
+
+ // no error, fallthrough for va_end
+ err = 0;
+
+error:
+ // possible cleanup
+ if (err)
+ _evsql_query_free(query);
+
+ // end varargs
+ va_end(vargs);
+
+ // return
+ return err ? NULL : query;
+}
+
+
+void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query) {
+ assert(query);
+
+ if (trans) {
+ // must be the right query
+ assert(trans->query == query);
+ }
+
+ // just strip the callback and wait for it to complete as normal
+ query->cb_fn = NULL;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/evsql/result.c Thu Nov 20 01:16:24 2008 +0200
@@ -0,0 +1,207 @@
+
+#include "evsql.h"
+
+const char *evsql_result_error (const struct evsql_result *res) {
+ if (!res->error)
+ return "No error";
+
+ switch (res->evsql->type) {
+ case EVSQL_EVPQ:
+ if (!res->result.pq)
+ return "unknown error (no result)";
+
+ return PQresultErrorMessage(res->result.pq);
+
+ default:
+ FATAL("res->evsql->type");
+ }
+
+}
+
+size_t evsql_result_rows (const struct evsql_result *res) {
+ switch (res->evsql->type) {
+ case EVSQL_EVPQ:
+ return PQntuples(res->result.pq);
+
+ default:
+ FATAL("res->evsql->type");
+ }
+}
+
+size_t evsql_result_cols (const struct evsql_result *res) {
+ switch (res->evsql->type) {
+ case EVSQL_EVPQ:
+ return PQnfields(res->result.pq);
+
+ default:
+ FATAL("res->evsql->type");
+ }
+}
+
+size_t evsql_result_affected (const struct evsql_result *res) {
+ switch (res->evsql->type) {
+ case EVSQL_EVPQ:
+ return strtol(PQcmdTuples(res->result.pq), NULL, 10);
+
+ default:
+ FATAL("res->evsql->type");
+ }
+}
+
+
+int evsql_result_null (const struct evsql_result *res, size_t row, size_t col) {
+ switch (res->evsql->type) {
+ case EVSQL_EVPQ:
+ return PQgetisnull(res->result.pq, row, col);
+
+ default:
+ FATAL("res->evsql->type");
+ }
+
+error:
+ return -1;
+}
+
+int evsql_result_field (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size) {
+ *ptr = NULL;
+
+ switch (res->evsql->type) {
+ case EVSQL_EVPQ:
+ if (PQfformat(res->result.pq, col) != 1)
+ ERROR("[%zu:%zu] PQfformat is not binary: %d", row, col, PQfformat(res->result.pq, col));
+
+ *size = PQgetlength(res->result.pq, row, col);
+ *ptr = PQgetvalue(res->result.pq, row, col);
+
+ return 0;
+
+ default:
+ FATAL("res->evsql->type");
+ }
+
+error:
+ return -1;
+}
+
+err_t evsql_result_begin (struct evsql_result_info *info, struct evsql_result *res) {
+ struct evsql_item_info *col;
+ size_t cols = 0, nrows;
+ err_t err;
+
+ // count info columns
+ for (col = info->columns; col->type; col++)
+ cols++;
+
+ // number of rows returned/affected
+ nrows = evsql_result_rows(res) || evsql_result_affected(res);
+
+ // did the query fail outright?
+ if (res->error)
+ // dump error message
+ NXERROR(err = EIO, evsql_result_error(res));
+
+/*
+ // SELECT/DELETE/UPDATE WHERE didn't match any rows -> ENOENT
+ if (nrows == 0)
+ XERROR(err = ENOENT, "no rows returned/affected");
+*/
+
+ // correct number of columns
+ if (evsql_result_cols(res) != cols)
+ XERROR(err = EINVAL, "wrong number of columns: %zu -> %zu", cols, evsql_result_cols(res));
+
+ // assign
+ res->info = info;
+ res->row_offset = 0;
+
+ // good
+ return 0;
+
+error:
+ return err;
+
+}
+
+int evsql_result_next (struct evsql_result *res, ...) {
+ va_list vargs;
+ struct evsql_item_info *col;
+ size_t col_idx, row_idx = res->row_offset;
+ int err;
+
+ // check if we're past the end
+ if (row_idx >= evsql_result_rows(res))
+ return 0;
+
+ // varargs
+ va_start(vargs);
+
+ for (col = info->columns, col_idx = 0; col->type; col++, col_idx++) {
+ char *value = NULL;
+ size_t length = 0;
+
+ // check for NULLs, then try and get the field value
+ if (evsql_result_null(res, row_idx, col_idx)) {
+ if (!col->flags.null_ok)
+ XERROR(err = EINVAL, "r%zu:c%zu: NULL", row_idx, col_idx);
+
+ } else if (evsql_result_field(row_idx, col_idx, &value, &length)) {
+ EERROR(err = EINVAL);
+
+ }
+
+ // read the arg
+ switch (col->type) {
+ case EVSQL_TYPE_BINARY: {
+ struct evsql_item_binary *item_ptr = va_arg(vargs, struct evsql_item_binary *);
+
+ if (value) {
+ item_ptr->ptr = value;
+ item_ptr->len = length;
+ }
+ } break;
+
+ case EVSQL_TYPE_STRING: {
+ char **str_ptr = va_arg(vargs, char **);
+
+ if (value)
+ *str_ptr = value;
+
+ } break;
+
+ case EVSQL_TYPE_UINT16: {
+ uint16_t *uval_ptr = va_arg(vars, uint16_t *);
+
+ if (value) {
+ if (length != sizeof(uint16_t))
+ XERROR(err = EINVAL, "r%zu:c%zu: wrong size for uint16_t: %zu", row_idx, col_idx, length);
+
+ int16_t sval = ntohs(*((int16_t *) value));
+
+ if (sval < 0)
+ XERROR(err = ERANGE, "r%zu:c%zu: out of range for uint16_t: %d", row_idx, col_idx, sval);
+
+ *uval_ptr = sval;
+ }
+ } break;
+ }
+ }
+
+
+}
+
+void evsql_result_end (struct evsql_result *res) {
+ // not much more to it...
+ evsql_result_free(res);
+}
+
+void evsql_result_free (struct evsql_result *res) {
+ switch (res->evsql->type) {
+ case EVSQL_EVPQ:
+ return PQclear(res->result.pq);
+
+ default:
+ FATAL("res->evsql->type");
+ }
+}
+
+
--- a/src/evsql/util.c Wed Nov 19 19:06:10 2008 +0200
+++ b/src/evsql/util.c Thu Nov 20 01:16:24 2008 +0200
@@ -121,54 +121,7 @@
}
}
-const char *evsql_result_error (const struct evsql_result_info *res) {
- if (!res->error)
- return "No error";
-
- switch (res->evsql->type) {
- case EVSQL_EVPQ:
- if (!res->result.pq)
- return "unknown error (no result)";
-
- return PQresultErrorMessage(res->result.pq);
-
- default:
- FATAL("res->evsql->type");
- }
-
-}
-
-size_t evsql_result_rows (const struct evsql_result_info *res) {
- switch (res->evsql->type) {
- case EVSQL_EVPQ:
- return PQntuples(res->result.pq);
-
- default:
- FATAL("res->evsql->type");
- }
-}
-
-size_t evsql_result_cols (const struct evsql_result_info *res) {
- switch (res->evsql->type) {
- case EVSQL_EVPQ:
- return PQnfields(res->result.pq);
-
- default:
- FATAL("res->evsql->type");
- }
-}
-
-size_t evsql_result_affected (const struct evsql_result_info *res) {
- switch (res->evsql->type) {
- case EVSQL_EVPQ:
- return strtol(PQcmdTuples(res->result.pq), NULL, 10);
-
- default:
- FATAL("res->evsql->type");
- }
-}
-
-int evsql_result_binary (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok) {
+int evsql_result_binary (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok) {
*ptr = NULL;
switch (res->evsql->type) {
@@ -196,7 +149,7 @@
return -1;
}
-int evsql_result_binlen (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t size, int nullok) {
+int evsql_result_binlen (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t size, int nullok) {
size_t real_size = 0;
if (evsql_result_binary(res, row, col, ptr, &real_size, nullok))
@@ -216,7 +169,7 @@
return -1;
}
-int evsql_result_string (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, int nullok) {
+int evsql_result_string (const struct evsql_result *res, size_t row, size_t col, const char **ptr, int nullok) {
size_t real_size;
if (evsql_result_binary(res, row, col, ptr, &real_size, nullok))
@@ -230,7 +183,7 @@
return -1;
}
-int evsql_result_uint16 (const struct evsql_result_info *res, size_t row, size_t col, uint16_t *uval, int nullok) {
+int evsql_result_uint16 (const struct evsql_result *res, size_t row, size_t col, uint16_t *uval, int nullok) {
const char *data;
int16_t sval;
@@ -253,7 +206,7 @@
return nullok ? 0 : -1;
}
-int evsql_result_uint32 (const struct evsql_result_info *res, size_t row, size_t col, uint32_t *uval, int nullok) {
+int evsql_result_uint32 (const struct evsql_result *res, size_t row, size_t col, uint32_t *uval, int nullok) {
const char *data;
int32_t sval;
@@ -276,7 +229,7 @@
return nullok ? 0 : -1;
}
-int evsql_result_uint64 (const struct evsql_result_info *res, size_t row, size_t col, uint64_t *uval, int nullok) {
+int evsql_result_uint64 (const struct evsql_result *res, size_t row, size_t col, uint64_t *uval, int nullok) {
const char *data;
int64_t sval;
@@ -299,16 +252,6 @@
return nullok ? 0 : -1;
}
-void evsql_result_free (const struct evsql_result_info *res) {
- switch (res->evsql->type) {
- case EVSQL_EVPQ:
- return PQclear(res->result.pq);
-
- default:
- FATAL("res->evsql->type");
- }
-}
-
const char *evsql_conn_error (struct evsql_conn *conn) {
switch (conn->evsql->type) {
case EVSQL_EVPQ:
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/evsql_test.c Thu Nov 20 01:16:24 2008 +0200
@@ -0,0 +1,82 @@
+
+#include <event2/event.h>
+
+#include "evsql.h"
+#include "lib/log.h"
+#include "lib/signals.h"
+#include "lib/misc.h"
+
+#define CONNINFO_DEFAULT "dbname=dbfs port=5433"
+
+void db_results (struct evsql_result *result, void *arg) {
+ uint32_t val;
+
+ static struct evsql_result_info result_info = {
+ 0, {
+ { EVSQL_FMT_BINARY, EVSQL_TYPE_UINT32 },
+ { 0, 0 }
+ }
+ };
+
+
+}
+
+void do_query (struct evsql *db) {
+ struct evsql_query *query = NULL;
+
+ static struct evsql_query_info query_info = {
+ .sql = "SELECT $1::int4 + 5",
+
+ .params = {
+ { EVSQL_FMT_BINARY, EVSQL_TYPE_UINT32 },
+ { 0, 0 }
+ }
+ };
+
+ // query
+ assert((query = evsql_query_exec(db, NULL, &query_info, (uint32_t) 4, db_results, NULL)) != NULL);
+}
+
+int main (char argc, char **argv) {
+ struct event_base *ev_base = NULL;
+ struct signals *signals = NULL;
+ struct evsql *db = NULL;
+
+ const char *db_conninfo;
+
+ // parse args
+ db_conninfo = CONNINFO_DEFAULT;
+
+ // init libevent
+ if ((ev_base = event_base_new()) == NULL)
+ ERROR("event_base_new");
+
+ // setup signals
+ if ((signals = signals_default(ev_base)) == NULL)
+ ERROR("signals_default");
+
+ // setup evsql
+ if ((db = evsql_new_pq(ev_base, db_conninfo, NULL, NULL)) == NULL)
+ ERROR("evsql_new_pq");
+
+ // run libevent
+ INFO("running libevent loop");
+
+ if (event_base_dispatch(ev_base))
+ PERROR("event_base_dispatch");
+
+ // clean shutdown
+
+error :
+ if (db) {
+ /* evsql_close(db); */
+ }
+
+ if (signals)
+ signals_free(signals);
+
+ if (ev_base)
+ event_base_free(ev_base);
+
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/err.h Thu Nov 20 01:16:24 2008 +0200
@@ -0,0 +1,9 @@
+#ifndef LIB_ERR_H
+#define LIB_ERR_H
+
+/*
+ * err_t is always positive
+ */
+typedef unsigned int err_t;
+
+#endif /* LIB_ERR_H */
--- a/src/lib/error.h Wed Nov 19 19:06:10 2008 +0200
+++ b/src/lib/error.h Thu Nov 20 01:16:24 2008 +0200
@@ -2,11 +2,7 @@
#define LIB_ERROR_H
#include "log.h"
-
-/*
- * err_t is always positive
- */
-typedef unsigned int err_t;
+#include "err.h"
#define ERROR(...) do { err_func(__func__, __VA_ARGS__); goto error; } while (0)
#define PERROR(...) do { perr_func(__func__, __VA_ARGS__); goto error; } while (0)