--- a/Makefile Sun Oct 12 01:09:00 2008 +0300
+++ b/Makefile Sun Oct 12 14:57:06 2008 +0300
@@ -9,7 +9,7 @@
DEFINES = -D_FILE_OFFSET_BITS=64
MY_CFLAGS = -Wall -g -std=gnu99
-BIN_NAMES = helloworld hello simple_hello evpq_test url_test
+BIN_NAMES = helloworld hello simple_hello evpq_test url_test dbfs
BIN_PATHS = $(addprefix bin/,$(BIN_NAMES))
# first target
@@ -21,6 +21,7 @@
bin/simple_hello: obj/evfuse.o obj/dirbuf.o obj/lib/log.o obj/lib/signals.o obj/simple.o
bin/evpq_test: obj/evpq.o obj/lib/log.o
bin/url_test: obj/lib/url.o obj/lib/lex.o obj/lib/log.o
+bin/dbfs: obj/evsql.o obj/evpq.o obj/evfuse.o obj/dirbuf.o obj/lib/log.o obj/lib/signals.o
# computed
LDFLAGS = ${LIBRARY_PATHS} ${LIBRARY_LIST}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/evfuse_db.txt Sun Oct 12 14:57:06 2008 +0300
@@ -0,0 +1,19 @@
+
+Just a simple pure-data filesystem stored in a SQL database. Implementation using PostgreSQL.
+No weird dynamic magic.
+
+
+file_tree:
+ offset serial4 ephemeral counter used to tell one file entry from another
+ name varchar(256) the filename
+ parent int4 -> inodes.ino file entry's parent
+ inode int4 -> inodes.ino the file's data
+
+inodes:
+ ino serial4 inode number
+ type char(3)
+ REG normal file
+ DIR directory
+ mode int2 file access modes
+ size int8 file content size (?)
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/doc/fuse_db.sql Sun Oct 12 14:57:06 2008 +0300
@@ -0,0 +1,7 @@
+CREATE TABLE inodes (ino serial4 primary key, type char(3), mode int2, size int8);
+CREATE TABLE file_tree ("offset" serial4 primary key, name varchar(256) NOT NULL, parent int4 references inodes(ino) NOT NULL, inode int4 references inodes(ino) NOT NULL);
+
+INSERT INTO inodes VALUES (1, 'DIR', 365, 0);
+INSERT INTO inodes VALUES (2, 'REG', 292, 0);
+INSERT INTO file_tree (name, parent, inode) VALUES ('foo', 1, 2);
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dbfs.c Sun Oct 12 14:57:06 2008 +0300
@@ -0,0 +1,223 @@
+
+/*
+ * A simple PostgreSQL-based filesystem.
+ */
+
+#include <string.h>
+#include <errno.h>
+
+#include <event2/event.h>
+
+#include "evfuse.h"
+#include "evsql.h"
+#include "dirbuf.h"
+#include "lib/log.h"
+#include "lib/signals.h"
+#include "lib/misc.h"
+
+#define SERROR(val) do { (val); goto error; } while(0)
+
+struct dbfs {
+ struct event_base *ev_base;
+ struct signals *signals;
+
+ const char *db_conninfo;
+ struct evsql *db;
+
+ struct evfuse *ev_fuse;
+};
+
+#define CONNINFO_DEFAULT "dbname=test"
+
+// XXX: not sure how this should work
+#define CACHE_TIMEOUT 1.0
+
+mode_t _dbfs_mode (const char *type) {
+ if (!strcmp(type, "DIR"))
+ return S_IFDIR;
+
+ if (!strcmp(type, "REG"))
+ return S_IFREG;
+
+ else {
+ WARNING("[dbfs] weird mode-type: %s", type);
+ return 0;
+ }
+}
+
+void dbfs_init (void *userdata, struct fuse_conn_info *conn) {
+ INFO("[dbfs.init] userdata=%p, conn=%p", userdata, conn);
+
+}
+
+void dbfs_destroy (void *userdata) {
+ INFO("[dbfs.destroy] userdata=%p", userdata);
+
+
+}
+
+void _dbfs_lookup_result (const struct evsql_result_info *res, void *arg) {
+ struct fuse_req *req = arg;
+ struct fuse_entry_param e; ZINIT(e);
+ int err = 0;
+
+ uint16_t mode;
+ uint32_t ino;
+ uint64_t size, nlink;
+ const char *type;
+
+ // check if it failed
+ if (res->error && (err = EIO))
+ NERROR(evsql_result_error(res));
+
+ // duplicate rows?
+ if (evsql_result_rows(res) > 1)
+ EERROR(err = EIO, "multiple rows returned");
+
+ // not found?
+ if (evsql_result_rows(res) == 0)
+ SERROR(err = ENOENT);
+
+ // correct number of columns
+ if (evsql_result_cols(res) != 5)
+ EERROR(err = EIO, "wrong number of columns: %zu", evsql_result_cols(res));
+
+ // get the data
+ if (0
+ || evsql_result_uint32(res, 0, 0, &ino, 0 ) // inodes.ino
+ || evsql_result_string(res, 0, 1, &type, 0 ) // inodes.type
+ || evsql_result_uint16(res, 0, 2, &mode, 0 ) // inodes.mode
+ || evsql_result_uint64(res, 0, 3, &size, 0 ) // inodes.size
+ || evsql_result_uint64(res, 0, 4, &nlink, 0 ) // count(*)
+ )
+ EERROR(err = EIO, "invalid db data");
+
+ INFO("[dbfs.look] -> ino=%u, st_mode=S_IF%s | %ho, st_nlink=%llu, st_size=%llu", ino, type, mode, (long long unsigned int) nlink, (long long unsigned int) size);
+
+ // convert and store
+ e.ino = e.attr.st_ino = ino;
+ e.attr.st_mode = _dbfs_mode(type) | mode;
+ e.attr.st_nlink = nlink;
+ e.attr.st_size = size;
+
+ // XXX: timeouts
+ e.attr_timeout = CACHE_TIMEOUT;
+ e.entry_timeout = CACHE_TIMEOUT;
+
+ // reply
+ if ((err = fuse_reply_entry(req, &e)))
+ EERROR(err, "fuse_reply_entry");
+
+error:
+ if (err && (err = fuse_reply_err(req, err)))
+ EWARNING(err, "fuse_reply_err");
+
+ // free
+ evsql_result_free(res);
+}
+
+void dbfs_lookup (struct fuse_req *req, fuse_ino_t parent, const char *name) {
+ struct dbfs *ctx = fuse_req_userdata(req);
+ int err;
+
+ INFO("[dbfs.lookup] parent=%lu name=%s", parent, name);
+
+ // query and params
+ const char *sql =
+ "SELECT"
+ " inodes.ino, inodes.type, inodes.mode, inodes.size, count(*)"
+ " FROM file_tree INNER JOIN inodes ON (file_tree.inode = inodes.ino)"
+ " WHERE file_tree.parent = $1::int AND file_tree.name = $2::varchar"
+ " GROUP BY inodes.ino, inodes.type, inodes.mode, inodes.size";
+
+ static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
+ EVSQL_PARAM ( UINT32 ),
+ EVSQL_PARAM ( STRING ),
+
+ EVSQL_PARAMS_END
+ };
+
+ // build params
+ if (0
+ || evsql_param_uint32(¶ms, 0, parent)
+ || evsql_param_string(¶ms, 1, name)
+ )
+ EERROR(err = EIO, "evsql_param_*");
+
+ // query
+ if (evsql_query_params(ctx->db, sql, ¶ms, _dbfs_lookup_result, req) == NULL)
+ EERROR(err = EIO, "evsql_query_params");
+
+ // XXX: handle interrupts
+
+ // wait
+ return;
+
+error:
+ if ((err = fuse_reply_err(req, err)))
+ EWARNING(err, "fuse_reply_err");
+}
+
+struct fuse_lowlevel_ops dbfs_llops = {
+ .init = dbfs_init,
+ .destroy = dbfs_destroy,
+
+ .lookup = dbfs_lookup,
+};
+
+void dbfs_sql_error (struct evsql *evsql, void *arg) {
+ struct dbfs *ctx = arg;
+
+ // AAAAAAAAAA.... panic
+ WARNING("[dbfs] SQL error: BREAKING MAIN LOOP LIEK NAO");
+
+ event_base_loopbreak(ctx->ev_base);
+}
+
+int main (int argc, char **argv) {
+ struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv);
+ struct dbfs ctx; ZINIT(ctx);
+
+ // parse args, XXX: fuse_args
+ ctx.db_conninfo = CONNINFO_DEFAULT;
+
+ // init libevent
+ if ((ctx.ev_base = event_base_new()) == NULL)
+ ERROR("event_base_new");
+
+ // setup signals
+ if ((ctx.signals = signals_default(ctx.ev_base)) == NULL)
+ ERROR("signals_default");
+
+ // open sql
+ if ((ctx.db = evsql_new_pq(ctx.ev_base, ctx.db_conninfo, dbfs_sql_error, &ctx)) == NULL)
+ ERROR("evsql_new_pq");
+
+ // open fuse
+ if ((ctx.ev_fuse = evfuse_new(ctx.ev_base, &fuse_args, &dbfs_llops, &ctx)) == NULL)
+ ERROR("evfuse_new");
+
+ // run libevent
+ INFO("running libevent loop");
+
+ if (event_base_dispatch(ctx.ev_base))
+ PERROR("event_base_dispatch");
+
+ // clean shutdown
+
+error :
+ // cleanup
+ if (ctx.ev_fuse)
+ evfuse_close(ctx.ev_fuse);
+
+ // XXX: ctx.db
+
+ if (ctx.signals)
+ signals_free(ctx.signals);
+
+ if (ctx.ev_base)
+ event_base_free(ctx.ev_base);
+
+ fuse_opt_free_args(&fuse_args);
+}
+
--- a/src/evpq.c Sun Oct 12 01:09:00 2008 +0300
+++ b/src/evpq.c Sun Oct 12 14:57:06 2008 +0300
@@ -288,7 +288,7 @@
ERROR("PQsendQuery: %s", PQerrorMessage(conn->pg_conn));
// handle it
- if (_evpq_handle_query(con))
+ if (_evpq_handle_query(conn))
goto error;
// success
@@ -308,7 +308,7 @@
ERROR("PQsendQueryParams: %s", PQerrorMessage(conn->pg_conn));
// handle it
- if (_evpq_handle_query(con))
+ if (_evpq_handle_query(conn))
goto error;
// success
--- a/src/evsql.c Sun Oct 12 01:09:00 2008 +0300
+++ b/src/evsql.c Sun Oct 12 14:57:06 2008 +0300
@@ -114,14 +114,13 @@
/*
* Dequeue the query, execute the callback, and free it.
*/
-static void _evsql_query_done (struct evsql_query *query, const struct evsql_result_info *result_info) {
-
+static void _evsql_query_done (struct evsql_query *query, const struct evsql_result_info *res) {
// dequeue
TAILQ_REMOVE(&query->evsql->queue, query, entry);
- if (result_info)
+ if (res)
// call the callback
- query->cb_fn(*result_info, query->cb_arg);
+ query->cb_fn(res, query->cb_arg);
// free
_evsql_query_free(query);
@@ -131,14 +130,14 @@
* A query has failed, notify the user and remove it.
*/
static void _evsql_query_failure (struct evsql *evsql, struct evsql_query *query) {
- struct evsql_result_info result; ZINIT(result);
+ struct evsql_result_info res; ZINIT(res);
// set up the result_info
- result.evsql = evsql;
- result.error = 1;
+ res.evsql = evsql;
+ res.error = 1;
// finish it off
- _evsql_query_done(query, &result);
+ _evsql_query_done(query, &res);
}
/*
@@ -146,18 +145,18 @@
*
* If result_info is given, each query will also recieve it via their callback, and the error_fn will be called.
*/
-static void _evsql_destroy (struct evsql *evsql, const struct evsql_result_info *result_info) {
+static void _evsql_destroy (struct evsql *evsql, const struct evsql_result_info *res) {
struct evsql_query *query;
// clear the queue
while ((query = TAILQ_FIRST(&evsql->queue)) != NULL) {
- _evsql_query_done(query, result_info);
+ _evsql_query_done(query, res);
TAILQ_REMOVE(&evsql->queue, query, entry);
}
// do the error callback if required
- if (result_info)
+ if (res)
evsql->error_fn(evsql, evsql->cb_arg);
// free
@@ -214,27 +213,32 @@
static void _evsql_evpq_done (struct evpq_conn *conn, void *arg) {
struct evsql *evsql = arg;
struct evsql_query *query;
- struct evsql_result_info result; ZINIT(result);
+ struct evsql_result_info res; ZINIT(res);
assert((query = TAILQ_FIRST(&evsql->queue)) != NULL);
// set up the result_info
- result.evsql = evsql;
+ res.evsql = evsql;
if (query->result.evpq == NULL) {
// if a query didn't return any results (bug?), warn and fail the query
WARNING("[evsql] evpq query didn't return any results");
- result.error = 1;
+ res.error = 1;
+
+ } else if (strcmp(PQresultErrorMessage(query->result.evpq), "") != 0) {
+ // the query failed with some error
+ res.error = 1;
+ res.result.pq = query->result.evpq;
} else {
- result.error = 0;
- result.result.pq = query->result.evpq;
+ res.error = 0;
+ res.result.pq = query->result.evpq;
}
// finish it off
- _evsql_query_done(query, &result);
+ _evsql_query_done(query, &res);
// pump the next one
_evsql_pump(evsql);
@@ -311,7 +315,7 @@
* 0 connection idle, can query immediately
* 1 connection busy, must queue query
*/
-static int _evsql_query_idle (struct evsql *evsql) {
+static int _evsql_query_busy (struct evsql *evsql) {
switch (evsql->type) {
case EVSQL_EVPQ: {
enum evpq_state state = evpq_state(evsql->engine.evpq);
@@ -359,23 +363,24 @@
}
static int _evsql_query_enqueue (struct evsql *evsql, struct evsql_query *query, const char *command) {
- int idle;
+ int busy;
// check state
- if ((idle = _evsql_query_idle(evsql)) < 0)
+ if ((busy = _evsql_query_busy(evsql)) < 0)
ERROR("connection is not valid");
- if (idle) {
+ if (busy) {
+ // copy the command for later execution
+ if ((query->command = strdup(command)) == NULL)
+ ERROR("strdup");
+
+ } else {
assert(TAILQ_EMPTY(&evsql->queue));
// execute directly
if (_evsql_query_exec(evsql, query, command))
goto error;
- } else {
- // copy the command for later execution
- if ((query->command = strdup(command)) == NULL)
- ERROR("strdup");
}
// store it on the list
@@ -408,9 +413,9 @@
return NULL;
}
-struct evsql_query *evsql_query_params (struct evsql *evsql, const char *command, struct evsql_query_params params, evsql_query_cb query_fn, void *cb_arg) {
+struct evsql_query *evsql_query_params (struct evsql *evsql, const char *command, const struct evsql_query_params *params, evsql_query_cb query_fn, void *cb_arg) {
struct evsql_query *query = NULL;
- struct evsql_query_param *param;
+ const struct evsql_query_param *param;
int idx;
// alloc new query
@@ -418,7 +423,7 @@
goto error;
// count the params
- for (param = params.list; param->value || param->length; param++)
+ for (param = params->list; param->type; param++)
query->params.count++;
// allocate the vertical storage for the parameters
@@ -432,22 +437,31 @@
ERROR("calloc");
// transform
- for (param = params.list, idx = 0; param->value || param->length; param++, idx++) {
+ for (param = params->list, idx = 0; param->type; param++, idx++) {
// `types` stays NULL
// query->params.types[idx] = 0;
// values
- query->params.values[idx] = param->value;
+ query->params.values[idx] = param->data_raw;
- // lengths (nonzero for NULLs)
- query->params.lengths[idx] = param->value ? param->length : 0;
+ // lengths
+ query->params.lengths[idx] = param->length;
// formats, binary if length is nonzero
- query->params.formats[idx] = param->value && param->length;
+ query->params.formats[idx] = param->length ? 1 : 0;
}
// result format
- query->params.result_format = params.result_binary ? 1 : 0;
+ 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, query, command))
@@ -462,3 +476,166 @@
return NULL;
}
+int evsql_param_string (struct evsql_query_params *params, size_t param, const char *ptr) {
+ struct evsql_query_param *p = ¶ms->list[param];
+
+ assert(p->type == EVSQL_PARAM_STRING);
+
+ p->data_raw = ptr;
+ p->length = 0;
+
+ return 0;
+}
+
+int evsql_param_uint32 (struct evsql_query_params *params, size_t param, uint32_t uval) {
+ struct evsql_query_param *p = ¶ms->list[param];
+
+ assert(p->type == EVSQL_PARAM_UINT32);
+
+ p->data.uint32 = htonl(uval);
+ p->data_raw = (const char *) &p->data.uint32;
+ p->length = sizeof(uval);
+
+ return 0;
+}
+
+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");
+ }
+}
+
+int evsql_result_binary (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t size, int nullok) {
+ *ptr = NULL;
+
+ switch (res->evsql->type) {
+ case EVSQL_EVPQ:
+ if (PQgetisnull(res->result.pq, row, col)) {
+ if (nullok)
+ return 0;
+ else
+ ERROR("[%zu:%zu] field is null", row, col);
+ }
+
+ if (PQfformat(res->result.pq, col) != 1)
+ ERROR("[%zu:%zu] PQfformat is not binary: %d", row, col, PQfformat(res->result.pq, col));
+
+ if (size && PQgetlength(res->result.pq, row, col) != size)
+ ERROR("[%zu:%zu] field size mismatch: %zu -> %d", row, 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;
+}
+
+int evsql_result_string (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, int nullok) {
+ return evsql_result_binary(res, row, col, ptr, 0, nullok);
+}
+
+int evsql_result_uint16 (const struct evsql_result_info *res, size_t row, size_t col, uint16_t *uval, int nullok) {
+ const char *data;
+ int16_t sval;
+
+ if (evsql_result_binary(res, row, col, &data, sizeof(*uval), nullok))
+ goto error;
+
+ sval = ntohs(*((int16_t *) data));
+
+ if (sval < 0)
+ ERROR("negative value for unsigned: %d", sval);
+
+ *uval = sval;
+
+ return 0;
+
+error:
+ 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) {
+ const char *data;
+ int32_t sval;
+
+ if (evsql_result_binary(res, row, col, &data, sizeof(*uval), nullok))
+ goto error;
+
+ sval = ntohl(*(int32_t *) data);
+
+ if (sval < 0)
+ ERROR("negative value for unsigned: %d", sval);
+
+ *uval = sval;
+
+ return 0;
+
+error:
+ 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) {
+ const char *data;
+ int64_t sval;
+
+ if (evsql_result_binary(res, row, col, &data, sizeof(*uval), nullok))
+ goto error;
+
+ sval = ntohq(*(int64_t *) data);
+
+ if (sval < 0)
+ ERROR("negative value for unsigned: %ld", sval);
+
+ *uval = sval;
+
+ return 0;
+
+error:
+ 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");
+ }
+}
--- a/src/evsql.h Sun Oct 12 01:09:00 2008 +0300
+++ b/src/evsql.h Sun Oct 12 14:57:06 2008 +0300
@@ -20,42 +20,74 @@
struct evsql_query;
/*
+ * Parameter type
+ */
+enum evsql_param_format {
+ EVSQL_FMT_TEXT,
+ EVSQL_FMT_BINARY,
+};
+
+enum evsql_param_type {
+ EVSQL_PARAM_INVALID,
+
+ EVSQL_PARAM_NULL,
+
+ EVSQL_PARAM_STRING,
+ EVSQL_PARAM_UINT16,
+ EVSQL_PARAM_UINT32,
+ EVSQL_PARAM_UINT64,
+};
+
+/*
* Query parameter info.
*
* Use the EVSQL_PARAM_* macros to define the value of list
*/
struct evsql_query_params {
// nonzero to get results in binary format
- int result_binary;
+ enum evsql_param_format result_fmt;
// the list of parameters, terminated by { 0, 0 }
struct evsql_query_param {
- // the textual or binary value for this parameter
- char *value;
+ // the param type
+ enum evsql_param_type type;
+
+ // pointer to the raw data
+ const char *data_raw;
+
+ // the value
+ union {
+ uint16_t uint16;
+ uint32_t uint32;
+ uint64_t uint64;
+ } data;
- // the explicit length of the parameter if it's binary. Must be non-zero for NULL values.
- int length;
+ // the explicit length of the parameter if it's binary, zero for text.
+ // set to -1 to indicate that the value is still missing
+ ssize_t length;
} list[];
};
// macros for defining evsql_query_params
-#define EVSQL_PARAM_NULL { NULL, 1 }
-#define EVSQL_PARAM_TEXT(value) { value, 0 }
-#define EVSQL_PARAM_BINARY(value, length) { value, length }
+#define EVSQL_PARAMS(result_fmt) { result_fmt,
+#define EVSQL_PARAM(typenam) { EVSQL_PARAM_ ## typenam, NULL }
+#define EVSQL_PARAMS_END { EVSQL_PARAM_INVALID, NULL } \
+ } // <<<
/*
* Result type
*/
+union evsql_result {
+ // libpq
+ PGresult *pq;
+};
+
struct evsql_result_info {
struct evsql *evsql;
int error;
- union {
- // XXX: libpq
- PGresult *pq;
-
- } result;
+ union evsql_result result;
};
/*
@@ -64,7 +96,7 @@
* The query has completed, either succesfully or unsuccesfully (look at info.error).
* info.result contains the result à la the evsql's type.
*/
-typedef void (*evsql_query_cb)(struct evsql_result_info info, void *arg);
+typedef void (*evsql_query_cb)(const struct evsql_result_info *res, void *arg);
/*
* Callback for handling connection-level errors.
@@ -86,7 +118,48 @@
/*
* Same, but uses the SQL-level support for binding parameters.
*/
-struct evsql_query *evsql_query_params (struct evsql *evsql, const char *command, struct evsql_query_params params, evsql_query_cb query_fn, void *cb_arg);
+struct evsql_query *evsql_query_params (struct evsql *evsql, const char *command, const struct evsql_query_params *params, evsql_query_cb query_fn, void *cb_arg);
+
+/*
+ * Param-building functions
+ */
+int evsql_param_string (struct evsql_query_params *params, size_t param, const char *ptr);
+int evsql_param_uint32 (struct evsql_query_params *params, size_t param, uint32_t uval);
+
+/*
+ * Result-handling functions
+ */
+
+// get error message associated with function
+const char *evsql_result_error (const struct evsql_result_info *res);
+
+// number of rows in the result
+size_t evsql_result_rows (const struct evsql_result_info *res);
+
+// number of columns in the result
+size_t evsql_result_cols (const struct evsql_result_info *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);
+
+// 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);
+
+// release the result set, freeing its memory
+void evsql_result_free (const struct evsql_result_info *res);
+
+// platform-dependant aliases
+#define evsql_result_ushort evsql_result_uint16
+
+#if _LP64
+#define evsql_result_ulong evsql_result_uint64
+#else
+#define evsql_result_ulong evsql_result_uint32
+#endif /* _LP64 */
/*
* Close a connection. Callbacks for waiting queries will not be run.
--- a/src/lib/error.h Sun Oct 12 01:09:00 2008 +0300
+++ b/src/lib/error.h Sun Oct 12 14:57:06 2008 +0300
@@ -6,6 +6,7 @@
#define ERROR(...) do { err_func(__func__, __VA_ARGS__); goto error; } while (0)
#define PERROR(...) do { perr_func(__func__, __VA_ARGS__); goto error; } while (0)
#define EERROR(_err, ...) do { eerr_func(__func__, (_err), __VA_ARGS__); goto error; } while (0)
+#define NERROR(...) do { err_func_nonl(__func__, __VA_ARGS__); goto error; } while (0)
#define FATAL(...) err_func_exit(__func__, __VA_ARGS__)
#define PFATAL(...) perr_func_exit(__func__, __VA_ARGS__)
--- a/src/lib/log.c Sun Oct 12 01:09:00 2008 +0300
+++ b/src/lib/log.c Sun Oct 12 14:57:06 2008 +0300
@@ -15,7 +15,7 @@
vfprintf(stream, fmt, va);
if (flags & LOG_DISPLAY_PERR)
- fprintf(stream, ": %s\n", strerror(err == 0 ? errno : -err));
+ fprintf(stream, ": %s\n", strerror(err == 0 ? errno : err));
if (!(flags & LOG_DISPLAY_NONL))
fprintf(stream, "\n");
--- a/src/lib/log.h Sun Oct 12 01:09:00 2008 +0300
+++ b/src/lib/log.h Sun Oct 12 14:57:06 2008 +0300
@@ -41,6 +41,7 @@
#define perr(...) _generic_err( LOG_DISPLAY_STDERR | LOG_DISPLAY_PERR, NULL, 0, __VA_ARGS__ )
#define perr_exit(...) _generic_err_exit( LOG_DISPLAY_STDERR | LOG_DISPLAY_PERR, NULL, 0, __VA_ARGS__ )
#define err_func(func, ...) _generic_err( LOG_DISPLAY_STDERR, func, 0, __VA_ARGS__ )
+#define err_func_nonl(func, ...) _generic_err( LOG_DISPLAY_STDERR | LOG_DISPLAY_NONL, func, 0, __VA_ARGS__ )
#define err_func_exit(func, ...) _generic_err_exit( LOG_DISPLAY_STDERR, func, 0, __VA_ARGS__ )
#define perr_func(func, ...) _generic_err( LOG_DISPLAY_STDERR | LOG_DISPLAY_PERR, func, 0, __VA_ARGS__ )
#define perr_func_exit(func, ...) _generic_err_exit( LOG_DISPLAY_STDERR | LOG_DISPLAY_PERR, func, 0, __VA_ARGS__ )
--- a/src/lib/misc.h Sun Oct 12 01:09:00 2008 +0300
+++ b/src/lib/misc.h Sun Oct 12 14:57:06 2008 +0300
@@ -1,10 +1,24 @@
#ifndef LIB_UTIL_H
#define LIB_UTIL_H
+#include <arpa/inet.h>
+
/*
* Initialize the given *value* with zeros
*/
#define ZINIT(obj) memset(&(obj), 0, sizeof((obj)))
+/*
+ * 64-bit hton{s,l,q}
+ */
+#ifndef WORDS_BIGENDIAN /* i.e. if (little endian) */
+#define htonq(x) (((uint64_t)htonl((x)>>32))|(((uint64_t)htonl(x))<<32))
+#define ntohq(x) htonq(x)
+#else
+#define htonq(x) ((uint64_t)(x))
+#define ntohq(x) ((uint64_t)(x))
+#endif
+
+
#endif /* LIB_UTIL_H */