--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/include/evsql.h Sun Mar 08 00:19:12 2009 +0200
@@ -0,0 +1,276 @@
+#ifndef EVSQL_H
+#define EVSQL_H
+
+/*
+ * An event-based (Postgre)SQL client API using libevent
+ */
+
+// XXX: libpq
+#include <stdint.h>
+#include <postgresql/libpq-fe.h>
+#include <event2/event.h>
+
+/*
+ * The generic context handle
+ */
+struct evsql;
+
+/*
+ * A query handle
+ */
+struct evsql_query;
+
+/*
+ * Transaction handle
+ */
+struct evsql_trans;
+
+/*
+ * Transaction type
+ */
+enum evsql_trans_type {
+ EVSQL_TRANS_DEFAULT,
+ EVSQL_TRANS_SERIALIZABLE,
+ EVSQL_TRANS_REPEATABLE_READ,
+ EVSQL_TRANS_READ_COMMITTED,
+ EVSQL_TRANS_READ_UNCOMMITTED,
+};
+
+/*
+ * Parameter type
+ */
+enum evsql_param_format {
+ EVSQL_FMT_TEXT,
+ EVSQL_FMT_BINARY,
+};
+
+enum evsql_param_type {
+ EVSQL_PARAM_INVALID,
+
+ EVSQL_PARAM_NULL_,
+
+ EVSQL_PARAM_BINARY,
+ 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
+ enum evsql_param_format result_fmt;
+
+ // the list of parameters, terminated by { 0, 0 }
+ struct evsql_query_param {
+ // 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, 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_PARAMS(result_fmt) { result_fmt,
+#define EVSQL_PARAM(typenam) { EVSQL_PARAM_ ## typenam, NULL }
+#define EVSQL_PARAMS_END { EVSQL_PARAM_INVALID, NULL } \
+ } // <<<
+
+/*
+ * Result type
+ */
+struct evsql_result_info {
+ struct evsql *evsql;
+ struct evsql_trans *trans;
+
+ int error;
+
+ union evsql_result {
+ // libpq
+ PGresult *pq;
+ } result;
+};
+
+/*
+ * Callback for handling query results.
+ *
+ * The query has completed, either succesfully or unsuccesfully (nonzero .error).
+ *
+ * Use the evsql_result_* functions to manipulate the results.
+ */
+typedef void (*evsql_query_cb)(const struct evsql_result_info *res, void *arg);
+
+/*
+ * Callback for handling global-level errors.
+ *
+ * The evsql is not useable anymore.
+ *
+ * XXX: this is not actually called yet, no retry logic implemented.
+ */
+typedef void (*evsql_error_cb)(struct evsql *evsql, void *arg);
+
+/*
+ * Callback for handling transaction-level errors.
+ *
+ * The transaction is not useable anymore.
+ */
+typedef void (*evsql_trans_error_cb)(struct evsql_trans *trans, void *arg);
+
+/*
+ * The transaction is ready for use.
+ */
+typedef void (*evsql_trans_ready_cb)(struct evsql_trans *trans, void *arg);
+
+/*
+ * The transaction was commited, and should not be used anymore.
+ */
+typedef void (*evsql_trans_done_cb)(struct evsql_trans *trans, void *arg);
+
+/*
+ * Create a new PostgreSQL/libpq(evpq) -based evsql using the given conninfo.
+ *
+ * The given conninfo must stay valid for the duration of the evsql's lifetime.
+ */
+struct evsql *evsql_new_pq (struct event_base *ev_base, const char *pq_conninfo, evsql_error_cb error_fn, void *cb_arg);
+
+/*
+ * Create a new transaction.
+ *
+ * Transactions are separate connections that provide transaction-isolation.
+ *
+ * Once the transaction is ready for use, ready_fn will be called. If the transaction fails, any pending query will be
+ * forgotten, and error_fn called. This also includes some (but not all) cases where evsql_query returns nonzero.
+ *
+ */
+struct evsql_trans *evsql_trans (struct evsql *evsql, enum evsql_trans_type type, evsql_trans_error_cb error_fn, evsql_trans_ready_cb ready_fn, evsql_trans_done_cb done_fn, void *cb_arg);
+
+/*
+ * Queue the given query for execution.
+ *
+ * If trans is specified (not NULL), then the transaction must be idle, and the query will be executed in that
+ * transaction's context. Otherwise, the query will be executed without a transaction, andmay be executed immediately,
+ * or if other similar queries are running, it will be queued for later execution.
+ *
+ * Once the query is complete (got a result, got an error, the connection failed), then the query_cb will be triggered.
+ */
+struct evsql_query *evsql_query (struct evsql *evsql, struct evsql_trans *trans, const char *command, evsql_query_cb query_fn, void *cb_arg);
+
+/*
+ * 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);
+
+/*
+ * Abort a query, the query callback will not be called, the query and any possible results will be discarded.
+ *
+ * This does not garuntee that the query will not execute, simply that you won't get the results.
+ *
+ * If the query is part of a transaction, then trans must be given, and the query must be the query that is currently
+ * executing on that trans. The transaction's ready_fn will be called once the query has been aborted.
+ */
+void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query);
+
+/*
+ * Commit a transaction, calling done_fn if it was succesfull (error_fn otherwise).
+ *
+ * trans must be idle, just like for evsql_query.
+ *
+ * done_fn will never be called directly, always via the event loop.
+ *
+ * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error.
+ */
+int evsql_trans_commit (struct evsql_trans *trans);
+
+/*
+ * Abort a transaction, rolling it back. No callbacks will be called.
+ *
+ * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error.
+ */
+void evsql_trans_abort (struct evsql_trans *trans);
+
+/*
+ * Transaction-handling functions
+ */
+
+// error string, meant to be called from evsql_trans_error_cb
+const char *evsql_trans_error (struct evsql_trans *trans);
+
+/*
+ * Param-building functions
+ */
+int evsql_param_binary (struct evsql_query_params *params, size_t param, const char *ptr, size_t len);
+int evsql_param_string (struct evsql_query_params *params, size_t param, const char *ptr);
+int evsql_param_uint16 (struct evsql_query_params *params, size_t param, uint16_t uval);
+int evsql_param_uint32 (struct evsql_query_params *params, size_t param, uint32_t uval);
+int evsql_param_null (struct evsql_query_params *params, size_t param);
+int evsql_params_clear (struct evsql_query_params *params);
+
+/*
+ * Query-handling functions
+ */
+
+// print out a textual repr of the given query/params via DEBUG
+void evsql_query_debug (const char *sql, const struct evsql_query_params *params);
+
+/*
+ * 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);
+
+// number of affected rows for UPDATE/INSERT
+size_t evsql_result_affected (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.
+ *
+ * XXX: not implemented yet.
+ */
+void evsql_close (struct evsql *evsql);
+
+#endif /* EVSQL_H */
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/core.c Sun Mar 08 00:19:12 2009 +0200
@@ -0,0 +1,1025 @@
+#define _GNU_SOURCE
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+
+#include "internal.h"
+#include "lib/log.h"
+#include "lib/error.h"
+#include "lib/misc.h"
+
+/*
+ * A couple function prototypes
+ */
+static void _evsql_pump (struct evsql *evsql, struct evsql_conn *conn);
+
+/*
+ * Actually execute the given query.
+ *
+ * The backend should be able to accept the query at this time.
+ *
+ * You should assume that if trying to execute a query fails, then the connection should also be considred as failed.
+ */
+static int _evsql_query_exec (struct evsql_conn *conn, struct evsql_query *query, const char *command) {
+ int err;
+
+ DEBUG("evsql.%p: exec query=%p on trans=%p on conn=%p:", conn->evsql, query, conn->trans, conn);
+
+ switch (conn->evsql->type) {
+ case EVSQL_EVPQ:
+ // got params?
+ if (query->params.count) {
+ err = evpq_query_params(conn->engine.evpq, command,
+ query->params.count,
+ query->params.types,
+ query->params.values,
+ query->params.lengths,
+ query->params.formats,
+ query->params.result_format
+ );
+
+ } else {
+ // plain 'ole query
+ err = evpq_query(conn->engine.evpq, command);
+ }
+
+ if (err) {
+ if (PQstatus(evpq_pgconn(conn->engine.evpq)) != CONNECTION_OK)
+ WARNING("conn failed");
+ else
+ WARNING("query failed, dropping conn as well");
+ }
+
+ break;
+
+ default:
+ FATAL("evsql->type");
+ }
+
+ if (!err)
+ // assign the query
+ conn->query = query;
+
+ return err;
+}
+
+/*
+ * Free the query and related resources, doesn't trigger any callbacks or remove from any queues.
+ *
+ * The command should already be taken care of (NULL).
+ */
+static void _evsql_query_free (struct evsql_query *query) {
+ if (!query)
+ return;
+
+ assert(query->command == NULL);
+
+ // free params if present
+ free(query->params.types);
+ free(query->params.values);
+ free(query->params.lengths);
+ free(query->params.formats);
+
+ // free the query itself
+ free(query);
+}
+
+/*
+ * Execute the callback if res is given, and free the query.
+ *
+ * The query has been aborted, it will simply be freed
+ */
+static void _evsql_query_done (struct evsql_query *query, const struct evsql_result_info *res) {
+ if (res) {
+ if (query->cb_fn)
+ // call the callback
+ query->cb_fn(res, query->cb_arg);
+ else
+ WARNING("supressing cb_fn because query was aborted");
+ }
+
+ // free
+ _evsql_query_free(query);
+}
+
+/*
+ * XXX:
+ * /
+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->query_queue)) != NULL) {
+ _evsql_query_done(query, res);
+
+ TAILQ_REMOVE(&evsql->query_queue, query, entry);
+ }
+
+ // free
+ free(evsql);
+}
+*/
+
+/*
+ * Free the transaction, it should already be deassociated from the query and conn.
+ */
+static void _evsql_trans_free (struct evsql_trans *trans) {
+ // ensure we don't leak anything
+ assert(trans->query == NULL);
+ assert(trans->conn == NULL);
+
+ // free
+ free(trans);
+}
+
+/*
+ * Release a connection. It should already be deassociated from the trans and query.
+ *
+ * Releases the engine, removes from the conn_list and frees this.
+ */
+static void _evsql_conn_release (struct evsql_conn *conn) {
+ // ensure we don't leak anything
+ assert(conn->trans == NULL);
+ assert(conn->query == NULL);
+
+ // release the engine
+ switch (conn->evsql->type) {
+ case EVSQL_EVPQ:
+ evpq_release(conn->engine.evpq);
+ break;
+
+ default:
+ FATAL("evsql->type");
+ }
+
+ // remove from list
+ LIST_REMOVE(conn, entry);
+
+ // catch deadlocks
+ assert(!LIST_EMPTY(&conn->evsql->conn_list) || TAILQ_EMPTY(&conn->evsql->query_queue));
+
+ // free
+ free(conn);
+}
+
+/*
+ * Release a transaction, it should already be deassociated from the query.
+ *
+ * Perform a two-way-deassociation with the conn, and then free the trans.
+ */
+static void _evsql_trans_release (struct evsql_trans *trans) {
+ assert(trans->query == NULL);
+ assert(trans->conn != NULL);
+
+ // deassociate the conn
+ trans->conn->trans = NULL; trans->conn = NULL;
+
+ // free the trans
+ _evsql_trans_free(trans);
+}
+
+/*
+ * Fail a single query, this will trigger the callback and free it.
+ *
+ * NOTE: Only for *TRANSACTIONLESS* queries.
+ */
+static void _evsql_query_fail (struct evsql* evsql, struct evsql_query *query) {
+ struct evsql_result_info res; ZINIT(res);
+
+ // set up the result_info
+ res.evsql = evsql;
+ res.trans = NULL;
+ res.error = 1;
+
+ // finish off the query
+ _evsql_query_done(query, &res);
+}
+
+/*
+ * Fail a transaction, this will silently drop any query, trigger the error callback, two-way-deassociate/release the
+ * conn, and then free the trans.
+ */
+static void _evsql_trans_fail (struct evsql_trans *trans) {
+ if (trans->query) {
+ // free the query silently
+ _evsql_query_free(trans->query); trans->query = NULL;
+
+ // also deassociate it from the conn!
+ trans->conn->query = NULL;
+ }
+
+ // tell the user
+ // XXX: trans is in a bad state during this call
+ if (trans->error_fn)
+ trans->error_fn(trans, trans->cb_arg);
+ else
+ WARNING("supressing error because error_fn was NULL");
+
+ // deassociate and release the conn
+ trans->conn->trans = NULL; _evsql_conn_release(trans->conn); trans->conn = NULL;
+
+ // pump the queue for requests that were waiting for this connection
+ _evsql_pump(trans->evsql, NULL);
+
+ // free the trans
+ _evsql_trans_free(trans);
+}
+
+/*
+ * Fail a connection. If the connection is transactional, this will just call _evsql_trans_fail, but otherwise it will
+ * fail any ongoing query, and then release the connection.
+ */
+static void _evsql_conn_fail (struct evsql_conn *conn) {
+ if (conn->trans) {
+ // let transactions handle their connection failures
+ _evsql_trans_fail(conn->trans);
+
+ } else {
+ if (conn->query) {
+ // fail the in-progress query
+ _evsql_query_fail(conn->evsql, conn->query); conn->query = NULL;
+ }
+
+ // finish off the whole connection
+ _evsql_conn_release(conn);
+ }
+}
+
+/*
+ * Processes enqueued non-transactional queries until the queue is empty, or we managed to exec a query.
+ *
+ * If execing a query on a connection fails, both the query and the connection are failed (in that order).
+ *
+ * Any further queries will then also be failed, because there's no reconnection/retry logic yet.
+ *
+ * This means that if conn is NULL, all queries are failed.
+ */
+static void _evsql_pump (struct evsql *evsql, struct evsql_conn *conn) {
+ struct evsql_query *query;
+ int err;
+
+ // look for waiting queries
+ while ((query = TAILQ_FIRST(&evsql->query_queue)) != NULL) {
+ // dequeue
+ TAILQ_REMOVE(&evsql->query_queue, query, entry);
+
+ if (conn) {
+ // try and execute it
+ err = _evsql_query_exec(conn, query, query->command);
+ }
+
+ // free the command buf
+ free(query->command); query->command = NULL;
+
+ if (err || !conn) {
+ if (!conn) {
+ // warn when dropping queries
+ WARNING("failing query becuse there are no conns");
+ }
+
+ // fail the query
+ _evsql_query_fail(evsql, query);
+
+ if (conn) {
+ // fail the connection
+ WARNING("failing the connection because a query-exec failed");
+
+ _evsql_conn_fail(conn); conn = NULL;
+ }
+
+ } else {
+ // we have succesfully enqueued a query, and we can wait for this connection to complete
+ break;
+
+ }
+
+ // handle the rest of the queue
+ }
+
+ // ok
+ return;
+}
+
+/*
+ * Callback for a trans's 'BEGIN' query, which means the transaction is now ready for use.
+ */
+static void _evsql_trans_ready (const struct evsql_result_info *res, void *arg) {
+ (void) arg;
+
+ assert(res->trans);
+
+ // check for errors
+ if (res->error)
+ ERROR("transaction 'BEGIN' failed: %s", evsql_result_error(res));
+
+ // transaction is now ready for use
+ res->trans->ready_fn(res->trans, res->trans->cb_arg);
+
+ // good
+ return;
+
+error:
+ _evsql_trans_fail(res->trans);
+}
+
+/*
+ * The transaction's connection is ready, send the 'BEGIN' query.
+ *
+ * If anything fails, calls _evsql_trans_fail and returns nonzero, zero on success
+ */
+static int _evsql_trans_conn_ready (struct evsql *evsql, struct evsql_trans *trans) {
+ char trans_sql[EVSQL_QUERY_BEGIN_BUF];
+ const char *isolation_level;
+ int ret;
+
+ // determine the isolation_level to use
+ switch (trans->type) {
+ case EVSQL_TRANS_DEFAULT:
+ isolation_level = NULL; break;
+
+ case EVSQL_TRANS_SERIALIZABLE:
+ isolation_level = "SERIALIZABLE"; break;
+
+ case EVSQL_TRANS_REPEATABLE_READ:
+ isolation_level = "REPEATABLE READ"; break;
+
+ case EVSQL_TRANS_READ_COMMITTED:
+ isolation_level = "READ COMMITTED"; break;
+
+ case EVSQL_TRANS_READ_UNCOMMITTED:
+ isolation_level = "READ UNCOMMITTED"; break;
+
+ default:
+ FATAL("trans->type: %d", trans->type);
+ }
+
+ // build the trans_sql
+ if (isolation_level)
+ ret = snprintf(trans_sql, EVSQL_QUERY_BEGIN_BUF, "BEGIN TRANSACTION ISOLATION LEVEL %s", isolation_level);
+ else
+ ret = snprintf(trans_sql, EVSQL_QUERY_BEGIN_BUF, "BEGIN TRANSACTION");
+
+ // make sure it wasn't truncated
+ if (ret >= EVSQL_QUERY_BEGIN_BUF)
+ ERROR("trans_sql overflow: %d >= %d", ret, EVSQL_QUERY_BEGIN_BUF);
+
+ // execute the query
+ if (evsql_query(evsql, trans, trans_sql, _evsql_trans_ready, NULL) == NULL)
+ ERROR("evsql_query");
+
+ // success
+ return 0;
+
+error:
+ // fail the transaction
+ _evsql_trans_fail(trans);
+
+ return -1;
+}
+
+/*
+ * The evpq connection was succesfully established.
+ */
+static void _evsql_evpq_connected (struct evpq_conn *_conn, void *arg) {
+ struct evsql_conn *conn = arg;
+
+ if (conn->trans)
+ // notify the transaction
+ // don't care about errors
+ (void) _evsql_trans_conn_ready(conn->evsql, conn->trans);
+
+ else
+ // pump any waiting transactionless queries
+ _evsql_pump(conn->evsql, conn);
+}
+
+/*
+ * Got one result on this evpq connection.
+ */
+static void _evsql_evpq_result (struct evpq_conn *_conn, PGresult *result, void *arg) {
+ struct evsql_conn *conn = arg;
+ struct evsql_query *query = conn->query;
+
+ assert(query != NULL);
+
+ // if we get multiple results, only return the first one
+ if (query->result.evpq) {
+ WARNING("[evsql] evpq query returned multiple results, discarding previous one");
+
+ PQclear(query->result.evpq); query->result.evpq = NULL;
+ }
+
+ // remember the result
+ query->result.evpq = result;
+}
+
+/*
+ * No more results for this query.
+ */
+static void _evsql_evpq_done (struct evpq_conn *_conn, void *arg) {
+ struct evsql_conn *conn = arg;
+ struct evsql_query *query = conn->query;
+ struct evsql_result_info res; ZINIT(res);
+
+ assert(query != NULL);
+
+ // set up the result_info
+ res.evsql = conn->evsql;
+ res.trans = conn->trans;
+
+ 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");
+
+ 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 {
+ res.error = 0;
+ res.result.pq = query->result.evpq;
+
+ }
+
+ // de-associate the query from the connection
+ conn->query = NULL;
+
+ // how we handle query completion depends on if we're a transaction or not
+ if (conn->trans) {
+ // we can deassign the trans's query
+ conn->trans->query = NULL;
+
+ // was an abort?
+ if (!query->cb_fn)
+ // notify the user that the transaction query has been aborted
+ conn->trans->ready_fn(conn->trans, conn->trans->cb_arg);
+
+ // then hand the query to the user
+ _evsql_query_done(query, &res);
+
+ } else {
+ // a transactionless query, so just finish it off and pump any other waiting ones
+ _evsql_query_done(query, &res);
+
+ // pump the next one
+ _evsql_pump(conn->evsql, conn);
+ }
+}
+
+/*
+ * The connection failed.
+ */
+static void _evsql_evpq_failure (struct evpq_conn *_conn, void *arg) {
+ struct evsql_conn *conn = arg;
+
+ // just fail the conn
+ _evsql_conn_fail(conn);
+}
+
+/*
+ * Our evpq behaviour
+ */
+static struct evpq_callback_info _evsql_evpq_cb_info = {
+ .fn_connected = _evsql_evpq_connected,
+ .fn_result = _evsql_evpq_result,
+ .fn_done = _evsql_evpq_done,
+ .fn_failure = _evsql_evpq_failure,
+};
+
+/*
+ * Allocate the generic evsql context.
+ */
+static struct evsql *_evsql_new_base (struct event_base *ev_base, evsql_error_cb error_fn, void *cb_arg) {
+ struct evsql *evsql = NULL;
+
+ // allocate it
+ if ((evsql = calloc(1, sizeof(*evsql))) == NULL)
+ ERROR("calloc");
+
+ // store
+ evsql->ev_base = ev_base;
+ evsql->error_fn = error_fn;
+ evsql->cb_arg = cb_arg;
+
+ // init
+ LIST_INIT(&evsql->conn_list);
+ TAILQ_INIT(&evsql->query_queue);
+
+ // done
+ return evsql;
+
+error:
+ return NULL;
+}
+
+/*
+ * Start a new connection and add it to the list, it won't be ready until _evsql_evpq_connected is called
+ */
+static struct evsql_conn *_evsql_conn_new (struct evsql *evsql) {
+ struct evsql_conn *conn = NULL;
+
+ // allocate
+ if ((conn = calloc(1, sizeof(*conn))) == NULL)
+ ERROR("calloc");
+
+ // init
+ conn->evsql = evsql;
+
+ // connect the engine
+ switch (evsql->type) {
+ case EVSQL_EVPQ:
+ if ((conn->engine.evpq = evpq_connect(evsql->ev_base, evsql->engine_conf.evpq, _evsql_evpq_cb_info, conn)) == NULL)
+ goto error;
+
+ break;
+
+ default:
+ FATAL("evsql->type");
+ }
+
+ // add it to the list
+ LIST_INSERT_HEAD(&evsql->conn_list, conn, entry);
+
+ // success
+ return conn;
+
+error:
+ free(conn);
+
+ return NULL;
+}
+
+struct evsql *evsql_new_pq (struct event_base *ev_base, const char *pq_conninfo, evsql_error_cb error_fn, void *cb_arg) {
+ struct evsql *evsql = NULL;
+
+ // base init
+ if ((evsql = _evsql_new_base (ev_base, error_fn, cb_arg)) == NULL)
+ goto error;
+
+ // store conf
+ evsql->engine_conf.evpq = pq_conninfo;
+
+ // pre-create one connection
+ if (_evsql_conn_new(evsql) == NULL)
+ goto error;
+
+ // done
+ return evsql;
+
+error:
+ // XXX: more complicated than this?
+ free(evsql);
+
+ return NULL;
+}
+
+/*
+ * Checks if the connection is already allocated for some other trans/query.
+ *
+ * Returns:
+ * 0 connection idle, can be allocated
+ * >1 connection busy
+ */
+static int _evsql_conn_busy (struct evsql_conn *conn) {
+ // transactions get the connection to themselves
+ if (conn->trans)
+ return 1;
+
+ // if it has a query assigned, it's busy
+ if (conn->query)
+ return 1;
+
+ // otherwise, it's all idle
+ return 0;
+}
+
+/*
+ * Checks if the connection is ready for use (i.e. _evsql_evpq_connected was called).
+ *
+ * The connection should not already have a query running.
+ *
+ * Returns
+ * <0 the connection is not valid (failed, query in progress)
+ * 0 the connection is still pending, and will become ready at some point
+ * >0 it's ready
+ */
+static int _evsql_conn_ready (struct evsql_conn *conn) {
+ switch (conn->evsql->type) {
+ case EVSQL_EVPQ: {
+ enum evpq_state state = evpq_state(conn->engine.evpq);
+
+ switch (state) {
+ case EVPQ_CONNECT:
+ return 0;
+
+ case EVPQ_CONNECTED:
+ return 1;
+
+ case EVPQ_QUERY:
+ case EVPQ_INIT:
+ case EVPQ_FAILURE:
+ return -1;
+
+ default:
+ FATAL("evpq_state: %d", state);
+ }
+
+ }
+
+ default:
+ FATAL("evsql->type: %d", conn->evsql->type);
+ }
+}
+
+/*
+ * Allocate a connection for use and return it via *conn_ptr, or if may_queue is nonzero and the connection pool is
+ * getting full, return NULL (query should be queued).
+ *
+ * Note that the returned connection might not be ready for use yet (if we created a new one, see _evsql_conn_ready).
+ *
+ * Returns zero if a connection was found or the request should be queued, or nonzero if something failed and the
+ * request should be dropped.
+ */
+static int _evsql_conn_get (struct evsql *evsql, struct evsql_conn **conn_ptr, int may_queue) {
+ int have_nontrans = 0;
+ *conn_ptr = NULL;
+
+ // find a connection that isn't busy and is ready (unless the query queue is empty).
+ LIST_FOREACH(*conn_ptr, &evsql->conn_list, entry) {
+ // we can only have a query enqueue itself if there is a non-trans conn it can later use
+ if (!(*conn_ptr)->trans)
+ have_nontrans = 1;
+
+ // skip busy conns always
+ if (_evsql_conn_busy(*conn_ptr))
+ continue;
+
+ // accept pending conns as long as there are NO enqueued queries (might cause deadlock otherwise)
+ if (_evsql_conn_ready(*conn_ptr) == 0 && TAILQ_EMPTY(&evsql->query_queue))
+ break;
+
+ // accept conns that are in a fully ready state
+ if (_evsql_conn_ready(*conn_ptr) > 0)
+ break;
+ }
+
+ // if we found an idle connection, we can just return that right away
+ if (*conn_ptr)
+ return 0;
+
+ // return NULL if may_queue and we have a non-trans conn that we can, at some point, use
+ if (may_queue && have_nontrans)
+ return 0;
+
+ // we need to open a new connection
+ if ((*conn_ptr = _evsql_conn_new(evsql)) == NULL)
+ goto error;
+
+ // good
+ return 0;
+error:
+ return -1;
+}
+
+struct evsql_trans *evsql_trans (struct evsql *evsql, enum evsql_trans_type type, evsql_trans_error_cb error_fn, evsql_trans_ready_cb ready_fn, evsql_trans_done_cb done_fn, void *cb_arg) {
+ struct evsql_trans *trans = NULL;
+
+ // allocate it
+ if ((trans = calloc(1, sizeof(*trans))) == NULL)
+ ERROR("calloc");
+
+ // store
+ trans->evsql = evsql;
+ trans->ready_fn = ready_fn;
+ trans->done_fn = done_fn;
+ trans->cb_arg = cb_arg;
+ trans->type = type;
+
+ // find a connection
+ if (_evsql_conn_get(evsql, &trans->conn, 0))
+ ERROR("_evsql_conn_get");
+
+ // associate the conn
+ trans->conn->trans = trans;
+
+ // is it already ready?
+ if (_evsql_conn_ready(trans->conn) > 0) {
+ // call _evsql_trans_conn_ready directly, it will handle cleanup (silently, !error_fn)
+ if (_evsql_trans_conn_ready(evsql, trans)) {
+ // return NULL directly
+ return NULL;
+ }
+
+ } else {
+ // otherwise, wait for the conn to be ready
+
+ }
+
+ // and let it pass errors to the user
+ trans->error_fn = error_fn;
+
+ // ok
+ return trans;
+
+error:
+ free(trans);
+
+ return NULL;
+}
+
+/*
+ * Validate and allocate the basic stuff for a new query.
+ */
+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;
+
+ // if it's part of a trans, then make sure the trans is idle
+ if (trans && trans->query)
+ ERROR("transaction is busy");
+
+ // allocate it
+ if ((query = calloc(1, sizeof(*query))) == NULL)
+ ERROR("calloc");
+
+ // store
+ query->cb_fn = query_fn;
+ query->cb_arg = cb_arg;
+
+ // success
+ return query;
+
+error:
+ 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) {
+ // it's an in-transaction query
+ assert(trans->query == NULL);
+
+ // assign the query
+ trans->query = query;
+
+ // execute directly
+ if (_evsql_query_exec(trans->conn, query, command)) {
+ // ack, fail the transaction
+ _evsql_trans_fail(trans);
+
+ // caller frees query
+ goto error;
+ }
+
+ } else {
+ struct evsql_conn *conn;
+
+ // find an idle connection
+ if ((_evsql_conn_get(evsql, &conn, 1)))
+ ERROR("couldn't allocate a connection for the query");
+
+ // we must enqueue if no idle conn or the conn is not yet ready
+ if (conn && _evsql_conn_ready(conn) > 0) {
+ // execute directly
+ if (_evsql_query_exec(conn, query, command)) {
+ // ack, fail the connection
+ _evsql_conn_fail(conn);
+
+ // make sure we don't deadlock any queries, but if this query got a conn directly, then we shouldn't
+ // have any queries enqueued anyways
+ assert(TAILQ_EMPTY(&evsql->query_queue));
+
+ // caller frees query
+ goto error;
+ }
+
+ } else {
+ // copy the command for later execution
+ if ((query->command = strdup(command)) == NULL)
+ ERROR("strdup");
+
+ // enqueue until some connection pumps the queue
+ TAILQ_INSERT_TAIL(&evsql->query_queue, query, entry);
+ }
+ }
+
+ // ok, 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_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;
+
+ assert(res->trans);
+
+ // check for errors
+ if (res->error)
+ ERROR("transaction 'COMMIT' failed: %s", evsql_result_error(res));
+
+ // transaction is now done
+ res->trans->done_fn(res->trans, res->trans->cb_arg);
+
+ // release it
+ _evsql_trans_release(res->trans);
+
+ // success
+ return;
+
+error:
+ _evsql_trans_fail(res->trans);
+}
+
+int evsql_trans_commit (struct evsql_trans *trans) {
+ static const char *sql = "COMMIT TRANSACTION";
+
+ if (trans->query)
+ ERROR("cannot COMMIT because transaction is still busy");
+
+ // query
+ if (evsql_query(trans->evsql, trans, sql, _evsql_trans_commit_res, NULL) == NULL)
+ goto error;
+
+ // mark it as commited in case someone wants to abort it
+ trans->has_commit = 1;
+
+ // success
+ return 0;
+
+error:
+ return -1;
+}
+
+void _evsql_trans_rollback_res (const struct evsql_result_info *res, void *arg) {
+ (void) arg;
+
+ assert(res->trans);
+
+ // fail the connection on errors
+ if (res->error)
+ ERROR("transaction 'ROLLBACK' failed: %s", evsql_result_error(res));
+
+ // release it
+ _evsql_trans_release(res->trans);
+
+ // success
+ return;
+
+error:
+ // fail the connection too, errors are supressed
+ _evsql_trans_fail(res->trans);
+}
+
+/*
+ * Used as the ready_fn callback in case of abort, otherwise directly
+ */
+void _evsql_trans_rollback (struct evsql_trans *trans, void *unused) {
+ static const char *sql = "ROLLBACK TRANSACTION";
+
+ (void) unused;
+
+ // query
+ if (evsql_query(trans->evsql, trans, sql, _evsql_trans_rollback_res, NULL) == NULL) {
+ // fail the transaction/connection
+ _evsql_trans_fail(trans);
+ }
+
+}
+
+void evsql_trans_abort (struct evsql_trans *trans) {
+ // supress errors
+ trans->error_fn = NULL;
+
+ if (trans->has_commit) {
+ // abort after commit doesn't make sense
+ FATAL("transaction was already commited");
+ }
+
+ if (trans->query) {
+ // gah, some query is running
+ WARNING("aborting pending query");
+
+ // prepare to rollback once complete
+ trans->ready_fn = _evsql_trans_rollback;
+
+ // abort
+ evsql_query_abort(trans, trans->query);
+
+ } else {
+ // just rollback directly
+ _evsql_trans_rollback(trans, NULL);
+
+ }
+}
+
--- a/src/dbfs.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,64 +0,0 @@
-
-/*
- * A simple PostgreSQL-based filesystem.
- */
-
-#include <stdlib.h>
-#include <string.h>
-#include <errno.h>
-#include <assert.h>
-
-#include <event2/event.h>
-
-#include "dbfs.h"
-#include "evfuse.h"
-#include "evsql.h"
-#include "lib/log.h"
-#include "lib/signals.h"
-#include "lib/misc.h"
-
-#define CONNINFO_DEFAULT "dbname=dbfs port=5433"
-
-int main (int argc, char **argv) {
- struct event_base *ev_base = NULL;
- struct signals *signals = NULL;
- struct dbfs *ctx = NULL;
- const char *db_conninfo;
- struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv);
-
- // parse args, XXX: fuse_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 dbfs
- if ((ctx = dbfs_new(ev_base, &fuse_args, db_conninfo)) == NULL)
- ERROR("dbfs_new");
-
- // run libevent
- INFO("running libevent loop");
-
- if (event_base_dispatch(ev_base))
- PERROR("event_base_dispatch");
-
- // clean shutdown
-
-error :
- if (ctx)
- dbfs_free(ctx);
-
- if (signals)
- signals_free(signals);
-
- if (ev_base)
- event_base_free(ev_base);
-
- fuse_opt_free_args(&fuse_args);
-}
-
--- a/src/dbfs.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,25 +0,0 @@
-#ifndef DBFS_H
-#define DBFS_H
-
-#include "evfuse.h"
-
-/*
- * External interface for dbfs
- */
-
-/*
- * Context struct.
- */
-struct dbfs;
-
-/*
- * Create the evsql and evfuse contexts and run the fs
- */
-struct dbfs *dbfs_new (struct event_base *ev_base, struct fuse_args *args, const char *db_conninfo);
-
-/*
- * Release the dbfs's resources and free it
- */
-void dbfs_free (struct dbfs *ctx);
-
-#endif /* DBFS_H */
--- a/src/dbfs/attr.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,178 +0,0 @@
-
-#include "dbfs.h"
-#include "../lib/log.h"
-#include "../lib/misc.h"
-
-// max. size for a setattr UPDATE query
-#define DBFS_SETATTR_SQL_MAX 512
-
-// for building the setattr UPDATE
-#define FIELD(to_set, flag, field, value) ((to_set) & (flag)) ? (field " = " value ", ") : ""
-
-void _dbfs_attr_res (const struct evsql_result_info *res, void *arg) {
- struct fuse_req *req = arg;
- struct stat st; ZINIT(st);
- int err = 0;
-
- uint32_t ino;
-
- // check the results
- if ((err = _dbfs_check_res(res, 1, 5)))
- SERROR(err = (err == 1 ? ENOENT : EIO));
-
- // get our data
- if (0
- || evsql_result_uint32(res, 0, 0, &ino, 0 ) // inodes.ino
- )
- EERROR(err = EIO, "invalid db data");
-
-
- INFO("\t[dbfs.getattr %p] -> ino=%lu, stat follows", req, (unsigned long int) ino);
-
- // inode
- st.st_ino = ino;
-
- // stat attrs
- if ((err = _dbfs_stat_info(&st, res, 0, 1)))
- goto error;
-
- // reply
- if ((err = fuse_reply_attr(req, &st, st.st_nlink ? CACHE_TIMEOUT : 0)))
- 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_getattr (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct evsql_query *query;
- int err;
-
- (void) fi;
-
- INFO("[dbfs.getattr %p] ino=%lu", req, ino);
-
- const char *sql =
- "SELECT"
- " inodes.ino, " DBFS_STAT_COLS
- " FROM inodes"
- " WHERE inodes.ino = $1::int4";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT32 ),
-
- EVSQL_PARAMS_END
- };
-
- // build params
- if (0
- || evsql_param_uint32(¶ms, 0, ino)
- )
- SERROR(err = EIO);
-
- // query
- if ((query = evsql_query_params(ctx->db, NULL, sql, ¶ms, _dbfs_attr_res, req)) == NULL)
- SERROR(err = EIO);
-
- // handle interrupts
- fuse_req_interrupt_func(req, dbfs_interrupt_query, query);
-
- // wait
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
-
-
-void dbfs_setattr (struct fuse_req *req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct evsql_query *query;
- int err;
- int ret;
-
- char sql_buf[DBFS_SETATTR_SQL_MAX];
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT16 ), // inodes.mode
- EVSQL_PARAM ( UINT32 ), // inodes.uid
- EVSQL_PARAM ( UINT32 ), // inodes.gid
- EVSQL_PARAM ( UINT32 ), // data size
- EVSQL_PARAM ( UINT32 ), // ino
-
- EVSQL_PARAMS_END
- };
-
- // log
- INFO("[dbfs.setattr %p] ino=%lu, fileop=%p: ", req, ino, fi && fi->fh ? (void*) fi->fh : NULL);
-
- if (to_set & FUSE_SET_ATTR_MODE) {
- // ignore the S_IFMT
- attr->st_mode &= 07777;
-
- INFO("\tmode = %08o", attr->st_mode);
- }
-
- if (to_set & FUSE_SET_ATTR_UID)
- INFO("\tuid = %u", attr->st_uid);
-
- if (to_set & FUSE_SET_ATTR_GID)
- INFO("\tgid = %u", attr->st_gid);
-
- if (to_set & FUSE_SET_ATTR_SIZE)
- INFO("\tsize = %lu", attr->st_size);
-
- if (to_set & FUSE_SET_ATTR_ATIME)
- INFO("\tatime = %lu", attr->st_atime);
-
- if (to_set & FUSE_SET_ATTR_MTIME)
- INFO("\tmtime = %lu", attr->st_mtime);
-
- // the SQL
- if ((ret = snprintf(sql_buf, DBFS_SETATTR_SQL_MAX,
- "UPDATE inodes SET"
- " %s%s%s%s ino = ino"
- " WHERE inodes.ino = $5::int4"
- " RETURNING inodes.ino, " DBFS_STAT_COLS,
-
- FIELD(to_set, FUSE_SET_ATTR_MODE, "mode", "$1::int2"),
- FIELD(to_set, FUSE_SET_ATTR_UID, "uid", "$2::int4"),
- FIELD(to_set, FUSE_SET_ATTR_GID, "gid", "$3::int4"),
- FIELD(to_set, FUSE_SET_ATTR_SIZE, "data", "lo_otruncate(data, $4::int4)")
- )) >= DBFS_SETATTR_SQL_MAX && (err = EIO))
- ERROR("sql_buf is too small: %i", ret);
-
- // the params...
- if (0
- || ( evsql_params_clear(¶ms) )
- || ((to_set & FUSE_SET_ATTR_MODE ) && evsql_param_uint16(¶ms, 0, attr->st_mode) )
- || ((to_set & FUSE_SET_ATTR_UID ) && evsql_param_uint32(¶ms, 1, attr->st_uid) )
- || ((to_set & FUSE_SET_ATTR_GID ) && evsql_param_uint32(¶ms, 2, attr->st_gid) )
- || ((to_set & FUSE_SET_ATTR_SIZE ) && evsql_param_uint32(¶ms, 3, attr->st_size) )
- || ( evsql_param_uint32(¶ms, 4, ino) )
- )
- SERROR(err = EIO);
-
- // trace the query
- evsql_query_debug(sql_buf, ¶ms);
-
- // query... we can pretend it's a getattr :)
- if ((query = evsql_query_params(ctx->db, NULL, sql_buf, ¶ms, _dbfs_attr_res, req)) == NULL)
- SERROR(err = EIO);
-
- // handle interrupts
- fuse_req_interrupt_func(req, dbfs_interrupt_query, query);
-
- // wait
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
--- a/src/dbfs/common.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,114 +0,0 @@
-
-#include <string.h>
-
-#include "dbfs.h"
-#include "../lib/log.h"
-
-mode_t _dbfs_mode (const char *type) {
- if (!strcmp(type, "DIR"))
- return S_IFDIR;
-
- if (!strcmp(type, "REG"))
- return S_IFREG;
-
- if (!strcmp(type, "LNK"))
- return S_IFLNK;
-
- else {
- WARNING("[dbfs] weird mode-type: %s", type);
- return 0;
- }
-}
-
-int _dbfs_check_res (const struct evsql_result_info *res, size_t rows, size_t cols) {
- int err = 0;
-
- // check if it failed
- if (res->error)
- NERROR(evsql_result_error(res));
-
- // not found?
- if (evsql_result_rows(res) == 0 && evsql_result_affected(res) == 0)
- SERROR(err = 1);
-
- // duplicate rows?
- if (rows && evsql_result_rows(res) != rows)
- ERROR("wrong number of rows returned");
-
- // correct number of columns
- if (evsql_result_cols(res) != cols)
- ERROR("wrong number of columns: %zu", evsql_result_cols(res));
-
- // good
- return 0;
-
-error:
- if (!err)
- err = -1;
-
- return err;
-}
-
-err_t dbfs_check_result (const struct evsql_result_info *res, size_t rows, size_t cols) {
- err_t err;
-
- // number of rows returned/affected
- size_t 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");
-
- // duplicate rows where one expected?
- if (rows && nrows != rows)
- XERROR(err = EIO, "wrong number of rows: %zu -> %zu", rows, nrows);
-
- // correct number of columns
- if (evsql_result_cols(res) != cols)
- XERROR(err = EIO, "wrong number of columns: %zu -> %zu", cols, evsql_result_cols(res));
-
- // good
- return 0;
-
-error:
- return err;
-}
-
-int _dbfs_stat_info (struct stat *st, const struct evsql_result_info *res, size_t row, size_t col_offset) {
- int err = 0;
-
- uint16_t mode;
- uint32_t size = 0; // NULL for non-REG inodes
- uint64_t nlink = 0; // will be NULL for non-GROUP BY queries
- const char *type;
-
- // extract the data
- if ((0
- || evsql_result_string(res, row, col_offset + 0, &type, 0 ) // inodes.type
- || evsql_result_uint16(res, row, col_offset + 1, &mode, 0 ) // inodes.mode
- || evsql_result_uint32(res, row, col_offset + 2, &size, 1 ) // size
- || evsql_result_uint64(res, row, col_offset + 3, &nlink, 1 ) // count(*)
- ) && (err = EIO))
- ERROR("invalid db data");
-
- INFO("\tst_mode=S_IF%s | %ho, st_nlink=%llu, st_size=%llu", type, mode, (long long unsigned int) nlink, (long long unsigned int) size);
-
- // convert and store
- st->st_mode = _dbfs_mode(type) | mode;
- st->st_nlink = nlink;
- st->st_size = size;
-
- // good
- return 0;
-
-error:
- return err;
-}
-
-
-
--- a/src/dbfs/dbfs.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,100 +0,0 @@
-
-#include <stdlib.h>
-
-#include "dbfs.h"
-#include "../dbfs.h"
-#include "../lib/log.h"
-
-static struct fuse_lowlevel_ops dbfs_llops = {
- .init = dbfs_init,
- .destroy = dbfs_destroy,
- .lookup = dbfs_lookup,
- // .forget // not needed
- .getattr = dbfs_getattr,
- .setattr = dbfs_setattr,
- .readlink = dbfs_readlink,
- .mknod = dbfs_mknod,
- .mkdir = dbfs_mkdir,
- .unlink = dbfs_unlink,
- .rmdir = dbfs_unlink, // this behaves just the same
- .symlink = dbfs_symlink,
- .rename = dbfs_rename,
- .link = dbfs_link,
- .open = dbfs_open,
- .read = dbfs_read,
- .write = dbfs_write,
- .flush = dbfs_flush,
- .release = dbfs_release,
- // .fsync // not needed
- .opendir = dbfs_opendir,
- .readdir = dbfs_readdir,
- .releasedir = dbfs_releasedir,
-};
-
-void dbfs_init (void *userdata, struct fuse_conn_info *conn) {
- INFO("[dbfs.init] userdata=%p, conn=%p", userdata, conn);
-
-}
-
-void dbfs_destroy (void *arg) {
- struct dbfs *ctx = arg;
- INFO("[dbfs.destroy %p]", ctx);
-
- // exit libevent
- event_base_loopexit(ctx->ev_base, NULL);
-}
-
-
-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);
-}
-
-struct dbfs *dbfs_new (struct event_base *ev_base, struct fuse_args *args, const char *db_conninfo) {
- struct dbfs *ctx = NULL;
-
- // alloc ctx
- if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
- ERROR("calloc");
-
- ctx->ev_base = ev_base;
- ctx->db_conninfo = db_conninfo;
-
- // 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, args, &dbfs_llops, ctx)) == NULL)
- ERROR("evfuse_new");
-
- // success
- return ctx;
-
-error:
- if (ctx)
- dbfs_free(ctx);
-
- return NULL;
-}
-
-void dbfs_free (struct dbfs *ctx) {
- // cleanup
- if (ctx->ev_fuse) {
- evfuse_free(ctx->ev_fuse);
-
- ctx->ev_fuse = NULL;
- }
-
- if (ctx->db) {
- // XXX: not yet implemented
- // evsql_close(ctx->db);
- // ctx->db = NULL;
- }
-
- free(ctx);
-}
--- a/src/dbfs/dbfs.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-#ifndef DBFS_DBFS_H
-#define DBFS_DBFS_H
-
-#include <sys/stat.h>
-#include <errno.h>
-
-#include <event2/event.h>
-
-#include "ops.h"
-#include "../evfuse.h"
-#include "../evsql.h"
-#include "../lib/error.h"
-
-/*
- * Structs and functions shared between all dbfs components
- */
-
-struct dbfs {
- struct event_base *ev_base;
-
- const char *db_conninfo;
- struct evsql *db;
-
- struct evfuse *ev_fuse;
-};
-
-// XXX: not sure how this should work
-#define CACHE_TIMEOUT 1.0
-
-// columns used for stat_info
-#define DBFS_STAT_COLS " inodes.type, inodes.mode, dbfs_size(inodes.type, inodes.data, inodes.link_path), (SELECT COUNT(*) FROM inodes i LEFT JOIN file_tree ft ON (i.ino = ft.ino) WHERE i.ino = inodes.ino) AS nlink"
-
-/*
- * Convert the CHAR(4) inodes.type from SQL into a mode_t.
- *
- * Returns zero for unknown types.
- */
-mode_t _dbfs_mode (const char *type);
-
-/*
- * Check that the number of rows and columns in the result set matches what we expect.
- *
- * If rows is nonzero, there must be exactly that many rows (mostly useful for rows=1).
- * The number of columns must always be given, and match.
- *
- * Returns;
- * -1 if the query failed, the columns/rows do not match
- * 0 the results match
- * 1 there were no results (zero rows)
- */
-int _dbfs_check_res (const struct evsql_result_info *res, size_t rows, size_t cols);
-
-/*
- * Same as _dbfs_check_res, but returns ENOENT/EIO directly
- */
-err_t dbfs_check_result (const struct evsql_result_info *res, size_t rows, size_t cols);
-
-/*
- * Fill a `struct state` with info retrieved from a SQL query.
- *
- * The result must contain four columns, starting at the given offset:
- * inodes.type, inodes.mode, inodes.size, count(*) AS nlink
- *
- * Note that this does not fill the st_ino field
- */
-int _dbfs_stat_info (struct stat *st, const struct evsql_result_info *res, size_t row, size_t col_offset);
-
-/** interrupt.c
- *
- * Fuse interrupts are handled using fuse_req_interrupt_func. Calling this registers a callback function with the req,
- * which may or may not be called either by fuse_req_interrupt_func, or later on via evfuse's event handler. It is
- * assumed that this will never be called after a call to fuse_reply_*.
- *
- * Hence, to handle an interrupt, we must first ensure that fuse_reply_* will not be called afterwards (it'll return
- * an error), and then we must call fuse_reply_err(req, EINTR).
- *
- * In the simplest case, we can simply submit a query, and then abort it once the req is interrupted (now or later).
- * In the more complicated case, we can check if the request was interrupted, if not, do the query and handle
- * interrupts.
- */
-
-/*
- * Useable as a callback to fuse_req_interrupt_func, will abort the given query and err the req.
- *
- * Due to a locking bug in libfuse 2.7.4, this will actually delay the fuse_req_err until the next event-loop iteration.
- */
-void dbfs_interrupt_query (struct fuse_req *req, void *query_ptr);
-
-/*
- * XXX: More complicated state, is this actually needed?
- */
-struct dbfs_interrupt_ctx {
- struct fuse_req *req;
- struct evsql_query *query;
-
- int interrupted : 1;
-};
-
-/*
- * Register as a fuse interrupt function for simple requests that only run one query without allocating any resources.
- *
- * This will abort the query if the interrupt is run, causing it's callback to not be called.
- *
- * Returns nonzero if the request was already interrupted, zero otherwise. Be careful that the interrupt does not get
- * fired between you checking for it and setting query.
- */
-int dbfs_interrupt_register (struct fuse_req *req, struct dbfs_interrupt_ctx *ctx);
-
-#endif /* DBFS_DBFS_H */
--- a/src/dbfs/dirop.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,303 +0,0 @@
-
-#include <stdlib.h>
-#include <assert.h>
-
-#include "dbfs.h"
-#include "op_base.h"
-#include "../dirbuf.h"
-#include "../lib/log.h"
-
-/*
- * Directory related functionality like opendir, readdir, releasedir
- */
-struct dbfs_dirop {
- struct dbfs_op base;
-
- // parent dir inodes
- uint32_t parent;
-
- // for readdir
- struct dirbuf dirbuf;
-};
-
-/*
- * Release the dirbuf.
- */
-static void _dbfs_dirop_free (struct dbfs_op *op_base) {
- struct dbfs_dirop *dirop = (struct dbfs_dirop *) op_base;
-
- // just release the dirbuf
- dirbuf_release(&dirop->dirbuf);
-}
-
-/*
- * Handle the results for the initial attribute lookup for the dir itself during opendir ops.
- */
-static void dbfs_opendir_res (const struct evsql_result_info *res, void *arg) {
- struct dbfs_dirop *dirop = arg;
- int err;
-
- assert(dirop->base.req);
- assert(dirop->base.trans); // query callbacks don't get called if the trans fails
- assert(!dirop->base.open);
-
- // check the results
- if ((err = _dbfs_check_res(res, 1, 2)))
- SERROR(err = (err == 1 ? ENOENT : EIO));
-
- const char *type;
-
- // extract the data
- if (0
- || evsql_result_uint32(res, 0, 0, &dirop->parent, 1 ) // file_tree.parent
- || evsql_result_string(res, 0, 1, &type, 0 ) // inodes.type
- )
- SERROR(err = EIO);
-
- // is it a dir?
- if (_dbfs_mode(type) != S_IFDIR)
- EERROR(err = ENOTDIR, "wrong type: %s", type);
-
- INFO("\t[dbfs.opendir %p:%p] -> ino=%lu, parent=%lu, type=%s", dirop, dirop->base.req, (unsigned long int) dirop->base.ino, (unsigned long int) dirop->parent, type);
-
- // open_fn done, do the open_reply
- if ((err = dbfs_op_open_reply(&dirop->base)))
- goto error;
-
- // success, fallthrough for evsql_result_free
- err = 0;
-
-error:
- if (err)
- // fail it
- dbfs_op_fail(&dirop->base, err);
-
- // free
- evsql_result_free(res);
-}
-
-/*
- * The opendir transaction is ready for use. Query for the given dir's info
- */
-static void dbfs_dirop_open (struct dbfs_op *op_base) {
- struct dbfs_dirop *dirop = (struct dbfs_dirop *) op_base;
- struct dbfs *ctx = fuse_req_userdata(dirop->base.req);
- int err;
-
- assert(dirop->base.trans);
- assert(dirop->base.req);
- assert(!dirop->base.open);
-
- INFO("\t[dbfs.opendir %p:%p] -> trans=%p", dirop, dirop->base.req, dirop->base.trans);
-
- // first fetch info about the dir itself
- const char *sql =
- "SELECT"
- " file_tree.parent, inodes.type"
- " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.ino = inodes.ino)"
- " WHERE file_tree.ino = $1::int4";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT32 ),
-
- EVSQL_PARAMS_END
- };
-
- // build params
- if (0
- || evsql_param_uint32(¶ms, 0, dirop->base.ino)
- )
- SERROR(err = EIO);
-
- // query
- if (evsql_query_params(ctx->db, dirop->base.trans, sql, ¶ms, dbfs_opendir_res, dirop) == NULL)
- SERROR(err = EIO);
-
- // ok, wait for the info results
- return;
-
-error:
- // fail it
- dbfs_op_fail(&dirop->base, err);
-}
-
-/*
- * Handle opendir(), this means starting a new op.
- */
-void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct dbfs_dirop *dirop = NULL;
- int err;
-
- // allocate it
- if ((dirop = calloc(1, sizeof(*dirop))) == NULL && (err = EIO))
- ERROR("calloc");
-
- // do the op_open
- if ((err = dbfs_op_open(ctx, &dirop->base, req, ino, fi, _dbfs_dirop_free, dbfs_dirop_open)))
- ERROR("dbfs_op_open");
-
- INFO("[dbfs.opendir %p:%p] ino=%lu, fi=%p", dirop, req, ino, fi);
-
- // wait
- return;
-
-error:
- if (dirop) {
- // we can fail normally
- dbfs_op_fail(&dirop->base, err);
-
- } else {
- // must error out manually as we couldn't alloc the context
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
- }
-}
-
-/*
- * Got the list of files for our readdir() request.
- *
- * Fill up the dirbuf, and then send the reply.
- *
- */
-static void dbfs_readdir_res (const struct evsql_result_info *res, void *arg) {
- struct dbfs_dirop *dirop = arg;
- int err;
- size_t row;
-
- assert(dirop->base.req);
- assert(dirop->base.trans); // query callbacks don't get called if the trans fails
- assert(dirop->base.open);
-
- // check the results
- if ((err = _dbfs_check_res(res, 0, 4)) < 0)
- SERROR(err = EIO);
-
- INFO("\t[dbfs.readdir %p:%p] -> files: res_rows=%zu", dirop, dirop->base.req, evsql_result_rows(res));
-
- // iterate over the rows
- for (row = 0; row < evsql_result_rows(res); row++) {
- uint32_t off, ino;
- const char *name, *type;
-
- // extract the data
- if (0
- || evsql_result_uint32(res, row, 0, &off, 0 ) // file_tree.offset
- || evsql_result_string(res, row, 1, &name, 0 ) // file_tree.name
- || evsql_result_uint32(res, row, 2, &ino, 0 ) // inodes.ino
- || evsql_result_string(res, row, 3, &type, 0 ) // inodes.type
- )
- SERROR(err = EIO);
-
- INFO("\t%zu: off=%lu+2, name=%s, ino=%lu, type=%s", row, (long unsigned int) off, name, (long unsigned int) ino, type);
-
- // add to the dirbuf
- // offsets are just offset + 2
- if ((err = dirbuf_add(dirop->base.req, &dirop->dirbuf, off + 2, off + 3, name, ino, _dbfs_mode(type))) < 0 && (err = EIO))
- ERROR("failed to add dirent for inode=%lu", (long unsigned int) ino);
-
- // stop if it's full
- if (err > 0)
- break;
- }
-
- // send it
- if ((err = dirbuf_done(dirop->base.req, &dirop->dirbuf)))
- EERROR(err, "failed to send buf");
-
- // handled the req
- if ((err = dbfs_op_req_done(&dirop->base)))
- goto error;
-
- // good, fallthrough
- err = 0;
-
-error:
- if (err)
- dbfs_op_fail(&dirop->base, err);
-
- // free
- evsql_result_free(res);
-}
-
-/*
- * Handle a readdir request. This will execute a SQL query inside the transaction to get the files at the given offset,
- * and dbfs_readdir_res will handle the results.
- *
- * If trans failed earlier, detect that and return an error.
- */
-void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct dbfs_dirop *dirop;
- int err;
-
- // get the op
- if ((dirop = (struct dbfs_dirop *) dbfs_op_req(req, ino, fi)) == NULL)
- return;
-
- INFO("[dbfs.readdir %p:%p] ino=%lu, size=%zu, off=%zu, fi=%p : trans=%p", dirop, req, ino, size, off, fi, dirop->base.trans);
-
- // create the dirbuf
- if (dirbuf_init(&dirop->dirbuf, size, off))
- SERROR(err = EIO);
-
- // add . and ..
- // we set the next offset to 2, because all dirent offsets will be larger than that
- // assume that these two should *always* fit
- if ((err = (0
- || dirbuf_add(req, &dirop->dirbuf, 0, 1, ".", dirop->base.ino, S_IFDIR )
- || dirbuf_add(req, &dirop->dirbuf, 1, 2, "..",
- dirop->parent ? dirop->parent : dirop->base.ino, S_IFDIR )
- )) && (err = EIO))
- ERROR("failed to add . and .. dirents");
-
- // select all relevant file entries
- const char *sql =
- "SELECT"
- " file_tree.\"offset\", file_tree.name, inodes.ino, inodes.type"
- " FROM file_tree LEFT OUTER JOIN inodes ON (file_tree.ino = inodes.ino)"
- " WHERE file_tree.parent = $1::int4 AND file_tree.\"offset\" >= $2::int4"
- " ORDER BY file_tree.\"offset\""
- " LIMIT $3::int4";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT32 ),
- EVSQL_PARAM ( UINT32 ),
- EVSQL_PARAM ( UINT32 ),
-
- EVSQL_PARAMS_END
- };
-
- // adjust offset to take . and .. into account
- if (off > 2)
- off -= 2;
-
- // build params
- if (0
- || evsql_param_uint32(¶ms, 0, ino)
- || evsql_param_uint32(¶ms, 1, off)
- || evsql_param_uint32(¶ms, 2, dirbuf_estimate(&dirop->dirbuf, 0))
- )
- SERROR(err = EIO);
-
- // query
- if (evsql_query_params(ctx->db, dirop->base.trans, sql, ¶ms, dbfs_readdir_res, dirop) == NULL)
- SERROR(err = EIO);
-
- // good, wait
- return;
-
-error:
- dbfs_op_fail(&dirop->base, err);
-}
-
-/*
- * "For every [succesfull] opendir call there will be exactly one releasedir call."
- *
- * The dirop may be in a failed state.
- */
-void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
- // just passthrough to dbfs_op
- dbfs_op_release(req, ino, fi);
-}
-
--- a/src/dbfs/fileop.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,310 +0,0 @@
-#include <stdlib.h>
-#include <assert.h>
-
-#include <postgresql/libpq/libpq-fs.h>
-
-#include "dbfs.h"
-#include "op_base.h"
-#include "../lib/log.h"
-
-/*
- * A file-operation, i.e. a sequence consisting of an OPEN, a multitude of READ/WRITE, followed by zero or more FLUSHes, and finally a single RELEASE.
- *
- * For historical reasons this opens a transaction and keeps it between open/release, but reads/writes now use the oid directly and are transactionless.
- */
-
-struct dbfs_fileop {
- struct dbfs_op base;
-
- uint32_t oid;
-// uint32_t lo_fd;
-};
-
-static void _dbfs_fileop_free (struct dbfs_op *op_base) {
- struct dbfs_fileop *fop = (struct dbfs_fileop *) op_base;
-
- /* no-op */
- (void) fop;
-}
-
-static void dbfs_open_res (const struct evsql_result_info *res, void *arg) {
- struct dbfs_fileop *fop = arg;
- int err;
-
- // check the results
- if ((err = _dbfs_check_res(res, 1, 2)))
- SERROR(err = (err == 1 ? ENOENT : EIO));
-
- const char *type;
-
- // extract the data
- if (0
- || evsql_result_string(res, 0, 0, &type, 0 ) // inodes.type
- || evsql_result_uint32(res, 0, 1, &fop->oid, 0 ) // inodes.data
- )
- SERROR(err = EIO);
-
- // is it a dir?
- if (_dbfs_mode(type) != S_IFREG)
- EERROR(err = EINVAL, "wrong type: %s", type);
-
- INFO("\t[dbfs.open %p:%p] -> ino=%lu, type=%s", fop, fop->base.req, (unsigned long int) fop->base.ino, type);
-
- // open_fn done, do the open_reply
- if ((err = dbfs_op_open_reply(&fop->base)))
- goto error;
-
- // success, fallthrough for evsql_result_free
- err = 0;
-
-error:
- if (err)
- // fail it
- dbfs_op_fail(&fop->base, err);
-
- // free
- evsql_result_free(res);
-}
-
-static void dbfs_fileop_open (struct dbfs_op *op_base) {
- struct dbfs_fileop *fop = (struct dbfs_fileop *) op_base;
- struct dbfs *ctx = fuse_req_userdata(fop->base.req);
- int err;
-
- // make sure the file actually exists
- const char *sql =
- "SELECT"
- " inodes.type, inodes.data"
- " FROM inodes"
- " WHERE inodes.ino = $1::int4";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT32 ),
-
- EVSQL_PARAMS_END
- };
-
- // build params
- if (0
- || evsql_param_uint32(¶ms, 0, fop->base.ino)
- )
- SERROR(err = EIO);
-
- // query
- if (evsql_query_params(ctx->db, fop->base.trans, sql, ¶ms, dbfs_open_res, fop) == NULL)
- SERROR(err = EIO);
-
- // ok, wait for the info results
- return;
-
-error:
- // fail it
- dbfs_op_fail(&fop->base, err);
-}
-
-void dbfs_open (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct dbfs_fileop *fop = NULL;
- int err;
-
- // allocate it
- if ((fop = calloc(1, sizeof(*fop))) == NULL && (err = EIO))
- ERROR("calloc");
-
- // do the op_open
- if ((err = dbfs_op_open(ctx, &fop->base, req, ino, fi, _dbfs_fileop_free, dbfs_fileop_open)))
- ERROR("dbfs_op_open");
-
- // log
- INFO("[dbfs.open %p:%p] ino=%lu, fi->flags=%04X", fop, req, ino, fi->flags);
-
- // wait
- return;
-
-error:
- if (fop) {
- // we can fail normally
- dbfs_op_fail(&fop->base, err);
-
- } else {
- // must error out manually as we couldn't alloc the context
- if ((err = -fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
- }
-}
-
-void dbfs_read_res (const struct evsql_result_info *res, void *arg) {
- struct fuse_req *req = arg;
- int err;
- const char *buf;
- size_t size;
-
- // check the results
- if ((err = _dbfs_check_res(res, 1, 1)) < 0)
- SERROR(err = EIO);
-
- // get the data
- if (evsql_result_binary(res, 0, 0, &buf, &size, 0))
- SERROR(err = EIO);
-
- INFO("\t[dbfs.read %p] -> size=%zu", req, size);
-
- // send it
- if ((err = -fuse_reply_buf(req, buf, size)))
- EERROR(err, "fuse_reply_buf");
-
- // good, fallthrough
- err = 0;
-
-error:
- if (err)
- fuse_reply_err(req, err);
-
-
- // free
- evsql_result_free(res);
-}
-
-void dbfs_read (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
- struct dbfs *ctx = fuse_req_userdata(req);
- int err;
-
- // log
- INFO("[dbfs.read %p] ino=%lu, size=%zu, off=%lu, fi->flags=%04X", req, ino, size, off, fi->flags);
-
- // query
- const char *sql =
- "SELECT"
- " lo_pread_oid(data, $1::int4, $2::int4)"
- " FROM inodes"
- " WHERE ino = $3::int4";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT32 ), // len
- EVSQL_PARAM ( UINT32 ), // off
- EVSQL_PARAM ( UINT32 ), // ino
-
- EVSQL_PARAMS_END
- };
-
- // build params
- if (0
- || evsql_param_uint32(¶ms, 0, size)
- || evsql_param_uint32(¶ms, 1, off)
- || evsql_param_uint32(¶ms, 2, ino)
- )
- SERROR(err = EIO);
-
- // query, transactionless
- if (evsql_query_params(ctx->db, NULL, sql, ¶ms, dbfs_read_res, req) == NULL)
- SERROR(err = EIO);
-
- // ok, wait for the info results
- return;
-
-error:
- fuse_reply_err(req, err);
-}
-
-void dbfs_write_res (const struct evsql_result_info *res, void *arg) {
- struct fuse_req *req = arg;
- int err;
- uint32_t size;
-
- // check the results
- if ((err = _dbfs_check_res(res, 1, 1)) < 0)
- SERROR(err = EIO);
-
- // get the size
- if (evsql_result_uint32(res, 0, 0, &size, 0))
- SERROR(err = EIO);
-
- INFO("\t[dbfs.write %p] -> size=%lu", req, (long unsigned int) size);
-
- // send it
- if ((err = -fuse_reply_write(req, size)))
- EERROR(err, "fuse_reply_write");
-
- // good, fallthrough
- err = 0;
-
-error:
- if (err)
- fuse_reply_err(req, err);
-
- // free
- evsql_result_free(res);
-}
-
-void dbfs_write (struct fuse_req *req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi) {
- struct dbfs *ctx = fuse_req_userdata(req);
- int err;
-
- // log
- INFO("[dbfs.write %p] ino=%lu, size=%zu, off=%lu, fi->flags=%04X", req, ino, size, off, fi->flags);
-
- // query
- const char *sql =
- "SELECT"
- " lo_pwrite_oid(data, $1::bytea, $2::int4)"
- " FROM inodes"
- " WHERE ino = $3::int4";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( BINARY ), // buf
- EVSQL_PARAM ( UINT32 ), // off
- EVSQL_PARAM ( UINT32 ), // oid
-
- EVSQL_PARAMS_END
- };
-
- // build params
- if (0
- || evsql_param_binary(¶ms, 0, buf, size)
- || evsql_param_uint32(¶ms, 1, off)
- || evsql_param_uint32(¶ms, 2, ino)
- )
- SERROR(err = EIO);
-
- // query
- if (evsql_query_params(ctx->db, NULL, sql, ¶ms, dbfs_write_res, req) == NULL)
- SERROR(err = EIO);
-
- // ok, wait for the info results
- return;
-
-error:
- fuse_reply_err(req, err);
-}
-
-void dbfs_flush (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
- struct dbfs_fileop *fop;
- int err;
-
- // get the fop
- if ((fop = (struct dbfs_fileop *) dbfs_op_req(req, ino, fi)) == NULL)
- return;
-
- // log
- INFO("[dbfs.flush %p:%p] ino=%lu", fop, req, ino);
-
- // and reply...
- if ((err = -fuse_reply_err(req, 0)))
- EWARNING(err, "fuse_reply_err");
-
- // done
- if ((err = dbfs_op_req_done(&fop->base)))
- goto error;
-
- // good
- return;
-
-error:
- dbfs_op_fail(&fop->base, err);
-}
-
-void dbfs_release (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
- // just passthrough to dbfs_op
- // the lo_fd will be closed automatically
- dbfs_op_release(req, ino, fi);
-}
--- a/src/dbfs/interrupt.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,53 +0,0 @@
-
-#include "dbfs.h"
-
-void _dbfs_interrupt_reply (evutil_socket_t _unused1, short _unused2, void *req_ptr) {
- struct fuse_req *req = req_ptr;
- err_t err;
-
- // error the req
- if ((err = -fuse_reply_err(req, EINTR)))
- EWARNING(err, "fuse_reply_err");
-}
-
-void dbfs_interrupt_query (struct fuse_req *req, void *query_ptr) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct evsql_query *query = query_ptr;
- struct timeval tv;
- err_t err;
-
- // abort query
- evsql_query_abort(NULL, query);
-
- /*
- * 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) {
- // dereference ctx
- struct dbfs_interrupt_ctx *ctx = ctx_ptr;
-
- // just cancel query if pending
- if (ctx->query) {
- evsql_query_abort(NULL, ctx->query);
- ctx->query = NULL;
- }
-
- // mark as interrupted
- ctx->interrupted = 1;
-}
-
-int dbfs_interrupt_register (struct fuse_req *req, struct dbfs_interrupt_ctx *ctx) {
- // initialize
- ctx->query = NULL;
- ctx->interrupted = 0;
-
- // just pass over to fuse_req_interrupt_func
- fuse_req_interrupt_func(req, _dbfs_interrupt_ctx, ctx);
-}
--- a/src/dbfs/link.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,279 +0,0 @@
-#include "dbfs.h"
-
-/*
- * Handling simple ino-related ops, like lookup, readlink, unlink and link
- */
-
-#include "../lib/log.h"
-#include "../lib/misc.h"
-
-/*
- * Used for lookup and link
- */
-void dbfs_entry_res (const struct evsql_result_info *res, void *arg) {
- struct fuse_req *req = arg;
- struct fuse_entry_param e; ZINIT(e);
- int err = 0;
-
- uint32_t ino;
-
- // check the results
- if ((err = _dbfs_check_res(res, 1, 5)))
- SERROR(err = (err == 1 ? ENOENT : EIO));
-
- // get the data
- if (0
- || evsql_result_uint32(res, 0, 0, &ino, 0 ) // inodes.ino
- )
- EERROR(err = EIO, "invalid db data");
-
- INFO("\t[dbfs.lookup] -> ino=%u", ino);
-
- // stat attrs
- if ((err = _dbfs_stat_info(&e.attr, res, 0, 1)))
- goto error;
-
- // other attrs
- e.ino = e.attr.st_ino = ino;
- 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);
- struct evsql_query *query;
- int err;
-
- INFO("[dbfs.lookup] parent=%lu name=%s", parent, name);
-
- // query and params
- const char *sql =
- "SELECT"
- " inodes.ino, " DBFS_STAT_COLS
- " FROM file_tree INNER JOIN inodes ON (file_tree.ino = inodes.ino)"
- " WHERE file_tree.parent = $1::int4 AND file_tree.name = $2::varchar";
-
- 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 ((query = evsql_query_params(ctx->db, NULL, sql, ¶ms, dbfs_entry_res, req)) == NULL)
- EERROR(err = EIO, "evsql_query_params");
-
- // handle interrupts
- fuse_req_interrupt_func(req, dbfs_interrupt_query, query);
-
- // wait
- return;
-
-error:
- if ((err = -fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
-
-void _dbfs_readlink_res (const struct evsql_result_info *res, void *arg) {
- struct fuse_req *req = arg;
- int err = 0;
-
- uint32_t ino;
- const char *type, *link;
-
- // check the results
- if ((err = _dbfs_check_res(res, 1, 3)))
- SERROR(err = (err == 1 ? ENOENT : EIO));
-
- // get our 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_string(res, 0, 2, &link, 1 ) // inodes.link_path
- )
- EERROR(err = EIO, "invalid db data");
-
- // is it a symlink?
- if (_dbfs_mode(type) != S_IFLNK)
- EERROR(err = EINVAL, "wrong type: %s", type);
-
- INFO("\t[dbfs.readlink %p] -> ino=%lu, type=%s, link=%s", req, (unsigned long int) ino, type, link);
-
- // reply
- if ((err = -fuse_reply_readlink(req, link)))
- EERROR(err, "fuse_reply_readlink");
-
-error:
- if (err && (err = -fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-
- // free
- evsql_result_free(res);
-}
-
-void dbfs_readlink (struct fuse_req *req, fuse_ino_t ino) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct evsql_query *query;
- int err;
-
- INFO("[dbfs.readlink %p] ino=%lu", req, ino);
-
- const char *sql =
- "SELECT"
- " inodes.ino, inodes.type, inodes.link_path"
- " FROM inodes"
- " WHERE inodes.ino = $1::int4";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT32 ),
-
- EVSQL_PARAMS_END
- };
-
- // build params
- if (0
- || evsql_param_uint32(¶ms, 0, ino)
- )
- SERROR(err = EIO);
-
- // query
- if ((query = evsql_query_params(ctx->db, NULL, sql, ¶ms, _dbfs_readlink_res, req)) == NULL)
- SERROR(err = EIO);
-
- // handle interrupts
- fuse_req_interrupt_func(req, dbfs_interrupt_query, query);
-
- // wait
- return;
-
-error:
- if ((err = -fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-
-}
-
-#define SETERR(err_var, err_val, bool_val) ((err_var) = bool_val ? (err_val) : 0)
-
-void dbfs_unlink_res (const struct evsql_result_info *res, void *arg) {
- struct fuse_req *req = arg;
- int err = 0;
-
- // check the results
- // XXX: reply with ENOTEMPTY if it fails due to this inode being a dir
- if ((err = dbfs_check_result(res, 1, 0)))
- goto error;
-
- INFO("\t[dbfs.unlink %p] -> OK", req);
-
- // reply
- if ((err = -fuse_reply_err(req, 0)))
- EERROR(err, "fuse_reply_err");
-
-error:
- if (err && (err = -fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-
- // free
- evsql_result_free(res);
-}
-
-void dbfs_unlink (struct fuse_req *req, fuse_ino_t parent, const char *name) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct evsql_query *query;
- int err;
-
- INFO("[dbfs.unlink %p] parent=%lu, name=%s", req, parent, name);
-
- const char *sql =
- "DELETE"
- " FROM file_tree"
- " WHERE parent = $1::int4 AND name = $2::varchar";
-
- 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)
- )
- SERROR(err = EIO);
-
- // query
- if ((query = evsql_query_params(ctx->db, NULL, sql, ¶ms, dbfs_unlink_res, req)) == NULL)
- SERROR(err = EIO);
-
- // handle interrupts
- fuse_req_interrupt_func(req, dbfs_interrupt_query, query);
-
- // wait
- return;
-
-error:
- if ((err = -fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
-
-void dbfs_link (struct fuse_req *req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname) {
- struct dbfs *ctx = fuse_req_userdata(req);
- struct evsql_query *query;
- int err;
-
- INFO("[dbfs.link %p] ino=%lu, newparent=%lu, newname=%s", req, ino, newparent, newname);
-
- const char *sql =
- "SELECT ino, type, mode, size, nlink FROM dbfs_link($1::int4, $2::int4, $3::varchar)";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT32 ),
- EVSQL_PARAM ( UINT32 ),
- EVSQL_PARAM ( STRING ),
-
- EVSQL_PARAMS_END
- };
-
- // build params
- if (0
- || evsql_param_uint32(¶ms, 0, ino)
- || evsql_param_uint32(¶ms, 1, newparent)
- || evsql_param_string(¶ms, 2, newname)
- )
- SERROR(err = EIO);
-
- // query
- if ((query = evsql_query_params(ctx->db, NULL, sql, ¶ms, dbfs_entry_res, req)) == NULL)
- SERROR(err = EIO);
-
- // handle interrupts
- fuse_req_interrupt_func(req, dbfs_interrupt_query, query);
-
- // wait
- return;
-
-error:
- if ((err = -fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
--- a/src/dbfs/mk.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,243 +0,0 @@
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#include "trans.h"
-#include "../lib/log.h"
-
-struct dbfs_mk_ctx {
- struct dbfs_trans base;
-
- const char *type, *data_expr;
- char *link, *name;
- uint16_t mode;
- uint32_t ino, parent;
-
- unsigned char is_dir : 1;
-};
-
-// default mode for symlinks
-#define DBFS_SYMLINK_MODE 0777
-
-// max. size for an dbfs_mk INSERT query
-#define DBFS_MK_SQL_MAX 512
-
-void dbfs_mk_free (struct dbfs_trans *ctx_base) {
- struct dbfs_mk_ctx *ctx = (struct dbfs_mk_ctx *) ctx_base;
-
- free(ctx->link);
- free(ctx->name);
-}
-
-void dbfs_mk_commit (struct dbfs_trans *ctx_base) {
- struct dbfs_mk_ctx *ctx = (struct dbfs_mk_ctx *) ctx_base;
- struct fuse_entry_param e;
- int err;
-
- // build entry
- e.ino = e.attr.st_ino = ctx->ino;
- e.attr.st_mode = _dbfs_mode(ctx->type) | ctx->mode;
- e.attr.st_size = ctx->link ? strlen(ctx->link) : 0;
- e.attr.st_nlink = 1;
- e.attr_timeout = e.entry_timeout = CACHE_TIMEOUT;
-
- // reply
- if ((err = fuse_reply_entry(ctx_base->req, &e)))
- goto error;
-
- // req good
- ctx_base->req = NULL;
-
- // free
- dbfs_trans_free(ctx_base);
-
- // return
- return;
-
-error:
- dbfs_trans_fail(ctx_base, err);
-}
-
-void dbfs_mk_filetree (const struct evsql_result_info *res, void *arg) {
- struct dbfs_mk_ctx *ctx = arg;
- int err = EIO;
-
- // check results
- if (_dbfs_check_res(res, 0, 0) < 0)
- goto error;
-
- // commit
- dbfs_trans_commit(&ctx->base);
-
- // fallthrough for result_free
- err = 0;
-
-error:
- if (err)
- dbfs_trans_fail(&ctx->base, err);
-
- evsql_result_free(res);
-}
-
-void dbfs_mk_inode (const struct evsql_result_info *res, void *arg) {
- struct dbfs_mk_ctx *ctx = arg;
- struct dbfs *dbfs_ctx = fuse_req_userdata(ctx->base.req);
- int err = EIO;
-
- // check result
- if ((err = _dbfs_check_res(res, 1, 1)))
- SERROR(err = err > 0 ? ENOENT : EIO);
-
- // get ino
- if (evsql_result_uint32(res, 0, 0, &ctx->ino, 0))
- goto error;
-
- // insert file_tree entry
- const char *sql =
- "INSERT"
- " INTO file_tree (name, parent, ino, ino_dir)"
- " VALUES ($1::varchar, $2::int4, $3::int4, $4::int4)";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( STRING ),
- EVSQL_PARAM ( UINT32 ),
- EVSQL_PARAM ( UINT32 ),
- EVSQL_PARAM ( UINT32 ),
-
- EVSQL_PARAMS_END
- };
-
- if (0
- || evsql_param_string(¶ms, 0, ctx->name)
- || evsql_param_uint32(¶ms, 1, ctx->parent)
- || evsql_param_uint32(¶ms, 2, ctx->ino)
- || ctx->is_dir ? evsql_param_uint32(¶ms, 3, ctx->ino) : evsql_param_null(¶ms, 3)
- )
- goto error;
-
- // query
- if (evsql_query_params(dbfs_ctx->db, ctx->base.trans, sql, ¶ms, dbfs_mk_filetree, ctx))
- goto error;
-
- // fallthrough for result_free
- err = 0;
-
-error:
- if (err)
- dbfs_trans_fail(&ctx->base, err);
-
- evsql_result_free(res);
-}
-
-void dbfs_mk_begin (struct dbfs_trans *ctx_base) {
- struct dbfs_mk_ctx *ctx = (struct dbfs_mk_ctx *) ctx_base;
- struct dbfs *dbfs_ctx = fuse_req_userdata(ctx_base->req);
- int ret;
-
- // insert inode
- char sql_buf[DBFS_MK_SQL_MAX];
-
- if ((ret = snprintf(sql_buf, DBFS_MK_SQL_MAX,
- "INSERT"
- " INTO inodes (type, mode, data, link_path)"
- " VALUES ($1::char(3), $2::int2, %s, $3::varchar)"
- " RETURNING inodes.ino", ctx->data_expr ? ctx->data_expr : "NULL"
- )) >= DBFS_MK_SQL_MAX)
- ERROR("sql_buf is too small: %d", ret);
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( STRING ),
- EVSQL_PARAM ( UINT16 ),
- EVSQL_PARAM ( STRING ),
-
- EVSQL_PARAMS_END
- };
-
- if (0
- || evsql_param_string(¶ms, 0, ctx->type)
- || evsql_param_uint16(¶ms, 1, ctx->mode)
- || evsql_param_string(¶ms, 2, ctx->link)
- )
- goto error;
-
- if (evsql_query_params(dbfs_ctx->db, ctx_base->trans, sql_buf, ¶ms, dbfs_mk_inode, ctx) == NULL)
- goto error;
-
- return;
-
-error:
- dbfs_trans_fail(ctx_base, EIO);
-}
-
-/*
- * It is assumed that name and link_path must be copied, but type remains useable
- */
-void dbfs_mk (struct fuse_req *req, fuse_ino_t parent, const char *name, const char *type, uint16_t mode, const char *data_expr, const char *link, unsigned char is_dir) {
- struct dbfs_mk_ctx *ctx = NULL;
-
- // alloc
- if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
- ERROR("calloc");
-
- // start trans
- if (dbfs_trans_init(&ctx->base, req))
- goto error;
-
- // callbacks
- ctx->base.free_fn = dbfs_mk_free;
- ctx->base.begin_fn = dbfs_mk_begin;
- ctx->base.commit_fn = dbfs_mk_commit;
-
- // state
- ctx->ino = 0;
- ctx->parent = parent;
- ctx->type = type;
- ctx->data_expr = data_expr;
- ctx->mode = mode;
- ctx->is_dir = is_dir;
-
- // copy volatile strings
- if (
- (link && (ctx->link = strdup(link)) == NULL)
- || (name && (ctx->name = strdup(name)) == NULL)
- )
- ERROR("strdup");
-
- // log
- INFO("[dbfs.mk %p:%p] parent=%lu, name=%s, type=%s, mode=%#04o data_expr=%s link=%s is_dir=%hhd", ctx, req, parent, name, type, mode, data_expr, link, is_dir);
-
- // wait
- return;
-
-error:
- if (ctx)
- dbfs_trans_fail(&ctx->base, EIO);
-}
-
-/*
- * These are all just aliases to dbfs_mk
- */
-void dbfs_mknod (struct fuse_req *req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev) {
- int err;
-
- if ((mode & S_IFMT) != S_IFREG)
- EERROR(err = EINVAL, "mode is not REG: %#08o", mode);
-
- dbfs_mk(req, parent, name, "REG", mode & 07777, "lo_create(0)", NULL, 0);
-
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_error");
-}
-
-void dbfs_mkdir (struct fuse_req *req, fuse_ino_t parent, const char *name, mode_t mode) {
- dbfs_mk(req, parent, name, "DIR", mode, NULL, NULL, 1);
-}
-
-
-void dbfs_symlink (struct fuse_req *req, const char *link, fuse_ino_t parent, const char *name) {
- dbfs_mk(req, parent, name, "LNK", DBFS_SYMLINK_MODE, NULL, link, 0);
-}
-
--- a/src/dbfs/op_base.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,299 +0,0 @@
-#include <stdlib.h>
-#include <assert.h>
-
-#include "op_base.h"
-#include "../lib/log.h"
-
-/*
- * Free the op.
- *
- * The op must any oustanding request responded to first, must not be open, and must not have a transaction.
- *
- * The op will be free'd.
- */
-static void dbfs_op_free (struct dbfs_op *op) {
- assert(op);
- assert(!op->open);
- assert(!op->req);
- assert(!op->trans);
-
- // free_fn
- if (op->free_fn)
- op->free_fn(op);
-
- // and then free the op
- free(op);
-}
-
-void dbfs_op_fail (struct dbfs_op *op, int err) {
- assert(op->req);
-
- if (op->trans) {
- // abort the trans
- evsql_trans_abort(op->trans);
-
- op->trans = NULL;
- }
-
- // send an error reply
- if ((err = fuse_reply_err(op->req, err)))
- // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid ops
- EFATAL(err, "\tdbfs_op.fail %p:%p -> reply with fuse_reply_err", op, op->req);
-
- // drop the req
- op->req = NULL;
-
- // is it open?
- if (!op->open) {
- // no, we can free it now and then forget about the whole thing
- dbfs_op_free(op);
-
- } else {
- // we need to wait for release
-
- }
-}
-
-/*
- * The op_open transaction is ready for use.
- */
-static void dbfs_op_ready (struct evsql_trans *trans, void *arg) {
- struct dbfs_op *op = arg;
-
- assert(trans == op->trans);
- assert(op->req);
- assert(!op->open);
-
- INFO("\tdbfs_op.ready %p:%p -> trans=%p", op, op->req, trans);
-
- // remember the transaction
- op->trans = trans;
-
- // ready
- op->open_fn(op);
-
- // good
- return;
-}
-
-/*
- * The op trans was committed, i.e. release has completed
- */
-static void dbfs_op_done (struct evsql_trans *trans, void *arg) {
- struct dbfs_op *op = arg;
- int err;
-
- assert(trans == op->trans);
- assert(op->req);
- assert(!op->open); // should not be considered as open anymore at this point, as errors should release
-
- INFO("\tdbfs_op.done %p:%p -> OK", op, op->req);
-
- // forget trans
- op->trans = NULL;
-
- // just reply
- if ((err = fuse_reply_err(op->req, 0)))
- // XXX: handle these failures /somehow/, or requests will hang and interrupts might handle invalid ops
- EFATAL(err, "dbfs_op.done %p:%p -> reply with fuse_reply_err", op, op->req);
-
- // req is done
- op->req = NULL;
-
- // then we can just free op
- dbfs_op_free(op);
-}
-
-/*
- * The op trans has failed, somehow, at some point, some where.
- *
- * This might happend during the open evsql_trans, during a read evsql_query, during the release
- * evsql_trans_commit, or at any point in between.
- *
- * 1) loose the transaction
- * 2) if op has a req, we handle failing it
- */
-static void dbfs_op_error (struct evsql_trans *trans, void *arg) {
- struct dbfs_op *op = arg;
-
- // unless we fail
- assert(trans == op->trans);
-
- INFO("\tdbfs_op.error %p:%p -> evsql transaction error: %s", op, op->req, evsql_trans_error(trans));
-
- // deassociate the trans
- op->trans = NULL;
-
- // if we were answering a req, error it out, and if the op isn't open, free
- // if we didn't have a req outstanding, the op must be open, so we wouldn't free it in any case, and must wait
- // for the next read/release to detect this and return an error reply
- if (op->req)
- dbfs_op_fail(op, EIO);
- else
- assert(op->open);
-}
-
-int dbfs_op_open (struct dbfs *ctx, struct dbfs_op *op, struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi, dbfs_op_free_cb free_fn, dbfs_op_open_cb open_fn) {
- int err;
-
- assert(op && req && ino && fi);
- assert(!(op->req || op->ino));
-
- // initialize the op
- op->req = req;
- op->ino = ino;
- // copy *fi since it's on the stack
- op->fi = *fi;
- op->fi.fh = (uint64_t) op;
- op->free_fn = free_fn;
- op->open_fn = open_fn;
-
- // start a new transaction
- if ((op->trans = evsql_trans(ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_op_error, dbfs_op_ready, dbfs_op_done, op)) == NULL)
- SERROR(err = EIO);
-
- // XXX: handle interrupts
-
- // good
- return 0;
-
-error:
- // nothing of ours to cleanup
- return err;
-}
-
-int dbfs_op_open_reply (struct dbfs_op *op) {
- int err;
-
- // detect earlier failures
- if (!op->trans && (err = EIO))
- ERROR("op trans has failed");
-
- // send the openddir reply
- if ((err = fuse_reply_open(op->req, &op->fi)))
- EERROR(err, "fuse_reply_open");
-
- // req is done
- op->req = NULL;
-
- // op is now open
- op->open = 1;
-
- // good
- return 0;
-
-error:
- return err;
-}
-
-struct dbfs_op *dbfs_op_req (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
- struct dbfs_op *op = (struct dbfs_op *) fi->fh;
- int err;
-
- // validate
- assert(op);
- assert(op->open);
- assert(op->ino == ino);
-
- // detect concurrent requests
- if (op->req) {
- // must handle req ourself, shouldn't fail the other req
- WARNING("op.%p: concurrent req: %p -> %p", op, op->req, req);
-
- // XXX: ignore error errors...
- fuse_reply_err(req, EBUSY);
-
- return NULL;
-
- } else
- // store the new req
- op->req = req;
-
- // inodes change?
- if (op->ino != ino)
- XERROR(err = EBADF, "op.%p: wrong ino: %u -> %lu", op, op->ino, ino);
-
- // detect earlier failures
- if (!op->trans && (err = EIO))
- ERROR("op trans has failed");
-
- // good
- return op;
-
-error:
- dbfs_op_fail(op, err);
-
- return NULL;
-}
-
-int dbfs_op_req_done (struct dbfs_op *op) {
- // validate
- assert(op);
- assert(op->req);
- assert(op->open);
-
- // unassign the req
- op->req = NULL;
-
- // k
- return 0;
-}
-
-void dbfs_op_release (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
- struct dbfs_op *op = (struct dbfs_op *) fi->fh;
- int err;
-
- assert(op);
- assert(!op->req);
- assert(op->ino == ino);
-
- // update to this req
- op->req = req;
-
- // fi is irrelevant, we don't touch the flags anyways
- (void) fi;
-
- // handle failed trans
- if (!op->trans && (err = EIO))
- ERROR("trans has failed");
-
- // log
- INFO("\tdbfs_op.release %p:%p : ino=%lu, fi=%p : trans=%p", op, req, ino, fi, op->trans);
-
- // we must commit the transaction.
- // Note that this might cause dbfs_op_error to be called, we can tell if that happaned by looking at op->req
- // or op->trans - this means that we need to keep the op open when calling trans_commit, so that op_error
- // doesn't free it out from underneath us.
- if (evsql_trans_commit(op->trans))
- SERROR(err = EIO);
-
- // fall-through to cleanup
- err = 0;
-
-error:
- // the op is not open anymore and can be free'd next, because we either:
- // a) already caught an error
- // b) we get+send an error later on
- // c) we get+send the done/no-error later on
- op->open = 0;
-
- // did the commit/pre-commit-checks fail?
- if (err) {
- // a) the trans failed earlier (read), so we have a req but no trans
- // b) the trans commit failed, op_error got called -> no req and no trans
- // c) the trans commit failed, op_error did not get called -> have req and trans
- // we either have a req (may or may not have trans), or we don't have a trans either
- // i.e. there is no situation where we don't have a req but do have a trans
-
- if (op->req)
- dbfs_op_fail(op, err);
- else
- assert(!op->trans);
-
- } else {
- // shouldn't slip by, op_done should not get called directly. Once it does, it will handle both.
- assert(op->req);
- assert(op->trans);
- }
-}
-
--- a/src/dbfs/op_base.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,96 +0,0 @@
-#ifndef DBFS_OP_BASE_H
-#define DBFS_OP_BASE_H
-
-#include "dbfs.h"
-
-// forward-declaration for callbacks
-struct dbfs_op;
-
-/*
- * Called by dbfs_op_free to release any resources when the op is free'd (i.e. not open anymore).
- */
-typedef void (*dbfs_op_free_cb) (struct dbfs_op *op_base);
-
-/*
- * Called after the transaction has been opened, and before reply_open.
- *
- * You can do any at-open initialization here.
- */
-typedef void (*dbfs_op_open_cb) (struct dbfs_op *op_base);
-
-// the base op state
-struct dbfs_op {
- struct fuse_file_info fi;
- struct fuse_req *req;
-
- struct evsql_trans *trans;
-
- // op target inode
- uint32_t ino;
-
- // open has returned and release hasn't been called yet
- int open;
-
- // callbacks
- dbfs_op_free_cb free_fn;
- dbfs_op_open_cb open_fn;
-};
-
-/*
- * This will handle failures during requests.
- *
- * 1) if we have a trans, abort it
- * 2) fail the req (mandatory) with the given err
- *
- * If the op is open, then we don't release it, but if it's not open, then the op will be free'd completely.
- *
- */
-void dbfs_op_fail (struct dbfs_op *op, int err);
-
-/*
- * Open the op, that is, store all the initial state, and open a new transaction.
- *
- * The op must be pre-allocated and zero-initialized.
- *
- * This will always set op->req, so op is safe for dbfs_op_fail after this.
- *
- * This does not fail the dirop, handle error replies yourself.
- *
- * Returns zero on success, err on failure.
- */
-int dbfs_op_open (struct dbfs *ctx, struct dbfs_op *op, struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi, dbfs_op_free_cb free_fn, dbfs_op_open_cb ready_fn);
-
-/*
- * Should be called from open_fn to send the fuse_reply_open with fi and mark the op as open.
- *
- * If the op has failed earlier or fuse_reply_open fails, this will return nonzero. Fail the op yourself.
- */
-int dbfs_op_open_reply (struct dbfs_op *op);
-
-/*
- * Start handling a normal op requests.
- *
- * Lookup the op for the given fi, validate params, and assign the new req.
- *
- * In case the op failed previously, this will error the req and return NULL, indicating that the req has been handled.
- *
- * Repeat, if this returns NULL, consider req invalid.
- */
-struct dbfs_op *dbfs_op_req (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi);
-
-/*
- * Done handling a request, adjust state accordingly.
- *
- * req *must* have been replied to.
- */
-int dbfs_op_req_done (struct dbfs_op *op);
-
-/*
- * Handle the op release.
- *
- * This will take care of committing the transaction, sending any reply/error, closing the op and freeing it.
- */
-void dbfs_op_release (struct fuse_req *req, fuse_ino_t, struct fuse_file_info *fi);
-
-
-#endif /* DBFS_OP_BASE_H */
--- a/src/dbfs/ops.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,40 +0,0 @@
-#ifndef DBFS_OPS_H
-#define DBFS_OPS_H
-
-#include "../evfuse.h"
-
-/* dbfs.c */
-void dbfs_init (void *userdata, struct fuse_conn_info *conn);
-void dbfs_destroy (void *arg);
-
-/* attr.c */
-void dbfs_getattr (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi);
-void dbfs_setattr(struct fuse_req *req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi);
-
-/* link.c */
-void dbfs_lookup (struct fuse_req *req, fuse_ino_t parent, const char *name);
-void dbfs_readlink (struct fuse_req *req, fuse_ino_t ino);
-void dbfs_unlink (struct fuse_req *req, fuse_ino_t parent, const char *name);
-void dbfs_link (struct fuse_req *req, fuse_ino_t ino, fuse_ino_t newparent, const char *newname);
-
-/* dirop.c */
-void dbfs_opendir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi);
-void dbfs_readdir (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi);
-void dbfs_releasedir (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi);
-
-/* mk.c */
-void dbfs_mknod (struct fuse_req *req, fuse_ino_t parent, const char *name, mode_t mode, dev_t rdev);
-void dbfs_mkdir (struct fuse_req *req, fuse_ino_t parent, const char *name, mode_t mode);
-void dbfs_symlink (struct fuse_req *req, const char *link, fuse_ino_t parent, const char *name);
-
-/* tree.c */
-void dbfs_rename (struct fuse_req *req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname);
-
-/* fileop.c */
-void dbfs_open (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi);
-void dbfs_read (struct fuse_req *req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi);
-void dbfs_write (struct fuse_req *req, fuse_ino_t ino, const char *buf, size_t size, off_t off, struct fuse_file_info *fi);
-void dbfs_flush (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi);
-void dbfs_release (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi);
-
-#endif /* DBFS_OPS_H */
--- a/src/dbfs/trans.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,138 +0,0 @@
-
-#include <stdlib.h>
-#include <assert.h>
-
-#include "trans.h"
-#include "../lib/log.h"
-
-void dbfs_trans_free (struct dbfs_trans *ctx) {
- assert(!ctx->req);
- assert(!ctx->trans);
-
- if (ctx->free_fn)
- ctx->free_fn(ctx);
-
- free(ctx);
-}
-
-void dbfs_trans_fail (struct dbfs_trans *ctx, int err) {
- if (ctx->req) {
- if ((err = fuse_reply_err(ctx->req, err)))
- EWARNING(err, "fuse_reply_err: request hangs");
-
- ctx->req = NULL;
- }
-
- if (ctx->trans) {
- evsql_trans_abort(ctx->trans);
-
- ctx->trans = NULL;
- }
-
- dbfs_trans_free(ctx);
-}
-
-static void dbfs_trans_error (struct evsql_trans *trans, void *arg) {
- struct dbfs_trans *ctx = arg;
-
- // deassociate trans
- ctx->trans = NULL;
-
- // log error
- INFO("\t[dbfs_trans.err %p:%p] %s", ctx, ctx->req, evsql_trans_error(trans));
-
- // mark
- if (ctx->err_ptr)
- *ctx->err_ptr = EIO;
-
- // fail
- dbfs_trans_fail(ctx, EIO);
-}
-
-static void dbfs_trans_ready (struct evsql_trans *trans, void *arg) {
- struct dbfs_trans *ctx = arg;
-
- // associate trans
- ctx->trans = trans;
-
- // log
- INFO("\t[dbfs_trans.ready %p:%p] -> trans=%p", ctx, ctx->req, trans);
-
- // trigger the callback
- ctx->begin_fn(ctx);
-}
-
-static void dbfs_trans_done (struct evsql_trans *trans, void *arg) {
- struct dbfs_trans *ctx = arg;
-
- // deassociate trans
- ctx->trans = NULL;
-
- // log
- INFO("\t[dbfs_trans.done %p:%p]", ctx, ctx->req);
-
- // trigger the callback
- ctx->commit_fn(ctx);
-}
-
-int dbfs_trans_init (struct dbfs_trans *ctx, struct fuse_req *req) {
- struct dbfs *dbfs_ctx = fuse_req_userdata(req);
- int err;
-
- // store
- ctx->req = req;
-
- // trans
- if ((ctx->trans = evsql_trans(dbfs_ctx->db, EVSQL_TRANS_SERIALIZABLE, dbfs_trans_error, dbfs_trans_ready, dbfs_trans_done, ctx)) == NULL)
- EERROR(err = EIO, "evsql_trans");
-
- // good
- return 0;
-
-error:
- return -1;
-}
-
-struct dbfs_trans *dbfs_trans_new (struct fuse_req *req) {
- struct dbfs_trans *ctx = NULL;
-
- // alloc
- if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
- ERROR("calloc");
-
- // init
- if (dbfs_trans_init(ctx, req))
- goto error;
-
- // good
- return ctx;
-
-error:
- free(ctx);
-
- return NULL;
-}
-
-void dbfs_trans_commit (struct dbfs_trans *ctx) {
- int err, trans_err = 0;
-
- // detect errors
- ctx->err_ptr = &trans_err;
-
- // attempt commit
- if (evsql_trans_commit(ctx->trans))
- SERROR(err = EIO);
-
- // drop err_ptr
- ctx->err_ptr = NULL;
-
- // ok, wait for done or error
- return;
-
-error:
- // fail if not already failed
- if (!trans_err)
- dbfs_trans_fail(ctx, err);
-}
-
-
--- a/src/dbfs/trans.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,69 +0,0 @@
-#ifndef DBFS_TRANS_H
-#define DBFS_TRANS_H
-
-/*
- * Support for single-fuse_req transactions.
- */
-
-#include "dbfs.h"
-
-// forward-declaration
-struct dbfs_trans;
-
-// generic callback
-typedef void (*dbfs_trans_cb) (struct dbfs_trans *ctx);
-
-/*
- * Request/transaction state.
- */
-struct dbfs_trans {
- struct fuse_req *req;
- struct evsql_trans *trans;
-
- // called when the dbfs_trans is being free'd
- dbfs_trans_cb free_fn;
-
- // called once the transaction is ready
- dbfs_trans_cb begin_fn;
-
- // called once the transaction has been commited
- dbfs_trans_cb commit_fn;
-
- // set by trans_error to EIO if !NULL
- int *err_ptr;
-};
-
-/*
- * Call from commit_fn once you've set req to NULL. Also called from trans_fail.
- *
- * Will call free_fn if present.
- */
-void dbfs_trans_free (struct dbfs_trans *ctx);
-
-/*
- * Fail the dbfs_trans, aborting any trans, erroring out any req and freeing the ctx.
- */
-void dbfs_trans_fail (struct dbfs_trans *ctx, int err);
-
-/*
- * Initialize the ctx with the given req (stays the same during the lifetime), and opens the transaction.
- *
- * You should set the callback functions after/before calling this.
- *
- * begin_fn will be called once the transaction is open, if that fails, the req will be errored for you.
- *
- * If opening the transaction fails, this will return nonzero and not touch req, otherwise zero.
- */
-int dbfs_trans_init (struct dbfs_trans *ctx, struct fuse_req *req);
-
-/*
- * Same as init, but allocates/frees-on-error the dbfs_trans for you.
- */
-struct dbfs_trans *dbfs_trans_new (struct fuse_req *req);
-
-/*
- * Commit the dbfs_trans. After calling this, the ctx may or may not be valid, and commit_fn may or may not be called.
- */
-void dbfs_trans_commit (struct dbfs_trans *ctx);
-
-#endif /* DBFS_TRANS_H */
--- a/src/dbfs/tree.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,71 +0,0 @@
-
-#include "../lib/log.h"
-
-#include "dbfs.h"
-
-void dbfs_rename_res (const struct evsql_result_info *res, void *arg) {
- struct fuse_req *req = arg;
- int err;
-
- // check the results
- if ((err = _dbfs_check_res(res, 0, 0)))
- SERROR(err = (err == 1 ? ENOENT : EIO));
-
- // just reply
- if ((err = -fuse_reply_err(req, 0)))
- EERROR(err, "fuse_reply_err");
-
- // log
- INFO("[dbfs.rename %p] -> OK", req);
-
- // fallthrough for result_free
- err = 0;
-
-error:
- if (err && (err = -fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-
- evsql_result_free(res);
-}
-
-void dbfs_rename (struct fuse_req *req, fuse_ino_t parent, const char *name, fuse_ino_t newparent, const char *newname) {
- struct dbfs *dbfs_ctx = fuse_req_userdata(req);
- int err;
-
- INFO("[dbfs.rename %p] parent=%lu, name=%s, newparent=%lu, newname=%s", req, parent, name, newparent, newname);
-
- // just one UPDATE
- const char *sql =
- "UPDATE"
- " file_tree"
- " SET parent = $1::int4, name = $2::varchar"
- " WHERE parent = $3::int4 AND name = $4::varchar";
-
- static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
- EVSQL_PARAM ( UINT32 ),
- EVSQL_PARAM ( STRING ),
- EVSQL_PARAM ( UINT32 ),
- EVSQL_PARAM ( STRING ),
-
- EVSQL_PARAMS_END
- };
-
- if (0
- || evsql_param_uint32(¶ms, 0, newparent)
- || evsql_param_string(¶ms, 1, newname)
- || evsql_param_uint32(¶ms, 2, parent)
- || evsql_param_string(¶ms, 3, name)
- )
- SERROR(err = EIO);
-
- // query
- if (evsql_query_params(dbfs_ctx->db, NULL, sql, ¶ms, dbfs_rename_res, req) == NULL)
- SERROR(err = EIO);
-
- // good, wait
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
--- a/src/dirbuf.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,87 +0,0 @@
-
-#include <stdlib.h>
-
-#include "dirbuf.h"
-#include "lib/log.h"
-#include "lib/math.h"
-
-int dirbuf_init (struct dirbuf *buf, size_t req_size, off_t req_off) {
- buf->buf = NULL;
- buf->len = req_size;
- buf->off = 0;
- buf->req_off = req_off;
-
- DEBUG("\tdirbuf.init: req_size=%zu", req_size);
-
- // allocate the mem
- if ((buf->buf = malloc(buf->len)) == NULL)
- ERROR("malloc");
-
- // ok
- return 0;
-
-error:
- return -1;
-}
-
-size_t dirbuf_estimate (struct dirbuf *buf, size_t min_namelen) {
- char namebuf[DIRBUF_NAME_MAX];
- int i;
-
- // build a dummy string of the right length
- for (i = 0; i < min_namelen && i < DIRBUF_NAME_MAX - 1; i++)
- namebuf[i] = 'x';
-
- namebuf[i] = '\0';
-
- return buf->len / (fuse_add_direntry(NULL, NULL, 0, namebuf, NULL, 0));
-}
-
-int dirbuf_add (fuse_req_t req, struct dirbuf *buf, off_t ent_off, off_t next_off, const char *ent_name, fuse_ino_t ent_ino, mode_t ent_mode) {
- struct stat stbuf;
- size_t ent_size;
-
- DEBUG("\tdirbuf.add: req_off=%zu, buf->len=%zu, buf->off=%zu, ent_off=%zu, next_off=%zu, ent_name=`%s`, ent_ino=%lu, ent_mode=%07o",
- buf->req_off, buf->len, buf->off, ent_off, next_off, ent_name, ent_ino, ent_mode);
-
- // skip entries as needed
- if (ent_off < buf->req_off)
- return 0;
-
- // set ino
- stbuf.st_ino = ent_ino;
- stbuf.st_mode = ent_mode;
-
- // try and add the dirent, and see if it fits
- if ((ent_size = fuse_add_direntry(req, buf->buf + buf->off, buf->len - buf->off, ent_name, &stbuf, next_off)) > (buf->len - buf->off)) {
- // 'tis full
- return 1;
-
- } else {
- // it fit
- buf->off += ent_size;
- }
-
- // success
- return 0;
-}
-
-int dirbuf_done (fuse_req_t req, struct dirbuf *buf) {
- int err;
-
- // send the reply, return the error later
- err = -fuse_reply_buf(req, buf->buf, buf->off);
-
- DEBUG("\tdirbuf.done: size=%zu/%zu, err=%d", buf->off, buf->len, err);
-
- // free the dirbuf
- dirbuf_release(buf);
-
- // return the error code
- return err;
-}
-
-void dirbuf_release (struct dirbuf *buf) {
- free(buf->buf); buf->buf = NULL;
-}
-
--- a/src/dirbuf.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,60 +0,0 @@
-#ifndef DIRBUF_H
-#define DIRBUF_H
-
-/*
- * Simple dirent building
- */
-
-#include "evfuse.h"
-
-/*
- * Holds the dir entries
- */
-struct dirbuf {
- char *buf;
- size_t len;
- off_t off, req_off;
-};
-
-// maximum length for a dirbuf name, including NUL byte
-#define DIRBUF_NAME_MAX 256
-
-/*
- * Initialize a dirbuf for a request. The dirbuf will be filled with at most req_size bytes of dir entries.
- *
- * req_size - how many bytes of dirbuf data we want, at most
- * req_off - the offset of the first dirent to include
- */
-int dirbuf_init (struct dirbuf *buf, size_t req_size, off_t req_off);
-
-/*
- * Estimate how many dir entries will, at most, fit into a difbuf of the given size, based on a minimum filename size.
- */
-size_t dirbuf_estimate (struct dirbuf *buf, size_t min_namelen);
-
-/*
- * Add an dir entry to the dirbuf. The dirbuf should not be full.
- *
- * Offsets are followed:
- * ent_off - the offset of this dirent
- * next_off - the offset of the next dirent
- *
- * Only the S_IFMT bits of ent_mode are relevant.
- *
- * Returns 0 if the ent was added or skipped, -1 on error, 1 if the dirbuf is full (no more ents should be added).
- */
-int dirbuf_add (fuse_req_t req, struct dirbuf *buf, off_t ent_off, off_t next_off, const char *ent_name, fuse_ino_t ent_ino, mode_t ent_mode);
-
-/*
- * Attempt to send the readdir reply, free the buf, and return the error code from fuse_reply_buf
- */
-int dirbuf_done (fuse_req_t req, struct dirbuf *buf);
-
-/*
- * Release the dirop without sending any reply back.
- *
- * This is safe to be called multiple times.
- */
-void dirbuf_release (struct dirbuf *buf);
-
-#endif /* DIRBUF_H */
--- a/src/evfuse.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,153 +0,0 @@
-
-#include <errno.h>
-#include <string.h>
-#include <stdlib.h>
-
-#include "evfuse.h"
-#include "lib/log.h"
-
-struct evfuse {
- // our mountpoint
- char *mountpoint;
-
- // the /dev/fuse fd/channel that we get from fuse_mount
- struct fuse_chan *chan;
-
- // the session that we use to process the fuse stuff
- struct fuse_session *session;
-
- // the event that we use to receive requests
- struct event *ev;
-
- // what our receive-message length is
- size_t recv_size;
-
- // the buffer that we use to receive events
- char *recv_buf;
-};
-
-// prototypes
-void evfuse_close (struct evfuse *ctx);
-
-static void _evfuse_ev_read (evutil_socket_t fd, short what, void *arg) {
- struct evfuse *ctx = arg;
- struct fuse_chan *ch = ctx->chan;
- int res;
-
- // loop until we complete a recv
- do {
- // a new fuse_req is available
- res = fuse_chan_recv(&ch, ctx->recv_buf, ctx->recv_size);
- } while (res == -EINTR);
-
- if (res == 0)
- ERROR("fuse_chan_recv gave EOF");
-
- if (res < 0 && res != -EAGAIN)
- ERROR("fuse_chan_recv failed: %s", strerror(-res));
-
- if (res > 0) {
- DEBUG("got %d bytes from /dev/fuse", res);
-
- // received a fuse_req, so process it
- fuse_session_process(ctx->session, ctx->recv_buf, res, ch);
- }
-
- // reschedule
- if (event_add(ctx->ev, NULL))
- PERROR("event_add");
-
- // ok, wait for the next event
- return;
-
-error:
- // close, but don't free
- evfuse_close(ctx);
-}
-
-struct evfuse *evfuse_new (struct event_base *evbase, struct fuse_args *args, struct fuse_lowlevel_ops *llops, void *cb_data) {
- struct evfuse *ctx = NULL;
- int multithreaded, foreground;
-
- // allocate our context
- if ((ctx = calloc(1, sizeof(*ctx))) == NULL)
- ERROR("calloc");
-
- // parse the commandline for the mountpoint
- if (fuse_parse_cmdline(args, &ctx->mountpoint, &multithreaded, &foreground) == -1)
- ERROR("fuse_parse_cmdline");
-
- // mount it
- if ((ctx->chan = fuse_mount(ctx->mountpoint, args)) == NULL)
- PERROR("fuse_mount_common");
-
- // the receive buffer stufff
- ctx->recv_size = fuse_chan_bufsize(ctx->chan);
-
- // allocate the recv buffer
- if ((ctx->recv_buf = malloc(ctx->recv_size)) == NULL)
- ERROR("malloc");
-
- // allocate a low-level session
- if ((ctx->session = fuse_lowlevel_new(args, llops, sizeof(*llops), cb_data)) == NULL)
- PERROR("fuse_lowlevel_new");
-
- // add the channel to the session
- // this isn't strictly necessary, but let's do it anyways
- fuse_session_add_chan(ctx->session, ctx->chan);
-
- // now, we can start listening for events on the channel
- if ((ctx->ev = event_new(evbase, fuse_chan_fd(ctx->chan), EV_READ, &_evfuse_ev_read, ctx)) == NULL)
- ERROR("event_new");
-
- if (event_add(ctx->ev, NULL))
- PERROR("event_add");
-
- // and then we wait
- return ctx;
-
-error:
- evfuse_free(ctx);
-
- return NULL;
-}
-
-void evfuse_close (struct evfuse *ctx) {
- if (ctx->ev) {
- // remove our event
- if (event_del(ctx->ev))
- PWARNING("event_del");
-
- ctx->ev = NULL;
- }
-
- if (ctx->session) {
- // remove the chan
- fuse_session_remove_chan(ctx->chan);
-
- // destroy the session
- fuse_session_destroy(ctx->session);
-
- ctx->session = NULL;
- }
-
- if (ctx->chan) {
- // unmount
- fuse_unmount(ctx->mountpoint, ctx->chan);
-
- ctx->chan = NULL;
- }
-
- // free
- free(ctx->recv_buf); ctx->recv_buf = NULL;
- free(ctx->mountpoint); ctx->mountpoint = NULL;
-}
-
-void evfuse_free (struct evfuse *ctx) {
- if (ctx) {
- evfuse_close(ctx);
-
- free(ctx);
- }
-}
-
--- a/src/evfuse.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,27 +0,0 @@
-#ifndef EVFUSE_H
-#define EVFUSE_H
-
-#define FUSE_USE_VERSION 26
-
-#include <event2/event.h>
-#include <fuse/fuse_lowlevel.h>
-
-/*
- * A wrapper for the fuse + libevent context
- */
-struct evfuse;
-
-/*
- * Create a new new evfuse context.
- */
-struct evfuse *evfuse_new (struct event_base *evbase, struct fuse_args *args, struct fuse_lowlevel_ops *llops, void *cb_data);
-
-/*
- * Close and free evfuse context.
- *
- * Safe to call after errors/llops.destroy
- */
-void evfuse_free (struct evfuse *ctx);
-
-#endif /* EVFUSE_H */
-
--- a/src/evsql.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,276 +0,0 @@
-#ifndef EVSQL_H
-#define EVSQL_H
-
-/*
- * An event-based (Postgre)SQL client API using libevent
- */
-
-// XXX: libpq
-#include <stdint.h>
-#include <postgresql/libpq-fe.h>
-#include <event2/event.h>
-
-/*
- * The generic context handle
- */
-struct evsql;
-
-/*
- * A query handle
- */
-struct evsql_query;
-
-/*
- * Transaction handle
- */
-struct evsql_trans;
-
-/*
- * Transaction type
- */
-enum evsql_trans_type {
- EVSQL_TRANS_DEFAULT,
- EVSQL_TRANS_SERIALIZABLE,
- EVSQL_TRANS_REPEATABLE_READ,
- EVSQL_TRANS_READ_COMMITTED,
- EVSQL_TRANS_READ_UNCOMMITTED,
-};
-
-/*
- * Parameter type
- */
-enum evsql_param_format {
- EVSQL_FMT_TEXT,
- EVSQL_FMT_BINARY,
-};
-
-enum evsql_param_type {
- EVSQL_PARAM_INVALID,
-
- EVSQL_PARAM_NULL_,
-
- EVSQL_PARAM_BINARY,
- 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
- enum evsql_param_format result_fmt;
-
- // the list of parameters, terminated by { 0, 0 }
- struct evsql_query_param {
- // 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, 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_PARAMS(result_fmt) { result_fmt,
-#define EVSQL_PARAM(typenam) { EVSQL_PARAM_ ## typenam, NULL }
-#define EVSQL_PARAMS_END { EVSQL_PARAM_INVALID, NULL } \
- } // <<<
-
-/*
- * Result type
- */
-struct evsql_result_info {
- struct evsql *evsql;
- struct evsql_trans *trans;
-
- int error;
-
- union evsql_result {
- // libpq
- PGresult *pq;
- } result;
-};
-
-/*
- * Callback for handling query results.
- *
- * The query has completed, either succesfully or unsuccesfully (nonzero .error).
- *
- * Use the evsql_result_* functions to manipulate the results.
- */
-typedef void (*evsql_query_cb)(const struct evsql_result_info *res, void *arg);
-
-/*
- * Callback for handling global-level errors.
- *
- * The evsql is not useable anymore.
- *
- * XXX: this is not actually called yet, no retry logic implemented.
- */
-typedef void (*evsql_error_cb)(struct evsql *evsql, void *arg);
-
-/*
- * Callback for handling transaction-level errors.
- *
- * The transaction is not useable anymore.
- */
-typedef void (*evsql_trans_error_cb)(struct evsql_trans *trans, void *arg);
-
-/*
- * The transaction is ready for use.
- */
-typedef void (*evsql_trans_ready_cb)(struct evsql_trans *trans, void *arg);
-
-/*
- * The transaction was commited, and should not be used anymore.
- */
-typedef void (*evsql_trans_done_cb)(struct evsql_trans *trans, void *arg);
-
-/*
- * Create a new PostgreSQL/libpq(evpq) -based evsql using the given conninfo.
- *
- * The given conninfo must stay valid for the duration of the evsql's lifetime.
- */
-struct evsql *evsql_new_pq (struct event_base *ev_base, const char *pq_conninfo, evsql_error_cb error_fn, void *cb_arg);
-
-/*
- * Create a new transaction.
- *
- * Transactions are separate connections that provide transaction-isolation.
- *
- * Once the transaction is ready for use, ready_fn will be called. If the transaction fails, any pending query will be
- * forgotten, and error_fn called. This also includes some (but not all) cases where evsql_query returns nonzero.
- *
- */
-struct evsql_trans *evsql_trans (struct evsql *evsql, enum evsql_trans_type type, evsql_trans_error_cb error_fn, evsql_trans_ready_cb ready_fn, evsql_trans_done_cb done_fn, void *cb_arg);
-
-/*
- * Queue the given query for execution.
- *
- * If trans is specified (not NULL), then the transaction must be idle, and the query will be executed in that
- * transaction's context. Otherwise, the query will be executed without a transaction, andmay be executed immediately,
- * or if other similar queries are running, it will be queued for later execution.
- *
- * Once the query is complete (got a result, got an error, the connection failed), then the query_cb will be triggered.
- */
-struct evsql_query *evsql_query (struct evsql *evsql, struct evsql_trans *trans, const char *command, evsql_query_cb query_fn, void *cb_arg);
-
-/*
- * 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);
-
-/*
- * Abort a query, the query callback will not be called, the query and any possible results will be discarded.
- *
- * This does not garuntee that the query will not execute, simply that you won't get the results.
- *
- * If the query is part of a transaction, then trans must be given, and the query must be the query that is currently
- * executing on that trans. The transaction's ready_fn will be called once the query has been aborted.
- */
-void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query);
-
-/*
- * Commit a transaction, calling done_fn if it was succesfull (error_fn otherwise).
- *
- * trans must be idle, just like for evsql_query.
- *
- * done_fn will never be called directly, always via the event loop.
- *
- * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error.
- */
-int evsql_trans_commit (struct evsql_trans *trans);
-
-/*
- * Abort a transaction, rolling it back. No callbacks will be called.
- *
- * You cannot abort a COMMIT, calling trans_abort on trans after a succesful trans_commit is a FATAL error.
- */
-void evsql_trans_abort (struct evsql_trans *trans);
-
-/*
- * Transaction-handling functions
- */
-
-// error string, meant to be called from evsql_trans_error_cb
-const char *evsql_trans_error (struct evsql_trans *trans);
-
-/*
- * Param-building functions
- */
-int evsql_param_binary (struct evsql_query_params *params, size_t param, const char *ptr, size_t len);
-int evsql_param_string (struct evsql_query_params *params, size_t param, const char *ptr);
-int evsql_param_uint16 (struct evsql_query_params *params, size_t param, uint16_t uval);
-int evsql_param_uint32 (struct evsql_query_params *params, size_t param, uint32_t uval);
-int evsql_param_null (struct evsql_query_params *params, size_t param);
-int evsql_params_clear (struct evsql_query_params *params);
-
-/*
- * Query-handling functions
- */
-
-// print out a textual repr of the given query/params via DEBUG
-void evsql_query_debug (const char *sql, const struct evsql_query_params *params);
-
-/*
- * 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);
-
-// number of affected rows for UPDATE/INSERT
-size_t evsql_result_affected (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.
- *
- * XXX: not implemented yet.
- */
-void evsql_close (struct evsql *evsql);
-
-#endif /* EVSQL_H */
--- a/src/evsql/evsql.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,1025 +0,0 @@
-#define _GNU_SOURCE
-#include <stdlib.h>
-#include <assert.h>
-#include <string.h>
-
-#include "evsql.h"
-#include "../lib/log.h"
-#include "../lib/error.h"
-#include "../lib/misc.h"
-
-/*
- * A couple function prototypes
- */
-static void _evsql_pump (struct evsql *evsql, struct evsql_conn *conn);
-
-/*
- * Actually execute the given query.
- *
- * The backend should be able to accept the query at this time.
- *
- * You should assume that if trying to execute a query fails, then the connection should also be considred as failed.
- */
-static int _evsql_query_exec (struct evsql_conn *conn, struct evsql_query *query, const char *command) {
- int err;
-
- DEBUG("evsql.%p: exec query=%p on trans=%p on conn=%p:", conn->evsql, query, conn->trans, conn);
-
- switch (conn->evsql->type) {
- case EVSQL_EVPQ:
- // got params?
- if (query->params.count) {
- err = evpq_query_params(conn->engine.evpq, command,
- query->params.count,
- query->params.types,
- query->params.values,
- query->params.lengths,
- query->params.formats,
- query->params.result_format
- );
-
- } else {
- // plain 'ole query
- err = evpq_query(conn->engine.evpq, command);
- }
-
- if (err) {
- if (PQstatus(evpq_pgconn(conn->engine.evpq)) != CONNECTION_OK)
- WARNING("conn failed");
- else
- WARNING("query failed, dropping conn as well");
- }
-
- break;
-
- default:
- FATAL("evsql->type");
- }
-
- if (!err)
- // assign the query
- conn->query = query;
-
- return err;
-}
-
-/*
- * Free the query and related resources, doesn't trigger any callbacks or remove from any queues.
- *
- * The command should already be taken care of (NULL).
- */
-static void _evsql_query_free (struct evsql_query *query) {
- if (!query)
- return;
-
- assert(query->command == NULL);
-
- // free params if present
- free(query->params.types);
- free(query->params.values);
- free(query->params.lengths);
- free(query->params.formats);
-
- // free the query itself
- free(query);
-}
-
-/*
- * Execute the callback if res is given, and free the query.
- *
- * The query has been aborted, it will simply be freed
- */
-static void _evsql_query_done (struct evsql_query *query, const struct evsql_result_info *res) {
- if (res) {
- if (query->cb_fn)
- // call the callback
- query->cb_fn(res, query->cb_arg);
- else
- WARNING("supressing cb_fn because query was aborted");
- }
-
- // free
- _evsql_query_free(query);
-}
-
-/*
- * XXX:
- * /
-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->query_queue)) != NULL) {
- _evsql_query_done(query, res);
-
- TAILQ_REMOVE(&evsql->query_queue, query, entry);
- }
-
- // free
- free(evsql);
-}
-*/
-
-/*
- * Free the transaction, it should already be deassociated from the query and conn.
- */
-static void _evsql_trans_free (struct evsql_trans *trans) {
- // ensure we don't leak anything
- assert(trans->query == NULL);
- assert(trans->conn == NULL);
-
- // free
- free(trans);
-}
-
-/*
- * Release a connection. It should already be deassociated from the trans and query.
- *
- * Releases the engine, removes from the conn_list and frees this.
- */
-static void _evsql_conn_release (struct evsql_conn *conn) {
- // ensure we don't leak anything
- assert(conn->trans == NULL);
- assert(conn->query == NULL);
-
- // release the engine
- switch (conn->evsql->type) {
- case EVSQL_EVPQ:
- evpq_release(conn->engine.evpq);
- break;
-
- default:
- FATAL("evsql->type");
- }
-
- // remove from list
- LIST_REMOVE(conn, entry);
-
- // catch deadlocks
- assert(!LIST_EMPTY(&conn->evsql->conn_list) || TAILQ_EMPTY(&conn->evsql->query_queue));
-
- // free
- free(conn);
-}
-
-/*
- * Release a transaction, it should already be deassociated from the query.
- *
- * Perform a two-way-deassociation with the conn, and then free the trans.
- */
-static void _evsql_trans_release (struct evsql_trans *trans) {
- assert(trans->query == NULL);
- assert(trans->conn != NULL);
-
- // deassociate the conn
- trans->conn->trans = NULL; trans->conn = NULL;
-
- // free the trans
- _evsql_trans_free(trans);
-}
-
-/*
- * Fail a single query, this will trigger the callback and free it.
- *
- * NOTE: Only for *TRANSACTIONLESS* queries.
- */
-static void _evsql_query_fail (struct evsql* evsql, struct evsql_query *query) {
- struct evsql_result_info res; ZINIT(res);
-
- // set up the result_info
- res.evsql = evsql;
- res.trans = NULL;
- res.error = 1;
-
- // finish off the query
- _evsql_query_done(query, &res);
-}
-
-/*
- * Fail a transaction, this will silently drop any query, trigger the error callback, two-way-deassociate/release the
- * conn, and then free the trans.
- */
-static void _evsql_trans_fail (struct evsql_trans *trans) {
- if (trans->query) {
- // free the query silently
- _evsql_query_free(trans->query); trans->query = NULL;
-
- // also deassociate it from the conn!
- trans->conn->query = NULL;
- }
-
- // tell the user
- // XXX: trans is in a bad state during this call
- if (trans->error_fn)
- trans->error_fn(trans, trans->cb_arg);
- else
- WARNING("supressing error because error_fn was NULL");
-
- // deassociate and release the conn
- trans->conn->trans = NULL; _evsql_conn_release(trans->conn); trans->conn = NULL;
-
- // pump the queue for requests that were waiting for this connection
- _evsql_pump(trans->evsql, NULL);
-
- // free the trans
- _evsql_trans_free(trans);
-}
-
-/*
- * Fail a connection. If the connection is transactional, this will just call _evsql_trans_fail, but otherwise it will
- * fail any ongoing query, and then release the connection.
- */
-static void _evsql_conn_fail (struct evsql_conn *conn) {
- if (conn->trans) {
- // let transactions handle their connection failures
- _evsql_trans_fail(conn->trans);
-
- } else {
- if (conn->query) {
- // fail the in-progress query
- _evsql_query_fail(conn->evsql, conn->query); conn->query = NULL;
- }
-
- // finish off the whole connection
- _evsql_conn_release(conn);
- }
-}
-
-/*
- * Processes enqueued non-transactional queries until the queue is empty, or we managed to exec a query.
- *
- * If execing a query on a connection fails, both the query and the connection are failed (in that order).
- *
- * Any further queries will then also be failed, because there's no reconnection/retry logic yet.
- *
- * This means that if conn is NULL, all queries are failed.
- */
-static void _evsql_pump (struct evsql *evsql, struct evsql_conn *conn) {
- struct evsql_query *query;
- int err;
-
- // look for waiting queries
- while ((query = TAILQ_FIRST(&evsql->query_queue)) != NULL) {
- // dequeue
- TAILQ_REMOVE(&evsql->query_queue, query, entry);
-
- if (conn) {
- // try and execute it
- err = _evsql_query_exec(conn, query, query->command);
- }
-
- // free the command buf
- free(query->command); query->command = NULL;
-
- if (err || !conn) {
- if (!conn) {
- // warn when dropping queries
- WARNING("failing query becuse there are no conns");
- }
-
- // fail the query
- _evsql_query_fail(evsql, query);
-
- if (conn) {
- // fail the connection
- WARNING("failing the connection because a query-exec failed");
-
- _evsql_conn_fail(conn); conn = NULL;
- }
-
- } else {
- // we have succesfully enqueued a query, and we can wait for this connection to complete
- break;
-
- }
-
- // handle the rest of the queue
- }
-
- // ok
- return;
-}
-
-/*
- * Callback for a trans's 'BEGIN' query, which means the transaction is now ready for use.
- */
-static void _evsql_trans_ready (const struct evsql_result_info *res, void *arg) {
- (void) arg;
-
- assert(res->trans);
-
- // check for errors
- if (res->error)
- ERROR("transaction 'BEGIN' failed: %s", evsql_result_error(res));
-
- // transaction is now ready for use
- res->trans->ready_fn(res->trans, res->trans->cb_arg);
-
- // good
- return;
-
-error:
- _evsql_trans_fail(res->trans);
-}
-
-/*
- * The transaction's connection is ready, send the 'BEGIN' query.
- *
- * If anything fails, calls _evsql_trans_fail and returns nonzero, zero on success
- */
-static int _evsql_trans_conn_ready (struct evsql *evsql, struct evsql_trans *trans) {
- char trans_sql[EVSQL_QUERY_BEGIN_BUF];
- const char *isolation_level;
- int ret;
-
- // determine the isolation_level to use
- switch (trans->type) {
- case EVSQL_TRANS_DEFAULT:
- isolation_level = NULL; break;
-
- case EVSQL_TRANS_SERIALIZABLE:
- isolation_level = "SERIALIZABLE"; break;
-
- case EVSQL_TRANS_REPEATABLE_READ:
- isolation_level = "REPEATABLE READ"; break;
-
- case EVSQL_TRANS_READ_COMMITTED:
- isolation_level = "READ COMMITTED"; break;
-
- case EVSQL_TRANS_READ_UNCOMMITTED:
- isolation_level = "READ UNCOMMITTED"; break;
-
- default:
- FATAL("trans->type: %d", trans->type);
- }
-
- // build the trans_sql
- if (isolation_level)
- ret = snprintf(trans_sql, EVSQL_QUERY_BEGIN_BUF, "BEGIN TRANSACTION ISOLATION LEVEL %s", isolation_level);
- else
- ret = snprintf(trans_sql, EVSQL_QUERY_BEGIN_BUF, "BEGIN TRANSACTION");
-
- // make sure it wasn't truncated
- if (ret >= EVSQL_QUERY_BEGIN_BUF)
- ERROR("trans_sql overflow: %d >= %d", ret, EVSQL_QUERY_BEGIN_BUF);
-
- // execute the query
- if (evsql_query(evsql, trans, trans_sql, _evsql_trans_ready, NULL) == NULL)
- ERROR("evsql_query");
-
- // success
- return 0;
-
-error:
- // fail the transaction
- _evsql_trans_fail(trans);
-
- return -1;
-}
-
-/*
- * The evpq connection was succesfully established.
- */
-static void _evsql_evpq_connected (struct evpq_conn *_conn, void *arg) {
- struct evsql_conn *conn = arg;
-
- if (conn->trans)
- // notify the transaction
- // don't care about errors
- (void) _evsql_trans_conn_ready(conn->evsql, conn->trans);
-
- else
- // pump any waiting transactionless queries
- _evsql_pump(conn->evsql, conn);
-}
-
-/*
- * Got one result on this evpq connection.
- */
-static void _evsql_evpq_result (struct evpq_conn *_conn, PGresult *result, void *arg) {
- struct evsql_conn *conn = arg;
- struct evsql_query *query = conn->query;
-
- assert(query != NULL);
-
- // if we get multiple results, only return the first one
- if (query->result.evpq) {
- WARNING("[evsql] evpq query returned multiple results, discarding previous one");
-
- PQclear(query->result.evpq); query->result.evpq = NULL;
- }
-
- // remember the result
- query->result.evpq = result;
-}
-
-/*
- * No more results for this query.
- */
-static void _evsql_evpq_done (struct evpq_conn *_conn, void *arg) {
- struct evsql_conn *conn = arg;
- struct evsql_query *query = conn->query;
- struct evsql_result_info res; ZINIT(res);
-
- assert(query != NULL);
-
- // set up the result_info
- res.evsql = conn->evsql;
- res.trans = conn->trans;
-
- 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");
-
- 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 {
- res.error = 0;
- res.result.pq = query->result.evpq;
-
- }
-
- // de-associate the query from the connection
- conn->query = NULL;
-
- // how we handle query completion depends on if we're a transaction or not
- if (conn->trans) {
- // we can deassign the trans's query
- conn->trans->query = NULL;
-
- // was an abort?
- if (!query->cb_fn)
- // notify the user that the transaction query has been aborted
- conn->trans->ready_fn(conn->trans, conn->trans->cb_arg);
-
- // then hand the query to the user
- _evsql_query_done(query, &res);
-
- } else {
- // a transactionless query, so just finish it off and pump any other waiting ones
- _evsql_query_done(query, &res);
-
- // pump the next one
- _evsql_pump(conn->evsql, conn);
- }
-}
-
-/*
- * The connection failed.
- */
-static void _evsql_evpq_failure (struct evpq_conn *_conn, void *arg) {
- struct evsql_conn *conn = arg;
-
- // just fail the conn
- _evsql_conn_fail(conn);
-}
-
-/*
- * Our evpq behaviour
- */
-static struct evpq_callback_info _evsql_evpq_cb_info = {
- .fn_connected = _evsql_evpq_connected,
- .fn_result = _evsql_evpq_result,
- .fn_done = _evsql_evpq_done,
- .fn_failure = _evsql_evpq_failure,
-};
-
-/*
- * Allocate the generic evsql context.
- */
-static struct evsql *_evsql_new_base (struct event_base *ev_base, evsql_error_cb error_fn, void *cb_arg) {
- struct evsql *evsql = NULL;
-
- // allocate it
- if ((evsql = calloc(1, sizeof(*evsql))) == NULL)
- ERROR("calloc");
-
- // store
- evsql->ev_base = ev_base;
- evsql->error_fn = error_fn;
- evsql->cb_arg = cb_arg;
-
- // init
- LIST_INIT(&evsql->conn_list);
- TAILQ_INIT(&evsql->query_queue);
-
- // done
- return evsql;
-
-error:
- return NULL;
-}
-
-/*
- * Start a new connection and add it to the list, it won't be ready until _evsql_evpq_connected is called
- */
-static struct evsql_conn *_evsql_conn_new (struct evsql *evsql) {
- struct evsql_conn *conn = NULL;
-
- // allocate
- if ((conn = calloc(1, sizeof(*conn))) == NULL)
- ERROR("calloc");
-
- // init
- conn->evsql = evsql;
-
- // connect the engine
- switch (evsql->type) {
- case EVSQL_EVPQ:
- if ((conn->engine.evpq = evpq_connect(evsql->ev_base, evsql->engine_conf.evpq, _evsql_evpq_cb_info, conn)) == NULL)
- goto error;
-
- break;
-
- default:
- FATAL("evsql->type");
- }
-
- // add it to the list
- LIST_INSERT_HEAD(&evsql->conn_list, conn, entry);
-
- // success
- return conn;
-
-error:
- free(conn);
-
- return NULL;
-}
-
-struct evsql *evsql_new_pq (struct event_base *ev_base, const char *pq_conninfo, evsql_error_cb error_fn, void *cb_arg) {
- struct evsql *evsql = NULL;
-
- // base init
- if ((evsql = _evsql_new_base (ev_base, error_fn, cb_arg)) == NULL)
- goto error;
-
- // store conf
- evsql->engine_conf.evpq = pq_conninfo;
-
- // pre-create one connection
- if (_evsql_conn_new(evsql) == NULL)
- goto error;
-
- // done
- return evsql;
-
-error:
- // XXX: more complicated than this?
- free(evsql);
-
- return NULL;
-}
-
-/*
- * Checks if the connection is already allocated for some other trans/query.
- *
- * Returns:
- * 0 connection idle, can be allocated
- * >1 connection busy
- */
-static int _evsql_conn_busy (struct evsql_conn *conn) {
- // transactions get the connection to themselves
- if (conn->trans)
- return 1;
-
- // if it has a query assigned, it's busy
- if (conn->query)
- return 1;
-
- // otherwise, it's all idle
- return 0;
-}
-
-/*
- * Checks if the connection is ready for use (i.e. _evsql_evpq_connected was called).
- *
- * The connection should not already have a query running.
- *
- * Returns
- * <0 the connection is not valid (failed, query in progress)
- * 0 the connection is still pending, and will become ready at some point
- * >0 it's ready
- */
-static int _evsql_conn_ready (struct evsql_conn *conn) {
- switch (conn->evsql->type) {
- case EVSQL_EVPQ: {
- enum evpq_state state = evpq_state(conn->engine.evpq);
-
- switch (state) {
- case EVPQ_CONNECT:
- return 0;
-
- case EVPQ_CONNECTED:
- return 1;
-
- case EVPQ_QUERY:
- case EVPQ_INIT:
- case EVPQ_FAILURE:
- return -1;
-
- default:
- FATAL("evpq_state: %d", state);
- }
-
- }
-
- default:
- FATAL("evsql->type: %d", conn->evsql->type);
- }
-}
-
-/*
- * Allocate a connection for use and return it via *conn_ptr, or if may_queue is nonzero and the connection pool is
- * getting full, return NULL (query should be queued).
- *
- * Note that the returned connection might not be ready for use yet (if we created a new one, see _evsql_conn_ready).
- *
- * Returns zero if a connection was found or the request should be queued, or nonzero if something failed and the
- * request should be dropped.
- */
-static int _evsql_conn_get (struct evsql *evsql, struct evsql_conn **conn_ptr, int may_queue) {
- int have_nontrans = 0;
- *conn_ptr = NULL;
-
- // find a connection that isn't busy and is ready (unless the query queue is empty).
- LIST_FOREACH(*conn_ptr, &evsql->conn_list, entry) {
- // we can only have a query enqueue itself if there is a non-trans conn it can later use
- if (!(*conn_ptr)->trans)
- have_nontrans = 1;
-
- // skip busy conns always
- if (_evsql_conn_busy(*conn_ptr))
- continue;
-
- // accept pending conns as long as there are NO enqueued queries (might cause deadlock otherwise)
- if (_evsql_conn_ready(*conn_ptr) == 0 && TAILQ_EMPTY(&evsql->query_queue))
- break;
-
- // accept conns that are in a fully ready state
- if (_evsql_conn_ready(*conn_ptr) > 0)
- break;
- }
-
- // if we found an idle connection, we can just return that right away
- if (*conn_ptr)
- return 0;
-
- // return NULL if may_queue and we have a non-trans conn that we can, at some point, use
- if (may_queue && have_nontrans)
- return 0;
-
- // we need to open a new connection
- if ((*conn_ptr = _evsql_conn_new(evsql)) == NULL)
- goto error;
-
- // good
- return 0;
-error:
- return -1;
-}
-
-struct evsql_trans *evsql_trans (struct evsql *evsql, enum evsql_trans_type type, evsql_trans_error_cb error_fn, evsql_trans_ready_cb ready_fn, evsql_trans_done_cb done_fn, void *cb_arg) {
- struct evsql_trans *trans = NULL;
-
- // allocate it
- if ((trans = calloc(1, sizeof(*trans))) == NULL)
- ERROR("calloc");
-
- // store
- trans->evsql = evsql;
- trans->ready_fn = ready_fn;
- trans->done_fn = done_fn;
- trans->cb_arg = cb_arg;
- trans->type = type;
-
- // find a connection
- if (_evsql_conn_get(evsql, &trans->conn, 0))
- ERROR("_evsql_conn_get");
-
- // associate the conn
- trans->conn->trans = trans;
-
- // is it already ready?
- if (_evsql_conn_ready(trans->conn) > 0) {
- // call _evsql_trans_conn_ready directly, it will handle cleanup (silently, !error_fn)
- if (_evsql_trans_conn_ready(evsql, trans)) {
- // return NULL directly
- return NULL;
- }
-
- } else {
- // otherwise, wait for the conn to be ready
-
- }
-
- // and let it pass errors to the user
- trans->error_fn = error_fn;
-
- // ok
- return trans;
-
-error:
- free(trans);
-
- return NULL;
-}
-
-/*
- * Validate and allocate the basic stuff for a new query.
- */
-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;
-
- // if it's part of a trans, then make sure the trans is idle
- if (trans && trans->query)
- ERROR("transaction is busy");
-
- // allocate it
- if ((query = calloc(1, sizeof(*query))) == NULL)
- ERROR("calloc");
-
- // store
- query->cb_fn = query_fn;
- query->cb_arg = cb_arg;
-
- // success
- return query;
-
-error:
- 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) {
- // it's an in-transaction query
- assert(trans->query == NULL);
-
- // assign the query
- trans->query = query;
-
- // execute directly
- if (_evsql_query_exec(trans->conn, query, command)) {
- // ack, fail the transaction
- _evsql_trans_fail(trans);
-
- // caller frees query
- goto error;
- }
-
- } else {
- struct evsql_conn *conn;
-
- // find an idle connection
- if ((_evsql_conn_get(evsql, &conn, 1)))
- ERROR("couldn't allocate a connection for the query");
-
- // we must enqueue if no idle conn or the conn is not yet ready
- if (conn && _evsql_conn_ready(conn) > 0) {
- // execute directly
- if (_evsql_query_exec(conn, query, command)) {
- // ack, fail the connection
- _evsql_conn_fail(conn);
-
- // make sure we don't deadlock any queries, but if this query got a conn directly, then we shouldn't
- // have any queries enqueued anyways
- assert(TAILQ_EMPTY(&evsql->query_queue));
-
- // caller frees query
- goto error;
- }
-
- } else {
- // copy the command for later execution
- if ((query->command = strdup(command)) == NULL)
- ERROR("strdup");
-
- // enqueue until some connection pumps the queue
- TAILQ_INSERT_TAIL(&evsql->query_queue, query, entry);
- }
- }
-
- // ok, 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_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;
-
- assert(res->trans);
-
- // check for errors
- if (res->error)
- ERROR("transaction 'COMMIT' failed: %s", evsql_result_error(res));
-
- // transaction is now done
- res->trans->done_fn(res->trans, res->trans->cb_arg);
-
- // release it
- _evsql_trans_release(res->trans);
-
- // success
- return;
-
-error:
- _evsql_trans_fail(res->trans);
-}
-
-int evsql_trans_commit (struct evsql_trans *trans) {
- static const char *sql = "COMMIT TRANSACTION";
-
- if (trans->query)
- ERROR("cannot COMMIT because transaction is still busy");
-
- // query
- if (evsql_query(trans->evsql, trans, sql, _evsql_trans_commit_res, NULL) == NULL)
- goto error;
-
- // mark it as commited in case someone wants to abort it
- trans->has_commit = 1;
-
- // success
- return 0;
-
-error:
- return -1;
-}
-
-void _evsql_trans_rollback_res (const struct evsql_result_info *res, void *arg) {
- (void) arg;
-
- assert(res->trans);
-
- // fail the connection on errors
- if (res->error)
- ERROR("transaction 'ROLLBACK' failed: %s", evsql_result_error(res));
-
- // release it
- _evsql_trans_release(res->trans);
-
- // success
- return;
-
-error:
- // fail the connection too, errors are supressed
- _evsql_trans_fail(res->trans);
-}
-
-/*
- * Used as the ready_fn callback in case of abort, otherwise directly
- */
-void _evsql_trans_rollback (struct evsql_trans *trans, void *unused) {
- static const char *sql = "ROLLBACK TRANSACTION";
-
- (void) unused;
-
- // query
- if (evsql_query(trans->evsql, trans, sql, _evsql_trans_rollback_res, NULL) == NULL) {
- // fail the transaction/connection
- _evsql_trans_fail(trans);
- }
-
-}
-
-void evsql_trans_abort (struct evsql_trans *trans) {
- // supress errors
- trans->error_fn = NULL;
-
- if (trans->has_commit) {
- // abort after commit doesn't make sense
- FATAL("transaction was already commited");
- }
-
- if (trans->query) {
- // gah, some query is running
- WARNING("aborting pending query");
-
- // prepare to rollback once complete
- trans->ready_fn = _evsql_trans_rollback;
-
- // abort
- evsql_query_abort(trans, trans->query);
-
- } else {
- // just rollback directly
- _evsql_trans_rollback(trans, NULL);
-
- }
-}
-
--- a/src/evsql/evsql.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,140 +0,0 @@
-#ifndef EVSQL_INTERNAL_H
-#define EVSQL_INTERNAL_H
-
-/*
- * Internal interfaces
- */
-
-#include <sys/queue.h>
-
-#include <event2/event.h>
-
-#include "../evsql.h"
-#include "../evpq.h"
-
-/*
- * The engine type
- */
-enum evsql_type {
- EVSQL_EVPQ, // evpq
-};
-
-/*
- * Contains the type, engine configuration, list of connections and waiting query queue.
- */
-struct evsql {
- // what event_base to use
- struct event_base *ev_base;
-
- // what engine we use
- enum evsql_type type;
-
- // callbacks
- evsql_error_cb error_fn;
- void *cb_arg;
-
- // engine-specific connection configuration
- union {
- const char *evpq;
- } engine_conf;
-
- // list of connections that are open
- LIST_HEAD(evsql_conn_list, evsql_conn) conn_list;
-
- // list of queries running or waiting to run
- TAILQ_HEAD(evsql_query_queue, evsql_query) query_queue;
-};
-
-/*
- * A single connection to the server.
- *
- * Contains the engine connection, may have a transaction associated, and may have a query associated.
- */
-struct evsql_conn {
- // evsql we belong to
- struct evsql *evsql;
-
- // engine-specific connection info
- union {
- struct evpq_conn *evpq;
- } engine;
-
- // our position in the conn list
- LIST_ENTRY(evsql_conn) entry;
-
- // are we running a transaction?
- struct evsql_trans *trans;
-
- // are we running a transactionless query?
- struct evsql_query *query;
-};
-
-/*
- * A single transaction.
- *
- * Has a connection associated and possibly a query (which will also be associated with the connection)
- */
-struct evsql_trans {
- // our evsql_conn/evsql
- struct evsql *evsql;
- struct evsql_conn *conn;
-
- // callbacks
- evsql_trans_error_cb error_fn;
- evsql_trans_ready_cb ready_fn;
- evsql_trans_done_cb done_fn;
- void *cb_arg;
-
- // the transaction type
- enum evsql_trans_type type;
-
- // has evsql_trans_commit be called?
- int has_commit : 1;
-
- // our current query
- struct evsql_query *query;
-
-};
-
-/*
- * A single query.
- *
- * Has the info needed to exec the query (as these may be queued), and the callback/result info.
- */
-struct evsql_query {
- // the actual SQL query, this may or may not be ours, see _evsql_query_exec
- char *command;
-
- // possible query params
- struct evsql_query_param_info {
- int count;
-
- Oid *types;
- const char **values;
- int *lengths;
- int *formats;
-
- int result_format;
- } params;
-
- // our callback
- evsql_query_cb cb_fn;
- void *cb_arg;
-
- // our position in the query list
- TAILQ_ENTRY(evsql_query) entry;
-
- // the result
- union {
- PGresult *evpq;
- } result;
-};
-
-// maximum length for a 'BEGIN TRANSACTION ...' query
-#define EVSQL_QUERY_BEGIN_BUF 512
-
-// the should the OID of some valid psql type... *ANY* valid psql type, doesn't matter, only used for NULLs
-// 16 = bool in 8.3
-#define EVSQL_PQ_ARBITRARY_TYPE_OID 16
-
-#endif /* EVSQL_INTERNAL_H */
--- a/src/evsql/util.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,331 +0,0 @@
-#include <stdlib.h>
-#include <assert.h>
-
-#include "evsql.h"
-#include "../lib/log.h"
-#include "../lib/misc.h"
-
-#define _PARAM_TYPE_CASE(typenam) case EVSQL_PARAM_ ## typenam: return #typenam
-
-#define _PARAM_VAL_BUF_MAX 120
-#define _PARAM_VAL_CASE(typenam, ...) case EVSQL_PARAM_ ## typenam: if (param->data_raw) ret = snprintf(buf, _PARAM_VAL_BUF_MAX, __VA_ARGS__); else return "(null)"; break
-
-const char *evsql_param_type (const struct evsql_query_param *param) {
- switch (param->type) {
- _PARAM_TYPE_CASE (INVALID );
- _PARAM_TYPE_CASE (NULL_ );
- _PARAM_TYPE_CASE (BINARY );
- _PARAM_TYPE_CASE (STRING );
- _PARAM_TYPE_CASE (UINT16 );
- _PARAM_TYPE_CASE (UINT32 );
- _PARAM_TYPE_CASE (UINT64 );
- default: return "???";
- }
-}
-
-
-static const char *evsql_param_val (const struct evsql_query_param *param) {
- static char buf[_PARAM_VAL_BUF_MAX];
- int ret;
-
- switch (param->type) {
- _PARAM_VAL_CASE (INVALID, "???" );
- _PARAM_VAL_CASE (NULL_, "(null)" );
- _PARAM_VAL_CASE (BINARY, "%zu:%s", param->length, "..." );
- _PARAM_VAL_CASE (STRING, "%s", param->data_raw );
- _PARAM_VAL_CASE (UINT16, "%hu", (unsigned short int) ntohs(param->data.uint16) );
- _PARAM_VAL_CASE (UINT32, "%lu", (unsigned long int) ntohl(param->data.uint32) );
- _PARAM_VAL_CASE (UINT64, "%llu", (unsigned long long int) ntohq(param->data.uint64) );
- default: return "???";
- }
-
- return buf;
-}
-
-int evsql_params_clear (struct evsql_query_params *params) {
- struct evsql_query_param *param;
-
- for (param = params->list; param->type; param++)
- param->data_raw = NULL;
-
- return 0;
-}
-
-int evsql_param_null (struct evsql_query_params *params, size_t param) {
- struct evsql_query_param *p = ¶ms->list[param];
-
- p->data_raw = NULL;
-
- return 0;
-}
-
-int evsql_param_binary (struct evsql_query_params *params, size_t param, const char *ptr, size_t len) {
- struct evsql_query_param *p = ¶ms->list[param];
-
- assert(p->type == EVSQL_PARAM_BINARY);
-
- p->data_raw = ptr;
- p->length = len;
-
- return 0;
-}
-
-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_uint16 (struct evsql_query_params *params, size_t param, uint16_t uval) {
- struct evsql_query_param *p = ¶ms->list[param];
-
- assert(p->type == EVSQL_PARAM_UINT16);
-
- p->data.uint16 = htons(uval);
- p->data_raw = (const char *) &p->data.uint16;
- p->length = sizeof(uval);
-
- 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;
-}
-
-void evsql_query_debug (const char *sql, const struct evsql_query_params *params) {
- const struct evsql_query_param *param;
- size_t param_count = 0, idx = 0;
-
- // count the params
- for (param = params->list; param->type; param++)
- param_count++;
-
- DEBUG("sql: %s", sql);
- DEBUG("params: %zu", param_count);
-
- for (param = params->list; param->type; param++) {
- DEBUG("\t%2zu : %8s = %s", ++idx, evsql_param_type(param), evsql_param_val(param));
- }
-}
-
-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) {
- *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));
-
- *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_binlen (const struct evsql_result_info *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))
- goto error;
-
- if (*ptr == NULL) {
- assert(nullok);
- return 0;
- }
-
- if (size && real_size != size)
- ERROR("[%zu:%zu] field size mismatch: %zu -> %zu", row, col, size, real_size);
-
- return 0;
-
-error:
- return -1;
-}
-
-int evsql_result_string (const struct evsql_result_info *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))
- goto error;
-
- assert(real_size == strlen(*ptr));
-
- return 0;
-
-error:
- return -1;
-}
-
-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_binlen(res, row, col, &data, sizeof(*uval), nullok))
- goto error;
-
- if (!data)
- return 0;
-
- 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_binlen(res, row, col, &data, sizeof(*uval), nullok))
- goto error;
-
- if (!data)
- return 0;
-
- 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_binlen(res, row, col, &data, sizeof(*uval), nullok))
- goto error;
-
- if (!data)
- return 0;
-
- 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");
- }
-}
-
-const char *evsql_conn_error (struct evsql_conn *conn) {
- switch (conn->evsql->type) {
- case EVSQL_EVPQ:
- if (!conn->engine.evpq)
- return "unknown error (no conn)";
-
- return evpq_error_message(conn->engine.evpq);
-
- default:
- FATAL("res->evsql->type");
- }
-}
-
-const char *evsql_trans_error (struct evsql_trans *trans) {
- if (trans->conn == NULL)
- return "unknown error (no trans conn)";
-
- return evsql_conn_error(trans->conn);
-}
-
--- a/src/hello.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,247 +0,0 @@
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-
-#include <event2/event.h>
-#include <fuse/fuse_opt.h>
-
-#include "lib/log.h"
-#include "lib/math.h"
-#include "lib/signals.h"
-#include "evfuse.h"
-#include "dirbuf.h"
-
-const char *file_name = "hello";
-const char *file_data = "Hello World\n";
-
-static struct hello {
- struct event_base *ev_base;
-
- struct signals *signals;
-
- struct evfuse *ev_fuse;
-
-} ctx;
-
-void hello_init (void *userdata, struct fuse_conn_info *conn) {
- INFO("[hello.init] userdata=%p, conn=%p", userdata, conn);
-}
-
-void hello_destroy (void *userdata) {
- INFO("[hello.destroy] userdata=%p", userdata);
-}
-
-void hello_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) {
- struct fuse_entry_param e;
-
- INFO("[hello.lookup] (uid=%d, pid=%d) parent=%lu name=%s", fuse_req_ctx(req)->uid, fuse_req_ctx(req)->pid, parent, name);
-
- // the world is flat
- if (parent != 1 || strcmp(name, file_name)) {
- fuse_reply_err(req, ENOENT);
-
- return;
- }
-
- // set up the entry
- memset(&e, 0, sizeof(e));
- e.ino = 2;
- e.attr_timeout = 1.0;
- e.entry_timeout = 1.0;
- e.attr.st_mode = S_IFREG | 0444;
- e.attr.st_nlink = 1;
- e.attr.st_size = strlen(file_data);
-
- // reply
- fuse_reply_entry(req, &e);
-}
-
-void hello_getattr (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
- struct stat stbuf;
-
- INFO("[hello.getattr] (uid=%d, pid=%d) ino=%lu, fi=%p", fuse_req_ctx(req)->uid, fuse_req_ctx(req)->pid, ino, fi);
-
- memset(&stbuf, 0, sizeof(stbuf));
-
- // the root dir, or the file?
- if (ino == 1) {
- stbuf.st_mode = S_IFDIR | 0555;
- stbuf.st_nlink = 2;
-
- } else if (ino == 2) {
- stbuf.st_mode = S_IFREG | 0444;
- stbuf.st_nlink = 1;
- stbuf.st_size = strlen(file_data);
-
- } else {
- fuse_reply_err(req, ENOENT);
- return;
- }
-
- // reply
- fuse_reply_attr(req, &stbuf, 1.0);
-}
-
-
-void hello_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
- int err = 0;
- struct dirbuf buf;
-
- INFO("[hello.readdir] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi);
-
- // there exists only one dir
- if (ino != 1) {
- fuse_reply_err(req, ENOTDIR);
- return;
- }
-
- // fill in the dirbuf
- if (dirbuf_init(&buf, size, off))
- ERROR("failed to init dirbuf");
-
- err = dirbuf_add(req, &buf, 0, 1, ".", 1, S_IFDIR )
- || dirbuf_add(req, &buf, 1, 2, "..", 1, S_IFDIR )
- || dirbuf_add(req, &buf, 2, 3, file_name, 2, S_IFREG );
-
- if (err < 0)
- ERROR("failed to add dirents to buf");
-
- // send it
- if ((err = -dirbuf_done(req, &buf)))
- EERROR(-err, "failed to send buf");
-
- // success
- return;
-
-error:
- if ((err = fuse_reply_err(req, err ? err : EIO)))
- EWARNING(err, "failed to send error reply");
-}
-
-void hello_open (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
- int err = 0;
-
- INFO("[hello.open] ino=%lu, fi=%p, fi->flags=%08X", ino, fi, fi->flags);
-
- if (ino != 2) {
- // must open our only file, not the dir
- fuse_reply_err(req, ino == 1 ? EISDIR : ENOENT);
- return;
-
- } else if ((fi->flags & 0x03) != O_RDONLY) {
- // "permission denied"
- fuse_reply_err(req, EACCES);
- return;
- }
-
- // XXX: update fi stuff?
-
- // open it!
- if ((err = fuse_reply_open(req, fi)))
- EERROR(err, "fuse_reply_open");
-
- // success
- return;
-
-error:
- if ((err = fuse_reply_err(req, err ? err : EIO)))
- EWARNING(err, "failed to send error reply");
-}
-
-void hello_read (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
- int err = 0;
-
- // fi is unused
- (void) fi;
-
- INFO("[hello.read] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi);
-
- if (ino != 2) {
- // EEK!
- FATAL("wrong inode");
- }
-
- if (off >= strlen(file_data)) {
- // offset is out-of-file, so return EOF
- err = fuse_reply_buf(req, NULL, 0);
-
- } else {
- // reply with the requested file data
- err = fuse_reply_buf(req, file_data + off, MIN(strlen(file_data) - off, size));
- }
-
- // reply
- if (err)
- PERROR("fuse_reply_buf");
-
- // success
- return;
-
-error:
- if ((err = fuse_reply_err(req, err ? err : EIO)))
- EWARNING(err, "failed to send error reply");
-}
-
-void hello_getxattr (fuse_req_t req, fuse_ino_t ino, const char *name, size_t size) {
- INFO("[hello.getxattr] ino=%lu, name=`%s', size=%zu", ino, name, size);
-
- fuse_reply_err(req, ENOSYS);
-}
-
-struct fuse_lowlevel_ops hello_llops = {
- .init = &hello_init,
- .destroy = &hello_destroy,
-
- .lookup = &hello_lookup,
- .getattr = &hello_getattr,
-
- .open = &hello_open,
-
- .read = &hello_read,
-
- .readdir = &hello_readdir,
-
- .getxattr = hello_getxattr,
-};
-
-
-int main (int argc, char **argv) {
- struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv);
-
- // zero
- memset(&ctx, 0, sizeof(ctx));
-
- // 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 fuse
- if ((ctx.ev_fuse = evfuse_new(ctx.ev_base, &fuse_args, &hello_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_free(ctx.ev_fuse);
-
- 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/helloworld.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-#include <stdio.h>
-
-int main (void) {
- printf("Hello World\n");
-
- return 0;
-}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/internal.h Sun Mar 08 00:19:12 2009 +0200
@@ -0,0 +1,140 @@
+#ifndef EVSQL_INTERNAL_H
+#define EVSQL_INTERNAL_H
+
+/*
+ * Internal interfaces
+ */
+
+#include <sys/queue.h>
+
+#include <event2/event.h>
+
+#include "evsql.h"
+#include "evpq.h"
+
+/*
+ * The engine type
+ */
+enum evsql_type {
+ EVSQL_EVPQ, // evpq
+};
+
+/*
+ * Contains the type, engine configuration, list of connections and waiting query queue.
+ */
+struct evsql {
+ // what event_base to use
+ struct event_base *ev_base;
+
+ // what engine we use
+ enum evsql_type type;
+
+ // callbacks
+ evsql_error_cb error_fn;
+ void *cb_arg;
+
+ // engine-specific connection configuration
+ union {
+ const char *evpq;
+ } engine_conf;
+
+ // list of connections that are open
+ LIST_HEAD(evsql_conn_list, evsql_conn) conn_list;
+
+ // list of queries running or waiting to run
+ TAILQ_HEAD(evsql_query_queue, evsql_query) query_queue;
+};
+
+/*
+ * A single connection to the server.
+ *
+ * Contains the engine connection, may have a transaction associated, and may have a query associated.
+ */
+struct evsql_conn {
+ // evsql we belong to
+ struct evsql *evsql;
+
+ // engine-specific connection info
+ union {
+ struct evpq_conn *evpq;
+ } engine;
+
+ // our position in the conn list
+ LIST_ENTRY(evsql_conn) entry;
+
+ // are we running a transaction?
+ struct evsql_trans *trans;
+
+ // are we running a transactionless query?
+ struct evsql_query *query;
+};
+
+/*
+ * A single transaction.
+ *
+ * Has a connection associated and possibly a query (which will also be associated with the connection)
+ */
+struct evsql_trans {
+ // our evsql_conn/evsql
+ struct evsql *evsql;
+ struct evsql_conn *conn;
+
+ // callbacks
+ evsql_trans_error_cb error_fn;
+ evsql_trans_ready_cb ready_fn;
+ evsql_trans_done_cb done_fn;
+ void *cb_arg;
+
+ // the transaction type
+ enum evsql_trans_type type;
+
+ // has evsql_trans_commit be called?
+ int has_commit : 1;
+
+ // our current query
+ struct evsql_query *query;
+
+};
+
+/*
+ * A single query.
+ *
+ * Has the info needed to exec the query (as these may be queued), and the callback/result info.
+ */
+struct evsql_query {
+ // the actual SQL query, this may or may not be ours, see _evsql_query_exec
+ char *command;
+
+ // possible query params
+ struct evsql_query_param_info {
+ int count;
+
+ Oid *types;
+ const char **values;
+ int *lengths;
+ int *formats;
+
+ int result_format;
+ } params;
+
+ // our callback
+ evsql_query_cb cb_fn;
+ void *cb_arg;
+
+ // our position in the query list
+ TAILQ_ENTRY(evsql_query) entry;
+
+ // the result
+ union {
+ PGresult *evpq;
+ } result;
+};
+
+// maximum length for a 'BEGIN TRANSACTION ...' query
+#define EVSQL_QUERY_BEGIN_BUF 512
+
+// the should the OID of some valid psql type... *ANY* valid psql type, doesn't matter, only used for NULLs
+// 16 = bool in 8.3
+#define EVSQL_PQ_ARBITRARY_TYPE_OID 16
+
+#endif /* EVSQL_INTERNAL_H */
--- a/src/lib/lex.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,181 +0,0 @@
-
-#include <stdlib.h>
-
-#include "lex.h"
-#include "error.h"
-#include "log.h"
-
-#define INITIAL_BUF_SIZE 4096
-
-int lexer (const struct lex *lex, const char *input, void *arg) {
- // handling error returns
- int err = -1, cb_err;
-
- // token buffer
- char *buf = NULL, *buf_ptr;
- size_t buf_size = INITIAL_BUF_SIZE;
-
- // state
- int prev_state = LEX_INITIAL, cur_state = lex->initial_state, next_state = LEX_INITIAL;
-
- // input chars
- const char *c = input;
-
- // lookups
- const struct lex_transition *trans = NULL;
-
- // allocate the buffer
- if ((buf = malloc(sizeof(char) * buf_size)) == NULL)
- goto error;
-
- // set buf_ptr initial position
- buf_ptr = buf;
-
- // clear input
- DEBUG("*cough*");
- DEBUGN("%s", "");
-
- // process input
- do {
- if (*c) {
- // look up the next state
- for (trans = lex->state_list[cur_state - 1].trans_list; trans->next_state > 0 || trans->flags; trans++) {
- // accept defaults
- if (trans->flags & LEX_TRANS_DEFAULT)
- break;
-
- // disregard non-matches
- if (trans->left > *c || *c > trans->right)
- continue;
-
- // abort on invalids
- if (trans->flags & LEX_TRANS_INVALID) {
- goto error;
-
- } else {
- // accept it
- break;
- }
- }
-
- // did we find a transition with a valid next state?
- if (!(next_state = trans->next_state))
- goto error;
-
- // call the char handler
- if (lex->char_fn && (cb_err = lex->char_fn(*c, cur_state, next_state, arg)))
- goto error;
-
- } else {
- // EOF!
- next_state = LEX_EOF;
-
- // is cur_state a valid end state?
- if (!(lex->state_list[cur_state - 1].flags & LEX_STATE_END))
- goto error;
-
- // note: we don't pass the NUL byte to the char handler
- }
-
- // if this char is part of the next token...
- if (next_state != cur_state) {
- // terminate the buffer and reset buf_ptr
- *buf_ptr = 0; buf_ptr = buf;
-
- // dump state transitions
- DEBUGF("\n\t%25s -> %25s -> %25s",
- LEX_STATE_NAME(lex, prev_state),
- LEX_STATE_NAME(lex, cur_state),
- LEX_STATE_NAME(lex, next_state)
- );
-
- // pass in the complete token to the handler
- if (lex->token_fn && (cb_err = lex->token_fn(cur_state, buf, next_state, prev_state, arg)))
- goto error;
-
- // update states
- prev_state = cur_state;
- cur_state = next_state;
- next_state = LEX_INITIAL;
- }
-
- // dump chars
- if (next_state == LEX_INITIAL)
- DEBUGN("%c", *c);
- else
- DEBUGNF("%c", *c);
-
- // store this char in the buffer
- *(buf_ptr++) = *c;
-
- // grow the buffer if needed
- if (buf_ptr - buf >= buf_size) {
- // remember the offset, as buf_ptr might get invalidated if buf is moved
- size_t buf_offset = buf_ptr - buf;
-
- // calc new size
- buf_size *= 2;
-
- // grow/move
- if ((buf = realloc(buf, buf_size)) == NULL)
- goto error;
-
- // fix buf_ptr
- buf_ptr = buf + buf_offset;
- }
- } while (*(c++));
-
- // call the end handler
- if (lex->end_fn && (cb_err = lex->end_fn(cur_state, arg)))
- goto error;
-
- // successfully parsed!
- err = 0;
-
-error:
- DEBUGNF("\n");
-
- if (cb_err)
- err = cb_err;
-
- // dump debug info on error
- if (err) {
- const char *cc;
-
- // figure out the error
- if (!buf)
- WARNING("malloc/realloc");
-
- else if (trans && trans->flags & LEX_TRANS_INVALID)
- WARNING("hit invalid transition match");
-
- else if (!next_state)
- WARNING("no valid transition found");
-
- else if (next_state == LEX_EOF && !(lex->state_list[cur_state - 1].flags & LEX_STATE_END))
- WARNING("invalid end state");
-
- else
- WARNING("unknown error condition (!?)");
-
- DEBUG("%s", input);
- DEBUGN("%s", "");
-
- for (cc = input; cc < c; cc++)
- DEBUGNF(" ");
-
- DEBUGF("^\t%s -> %s -> %s",
- LEX_STATE_NAME(lex, prev_state),
- LEX_STATE_NAME(lex, cur_state),
- LEX_STATE_NAME(lex, next_state)
- );
- }
-
- // free stuff
- free(buf);
-
- // return
- return err;
-}
-
-
--- a/src/lib/lex.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,143 +0,0 @@
-#ifndef LIB_LEXER_H
-#define LIB_LEXER_H
-
-/*
- * Simple FSM lexing
- *
- * The lexer is implemented as a Finite State Machine, consisting for a number of states, which then contain a set of
- * transitions, which move the lexer from state to state based on each char of input at a time.
- *
- * Whenever the state changes, the token callback is triggered with the collected token data.
- */
-
-#include <sys/types.h>
-
-/*
- * Transition flags
- */
-enum lex_transition_flags {
- LEX_TRANS_DEFAULT = 0x01,
- /* not supported
- LEX_TRANS_FINAL = 0x02, */
- LEX_TRANS_INVALID = 0x04,
-};
-
-/*
- * A transition from one state to another.
- */
-struct lex_transition {
- // applies to chars [left, right]
- char left, right;
-
- // flags from lex_transition_flags
- char flags;
-
- // next state to enter
- int next_state;
-};
-
-/*
- * State flags
- */
-enum lex_state_flags {
- LEX_STATE_END = 0x01,
-};
-
-/*
- * A state
- */
-struct lex_state {
- // the state name (for debugging)
- const char *name;
-
- // flags from lex_state_flags
- char flags;
-
- // list of transitions for this state, terminated by a transition with next_state=0
- struct lex_transition trans_list[15];
-};
-
-/*
- * Special states, these are all defined as zero
- */
-
-// shows up in token_fn as the value of next_token when this_token is the last token.
-#define LEX_EOF 0
-
-// shows up as the initial value of prev_token
-#define LEX_INITIAL 0
-
-/*
- * Lex machine
- */
-struct lex {
- /*
- * Core token handler. Everytime a full token is lexed (i.e. the state changes), this will be called.
- * `this_token` represents the full token that was parsed, and `token_data` is the token's value. `next_token`
- * is the state that terminated this token, and `prev_token` was the token before this one.
- *
- * `token_data` is a buffer allocated by the lexer that the actual input data is copied into. Thence, it can be
- * modified, as its contents will be replaced by the next token. Hence, if you need to keep hold of it, copy it.
- *
- * Return zero to have lexing continue, nonzero to stop lexing.
- */
- int (*token_fn) (int this_token, char *token_data, int next_token, int prev_token, void *arg);
-
- /*
- * Called on every char handled by the lexer.
- *
- * The NUL byte at the end of the input string is not passed to char_fn (why not?).
- *
- * Return zero to have lexing continue, nonzero to stop lexing.
- */
- int (*char_fn) (char token_char, int from_token, int to_token, void *arg);
-
- /*
- * Called when the end of input has been reached, `last_token` is the state that we terminated in.
- *
- * Return zero to indiciate that the input was valid, nonzero to indicate an error.
- */
- int (*end_fn) (int last_token, void *arg);
-
- // number of states
- size_t state_count;
-
- // initial state
- int initial_state;
-
- // array of lex_states, indexable by the state id.
- struct lex_state state_list[];
-};
-
-/*
- * Helper macros for building the state_list
- */
-#define LEX_STATE(enum_val) { #enum_val, 0,
-#define LEX_STATE_END(enum_val) { #enum_val, LEX_STATE_END,
-
- #define LEX_CHAR(c, to) { c, c, 0, to }
- #define LEX_RANGE(l, r, to) { l, r, 0, to }
- #define LEX_ALPHA(to) LEX_RANGE('a', 'z', to), LEX_RANGE('A', 'Z', to)
- #define LEX_NUMBER(to) LEX_RANGE('0', '9', to)
- #define LEX_ALNUM(to) LEX_ALPHA(to), LEX_NUMBER(to), LEX_CHAR('-', to), LEX_CHAR('_', to)
- #define LEX_WHITESPACE(to) LEX_CHAR(' ', to), LEX_CHAR('\n', to), LEX_CHAR('\t', to)
- #define LEX_INVALID(c) { c, c, LEX_TRANS_INVALID, 0 }
-
- #define LEX_DEFAULT(to) { 0, 0, LEX_TRANS_DEFAULT, to } \
- }
- #define LEX_END { 0, 0, 0, 0 } \
- }
-
-/*
- * Helpers for handling states
- */
-#define LEX_STATE_NAME(lex, state) ((state) ? (lex)->state_list[(state) - 1].name : "...")
-
-/*
- * Lex it!
- *
- * Return zero to indiciate that the input was valid, nonzero otherwise.
- */
-int lexer (const struct lex *lex, const char *input, void *arg);
-
-#endif /* LIB_LEXER_H */
--- a/src/lib/signals.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,109 +0,0 @@
-#define _GNU_SOURCE
-#include <signal.h>
-#include <string.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include "signals.h"
-#include "log.h"
-
-struct signals {
- struct event_base *ev_base;
-
- struct signal {
- struct event *ev;
- } sig_list[MAX_SIGNALS];
-
- int sig_count;
-};
-
-void signals_loopexit (int signal, short event, void *arg) {
- struct signals *signals = arg;
-
- INFO("[signal] caught %s: exiting the event loop", strsignal(signal));
-
- if (event_base_loopexit(signals->ev_base, NULL))
- FATAL("event_base_loopexit");
-}
-
-void signals_ignore (int signal, short event, void *arg) {
- struct signals *signals = arg;
-
- (void) signals;
-
- /* ignore */
-}
-
-struct signals *signals_alloc (struct event_base *ev_base) {
- struct signals *signals = NULL;
-
- if ((signals = calloc(1, sizeof(*signals))) == NULL)
- ERROR("calloc");
-
- // simple attributes
- signals->ev_base = ev_base;
-
- // done
- return signals;
-
-error:
- return NULL;
-}
-
-int signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *)) {
- struct signal *sig_info;
-
- // find our sig_info
- assert(signals->sig_count < MAX_SIGNALS);
- sig_info = &signals->sig_list[signals->sig_count++];
-
- // set up the libevent signal events
- if ((sig_info->ev = signal_new(signals->ev_base, sigval, sig_handler, signals)) == NULL)
- PERROR("signal_new");
-
- if (signal_add(sig_info->ev, NULL))
- PERROR("signal_add");
-
- // success
- return 0;
-
-error:
- return -1;
-}
-
-struct signals *signals_default (struct event_base *ev_base) {
- struct signals *signals = NULL;
-
- // alloc signals
- if ((signals = signals_alloc(ev_base)) == NULL)
- return NULL;
-
- // add the set of default signals
- if ( signals_add(signals, SIGPIPE, &signals_ignore)
- || signals_add(signals, SIGINT, &signals_loopexit)
- ) ERROR("signals_add");
-
- // success
- return signals;
-
-error:
- if (signals)
- signals_free(signals);
-
- return NULL;
-}
-
-void signals_free (struct signals *signals) {
- int i;
-
- // free all events
- for (i = 0; i < signals->sig_count; i++) {
- if (signal_del(signals->sig_list[i].ev))
- PWARNING("signal_del");
-
- }
-
- // free the info itself
- free(signals);
-}
-
--- a/src/lib/signals.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-#ifndef LIB_SIGNAL_H
-#define LIB_SIGNAL_H
-
-/*
- * Handle signals in a libevent-sane way
- */
-
-#include <event2/event.h>
-
-/*
- * How many signals we can define actions for
- */
-#define MAX_SIGNALS 8
-
-/*
- * info about a set of signals
- */
-struct signals;
-
-/*
- * Used as a handler for signals that should cause a loopexit.
- */
-void signals_loopexit (int signal, short event, void *arg);
-
-/*
- * Used to receive signals, but discard them.
- */
-void signals_ignore (int signal, short event, void *arg);
-
-/*
- * Allocate a signals struct, acting on the given ev_base.
- *
- * Returns NULL on failure
- */
-struct signals *signals_alloc (struct event_base *ev_base);
-
-/*
- * Add a signal to be handled by the given signals struct with the given handler.
- */
-int signals_add (struct signals *signals, int sigval, void (*sig_handler)(evutil_socket_t, short, void *));
-
-/*
- * Add a set of default signals
- * SIGPIPE signals_ignore
- * SIGINT signals_loopexit
- */
-struct signals *signals_default (struct event_base *ev_base);
-
-/*
- * Free the resources/handlers associated with the given signal handler
- */
-void signals_free (struct signals *signals);
-
-#endif /* LIB_SIGNAL_H */
--- a/src/lib/url.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,587 +0,0 @@
-#define _GNU_SOURCE
-#include <stdlib.h>
-#include <string.h>
-
-#include "url.h"
-#include "lex.h"
-#include "error.h"
-#include "log.h"
-#include "misc.h"
-
-enum url_token {
- URL_INVALID,
-
- URL_BEGIN,
-
- // kludge to resolve ambiguous URL_SCHEME/URL_USERNAME+URL_PASSWORD/URL_HOSTNAME+URL_SERVICE at the beginning
- URL_BEGIN_ALNUM,
- URL_BEGIN_COLON,
-
- URL_SCHEME,
- URL_SCHEME_SEP,
- URL_SCHEME_END_COL,
- URL_SCHEME_END_SLASH1,
- URL_SCHEME_END_SLASH2,
-
- // kludge to resolve ambiguous URL_USERNAME+URL_PASSWORD/URL_HOSTNAME+URL_SERVICE after a scheme
- URL_USERHOST_ALNUM,
- URL_USERHOST_COLON,
- URL_USERHOST_ALNUM2,
-
- URL_USERNAME,
- URL_PASSWORD_SEP,
- URL_PASSWORD,
- URL_USERNAME_END,
-
- URL_HOSTNAME,
-
- URL_SERVICE_SEP,
- URL_SERVICE,
-
- URL_PATH_START,
- URL_PATH,
-
- URL_OPT_START,
- URL_OPT_KEY,
- URL_OPT_EQ,
- URL_OPT_VAL,
- URL_OPT_SEP,
-
- URL_MAX,
-};
-
-/*
- * Parser state
- */
-struct url_state {
- // the URL to parse into
- struct url *url;
-
- // our lookahead-kludge
- const char *alnum, *alnum2;
-
-};
-
-static int _url_append_scheme (struct url *url, const char *data, int copy) {
- if (!url->schema) {
- if ((url->schema = malloc(sizeof(struct url_schema) + (1 * sizeof(const char *)))) == NULL)
- ERROR("malloc");
-
- url->schema->count = 1;
-
- } else {
- url->schema->count++;
-
- // I'm starting to hate flexible array members...
- if ((url->schema = realloc(url->schema, sizeof(struct url_schema) + url->schema->count * sizeof(const char *))) == NULL)
- ERROR("realloc");
- }
-
- if ((url->schema->list[url->schema->count - 1] = copy ? strdup(data) : data) == NULL)
- ERROR("strdup");
-
- // k
- return 0;
-
-error:
- return -1;
-}
-
-static struct url_opt *_url_get_opt (struct url *url, int new) {
- if (!url->opts) {
- if ((url->opts = malloc(sizeof(struct url_opts) + (1 * sizeof(struct url_opt)))) == NULL)
- ERROR("malloc");
-
- url->opts->count = 1;
-
- } else if (new) {
- url->opts->count++;
-
- if ((url->opts = realloc(url->opts, sizeof(struct url_opts) + url->opts->count * sizeof(struct url_opt))) == NULL)
- ERROR("realloc");
- }
-
- // success
- return &url->opts->list[url->opts->count - 1];
-
-error:
- return NULL;
-}
-
-static int _url_append_opt_key (struct url *url, const char *key) {
- struct url_opt *opt;
-
- if ((opt = _url_get_opt(url, 1)) == NULL)
- goto error;
-
- if ((opt->key = strdup(key)) == NULL)
- ERROR("strdup");
-
- opt->value = NULL;
-
- return 0;
-
-error:
- return -1;
-}
-
-static int _url_append_opt_val (struct url *url, const char *value) {
- struct url_opt *opt;
-
- if ((opt = _url_get_opt(url, 0)) == NULL)
- goto error;
-
- if ((opt->value = strdup(value)) == NULL)
- ERROR("strdup");
-
- return 0;
-
-error:
- return -1;
-}
-
-static int url_lex_token (int _this_token, char *token_data, int _next_token, int _prev_token, void *arg);
-
-static struct lex url_lex = {
- .token_fn = url_lex_token,
- .char_fn = NULL,
- .end_fn = NULL,
-
- .state_count = URL_MAX,
- .initial_state = URL_BEGIN,
- .state_list = {
- LEX_STATE ( URL_BEGIN ) {
- LEX_ALNUM ( URL_BEGIN_ALNUM ),
- LEX_CHAR ( ':', URL_SERVICE_SEP ),
- LEX_CHAR ( '/', URL_PATH_START ),
- LEX_CHAR ( '?', URL_OPT_START ),
- LEX_END
- },
-
- // this can be URL_SCHEME, URL_USERNAME or URL_HOSTNAME
- LEX_STATE_END ( URL_BEGIN_ALNUM ) {
- LEX_CHAR ( '+', URL_SCHEME_SEP ), // it was URL_SCHEME
- LEX_CHAR ( ':', URL_BEGIN_COLON ),
- LEX_CHAR ( '@', URL_USERNAME_END ), // it was URL_USERNAME
- LEX_CHAR ( '/', URL_PATH_START ), // it was URL_HOSTNAME
- LEX_CHAR ( '?', URL_OPT_START ), // it was URL_HOSTNAME
- LEX_DEFAULT ( URL_BEGIN_ALNUM )
- },
-
- // this can be URL_SCHEME_END_COL, URL_USERNAME_END or URL_SERVICE_SEP
- LEX_STATE ( URL_BEGIN_COLON ) {
- LEX_CHAR ( '/', URL_SCHEME_END_SLASH1 ), // it was URL_SCHEME
- LEX_ALNUM ( URL_USERHOST_ALNUM2 ),
- LEX_END
- },
-
-
- LEX_STATE ( URL_SCHEME ) {
- LEX_ALNUM ( URL_SCHEME ),
- LEX_CHAR ( '+', URL_SCHEME_SEP ),
- LEX_CHAR ( ':', URL_SCHEME_END_COL ),
- LEX_END
- },
-
- LEX_STATE ( URL_SCHEME_SEP ) {
- LEX_ALNUM ( URL_SCHEME ),
- LEX_END
- },
-
- LEX_STATE ( URL_SCHEME_END_COL ) {
- LEX_CHAR ( '/', URL_SCHEME_END_SLASH1 ),
- LEX_END
- },
-
- LEX_STATE ( URL_SCHEME_END_SLASH1 ) {
- LEX_CHAR ( '/', URL_SCHEME_END_SLASH2 ),
- LEX_END
- },
-
- LEX_STATE_END ( URL_SCHEME_END_SLASH2 ) {
- LEX_ALNUM ( URL_USERHOST_ALNUM ),
- LEX_CHAR ( ':', URL_SERVICE_SEP ),
- LEX_CHAR ( '/', URL_PATH_START ),
- LEX_CHAR ( '?', URL_OPT_START ),
- LEX_END
- },
-
- // this can be URL_USERNAME or URL_HOSTNAME
- LEX_STATE_END ( URL_USERHOST_ALNUM ) {
- LEX_CHAR ( ':', URL_USERHOST_COLON ),
- LEX_CHAR ( '@', URL_USERNAME_END ), // it was URL_USERNAME
- LEX_CHAR ( '/', URL_PATH_START ), // it was URL_HOSTNAME
- LEX_CHAR ( '?', URL_OPT_START ), // it was URL_HOSTNAME
- LEX_DEFAULT ( URL_USERHOST_ALNUM ),
- },
-
- // this can be URL_USERNAME_END or URL_SERVICE_SEP
- LEX_STATE ( URL_USERHOST_COLON ) {
- LEX_ALNUM ( URL_USERHOST_ALNUM2 ),
- LEX_END
- },
-
- // this can be URL_PASSWORD or URL_SERVICE
- LEX_STATE_END ( URL_USERHOST_ALNUM2 ) {
- LEX_CHAR ( '@', URL_USERNAME_END ), // it was URL_PASSSWORD
- LEX_CHAR ( '/', URL_PATH_START ), // it was URL_SERVICE
- LEX_CHAR ( '?', URL_OPT_START ), // it was URL_SERVICE
- LEX_DEFAULT ( URL_USERHOST_ALNUM2 ),
- },
-
- // dummy states, covered by URL_USERHOST_ALNUM/URL_USERHOST_COLON/URL_USERHOST_ALNUM2
- LEX_STATE ( URL_USERNAME ) {
- LEX_END
- },
-
- LEX_STATE ( URL_PASSWORD_SEP ) {
- LEX_END
- },
-
- LEX_STATE ( URL_PASSWORD ) {
- LEX_END
- },
-
-
- LEX_STATE_END ( URL_USERNAME_END ) {
- LEX_ALNUM ( URL_HOSTNAME ),
- LEX_CHAR ( ':', URL_SERVICE_SEP ),
- LEX_CHAR ( '/', URL_PATH_START ),
- LEX_CHAR ( '?', URL_OPT_START ),
- LEX_END
- },
-
-
- LEX_STATE_END ( URL_HOSTNAME ) {
- LEX_ALNUM ( URL_HOSTNAME ),
- LEX_CHAR ( ':', URL_SERVICE_SEP ),
- LEX_CHAR ( '/', URL_PATH_START ),
- LEX_CHAR ( '?', URL_OPT_START ),
- LEX_END
- },
-
-
- LEX_STATE ( URL_SERVICE_SEP ) {
- LEX_ALNUM ( URL_SERVICE ),
- LEX_CHAR ( '/', URL_PATH_START ),
- LEX_CHAR ( '?', URL_OPT_START ),
- LEX_END
- },
-
- LEX_STATE_END ( URL_SERVICE ) {
- LEX_ALNUM ( URL_SERVICE ),
- LEX_CHAR ( '/', URL_PATH_START ),
- LEX_CHAR ( '?', URL_OPT_START ),
- LEX_END
- },
-
-
- LEX_STATE_END ( URL_PATH_START ) {
- LEX_CHAR ( '?', URL_OPT_START ),
- LEX_DEFAULT ( URL_PATH ),
- },
-
- LEX_STATE_END ( URL_PATH ) {
- LEX_CHAR ( '?', URL_OPT_START ),
- LEX_DEFAULT ( URL_PATH ),
- },
-
-
- LEX_STATE_END ( URL_OPT_START ) {
- LEX_CHAR ( '&', URL_OPT_SEP ),
- LEX_INVALID ( '=' ),
- LEX_DEFAULT ( URL_OPT_KEY ),
- },
-
- LEX_STATE_END ( URL_OPT_KEY ) {
- LEX_CHAR ( '&', URL_OPT_SEP ),
- LEX_CHAR ( '=', URL_OPT_EQ ),
- LEX_DEFAULT ( URL_OPT_KEY ),
- },
-
- LEX_STATE_END ( URL_OPT_EQ ) {
- LEX_CHAR ( '&', URL_OPT_SEP ),
- LEX_INVALID ( '=' ),
- LEX_DEFAULT ( URL_OPT_VAL ),
- },
-
- LEX_STATE_END ( URL_OPT_VAL ) {
- LEX_CHAR ( '&', URL_OPT_SEP ),
- LEX_INVALID ( '=' ),
- LEX_DEFAULT ( URL_OPT_VAL ),
- },
-
- LEX_STATE_END ( URL_OPT_SEP ) {
- LEX_CHAR ( '&', URL_OPT_SEP ),
- LEX_INVALID ( '=' ),
- LEX_DEFAULT ( URL_OPT_KEY ),
- },
-
- LEX_STATE ( URL_ERROR ) {
- LEX_END
- },
- }
-};
-
-static int url_lex_token (int _this_token, char *token_data, int _next_token, int _prev_token, void *arg) {
- enum url_token this_token = _this_token, next_token = _next_token, prev_token = _prev_token;
- struct url_state *state = arg;
- const char **copy_to = NULL;
-
- (void) prev_token;
-
- switch (this_token) {
- case URL_BEGIN:
- // irrelevant
- break;
-
- case URL_BEGIN_ALNUM:
- switch (next_token) {
- case URL_SCHEME_SEP:
- // store the scheme
- if (_url_append_scheme(state->url, token_data, 1))
- goto error;
-
- break;
-
- case URL_USERNAME_END:
- // store the username
- copy_to = &state->url->username; break;
-
- case URL_PATH_START:
- case URL_OPT_START:
- case LEX_EOF:
- // store the hostname
- copy_to = &state->url->hostname; break;
-
- case URL_BEGIN_COLON:
- // gah...
- copy_to = &state->alnum; break;
-
-
- default:
- FATAL("weird next token");
- }
-
- break;
-
- case URL_BEGIN_COLON:
- switch (next_token) {
- case URL_SCHEME_END_SLASH1:
- // store the schema
- if (_url_append_scheme(state->url, state->alnum, 0))
- goto error;
-
- state->alnum = NULL;
-
- break;
-
- case URL_USERHOST_ALNUM2:
- // gah..
- break;
-
- default:
- FATAL("weird next token");
- }
-
- break;
-
- case URL_SCHEME:
- // store the scheme
- if (_url_append_scheme(state->url, token_data, 1))
- goto error;
-
- break;
-
- case URL_SCHEME_SEP:
- // ignore
- break;
-
- case URL_SCHEME_END_COL:
- case URL_SCHEME_END_SLASH1:
- case URL_SCHEME_END_SLASH2:
- // ignore
- break;
-
- case URL_USERHOST_ALNUM:
- switch (next_token) {
- case URL_USERNAME_END:
- // store the username
- copy_to = &state->url->username; break;
-
- case URL_PATH_START:
- case URL_OPT_START:
- case LEX_EOF:
- // store the hostname
- copy_to = &state->url->hostname; break;
-
- case URL_USERHOST_COLON:
- // gah...
- copy_to = &state->alnum; break;
-
- default:
- FATAL("weird next token");
- }
-
- break;
-
- case URL_USERHOST_COLON:
- // ignore
- break;
-
- case URL_USERHOST_ALNUM2:
- switch (next_token) {
- case URL_USERNAME_END:
- // store the username and password
- state->url->username = state->alnum; state->alnum = NULL;
- copy_to = &state->url->password;
-
- break;
-
- case URL_PATH_START:
- case URL_OPT_START:
- case LEX_EOF:
- // store the hostname and service
- state->url->hostname = state->alnum; state->alnum = NULL;
- copy_to = &state->url->service; break;
-
- default:
- FATAL("weird next token");
- }
-
- break;
-
- case URL_USERNAME:
- case URL_PASSWORD_SEP:
- case URL_PASSWORD:
- FATAL("these should be overshadowed");
-
- case URL_USERNAME_END:
- // ignore
- break;
-
- case URL_HOSTNAME:
- // store
- copy_to = &state->url->hostname; break;
-
- case URL_SERVICE_SEP:
- // ignore
- break;
-
- case URL_SERVICE:
- // store
- copy_to = &state->url->service; break;
-
- case URL_PATH_START:
- // ignore
- break;
-
- case URL_PATH:
- // store
- copy_to = &state->url->path; break;
-
- case URL_OPT_START:
- // ignore
- break;
-
- case URL_OPT_KEY:
- // store
- if (_url_append_opt_key(state->url, token_data))
- goto error;
-
- break;
-
- case URL_OPT_EQ:
- // ignore
- break;
-
- case URL_OPT_VAL:
- // store
- if (_url_append_opt_val(state->url, token_data))
- goto error;
-
- break;
-
- case URL_OPT_SEP:
- // ignore
- break;
-
- default:
- ERROR("invalid token");
- }
-
- if (copy_to) {
- // copy the token data
- if ((*copy_to = strdup(token_data)) == NULL)
- ERROR("strdup");
- }
-
- // good
- return 0;
-
-error:
- DEBUG("token: %s -> %s -> %s: %s",
- LEX_STATE_NAME(&url_lex, prev_token), LEX_STATE_NAME(&url_lex, this_token), LEX_STATE_NAME(&url_lex, next_token),
- token_data
- );
- return -1;
-}
-
-
-int url_parse (struct url *url, const char *text) {
- struct url_state state; ZINIT(state);
- int ret;
-
- // set up state
- state.url = url;
-
- // parse it
- if ((ret = lexer(&url_lex, text, &state)))
- ERROR("invalid URL");
-
- // success
- return 0;
-
-error:
- return -1;
-}
-
-static void _url_dump_part (const char *field, const char *val, FILE *stream) {
- if (val) {
- fprintf(stream, "%s=%s ", field, val);
- }
-}
-
-void url_dump (const struct url *url, FILE *stream) {
- int i;
-
- if (url->schema) {
- fprintf(stream, "schema=(");
-
- for (i = 0; i < url->schema->count; i++) {
- if (i > 0)
- fprintf(stream, ",");
-
- fprintf(stream, "%s", url->schema->list[i]);
- }
-
- fprintf(stream, ") ");
- }
-
- _url_dump_part("username", url->username, stream);
- _url_dump_part("password", url->password, stream);
- _url_dump_part("hostname", url->hostname, stream);
- _url_dump_part("service", url->service, stream);
- _url_dump_part("path", url->path, stream);
-
- if (url->opts) {
- fprintf(stream, "opts: ");
-
- for (i = 0; i < url->opts->count; i++) {
- fprintf(stream, "%s=%s ", url->opts->list[i].key, url->opts->list[i].value);
- }
- }
-
- fprintf(stream, "\n");
-}
-
--- a/src/lib/url.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,63 +0,0 @@
-#ifndef LIB_URL_H
-#define LIB_URL_H
-
-/*
- * A trivial parser for simple URLs
- *
- * [ <scheme> [ "+" <scheme> [ ... ] ] "://" ] [ <username> [ ":" <password> ] "@" ] [ <hostname>Â ] [ ":" <service> ] [ "/" <path> ] [ "?" [ <key> [ "=" <value> ] ] [ "&" [ <key> [ "=" <value> ] ] [ ... ] ]
- *
- * example.com
- * tcp://example.com:7348/
- * psql://postgres@localhost/test_db?charset=utf8
- *
- */
-
-#include <sys/types.h>
-#include <stdio.h>
-
-/*
- * The schema
- */
-struct url_schema {
- size_t count;
- const char *list[];
-};
-
-/*
- * The options at the end
- */
-struct url_opts {
- size_t count;
- struct url_opt {
- const char *key;
- const char *value;
- } list[];
-};
-
-/*
- * A parsed URL
- */
-struct url {
- struct url_schema *schema;
- const char *username;
- const char *password;
- const char *hostname;
- const char *service;
- const char *path;
- struct url_opts *opts;
-};
-
-/*
- * Parse the given `text` as an URL, returning the result in `url`. Optional fields that are missing in the text will
- * cause those values to be returned unmodified.
- *
- * Returns zero if the url was valid and was parsed, nonzero if it was invalid.
- */
-int url_parse (struct url *url, const char *text);
-
-/*
- * Prints a url in a debug-output format.
- */
-void url_dump (const struct url *url, FILE *stream);
-
-#endif /* LIB_URL_H */
--- a/src/simple.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,283 +0,0 @@
-#include <string.h>
-#include <errno.h>
-#include <stdlib.h>
-#include <assert.h>
-
-#include "simple.h"
-#include "dirbuf.h"
-#include "lib/log.h"
-#include "lib/math.h"
-#include "lib/misc.h"
-
-struct simple_fs {
- const struct simple_node *inode_table;
-
- size_t inode_count;
-};
-
-/*
- * Used for stat/entry timeouts... not sure how this should really be set.
- */
-#define CACHE_TIMEOUT 1.0
-
-static void _simple_stat (struct stat *stat, const struct simple_node *node) {
- stat->st_ino = node->inode;
- stat->st_mode = node->mode_type | node->mode_perm;
- stat->st_nlink = 1;
- stat->st_size = node->data ? strlen(node->data) : 0;
-}
-
-/*
- * Fetch the simple_node for the given inode.
- *
- * Returns NULL for invalid inodes.
- */
-static const struct simple_node *_simple_get_ino (struct simple_fs *fs, fuse_ino_t ino) {
- // make sure it's a valid inode
- if (ino < 1 || ino > fs->inode_count) {
- WARNING("invalid inode=%zu", ino);
- return NULL;
- }
-
- // return the node
- return fs->inode_table + (ino - 1);
-}
-
-static void simple_lookup (fuse_req_t req, fuse_ino_t parent, const char *name) {
- struct simple_fs *fs = fuse_req_userdata(req);
- const struct simple_node *node;
- struct fuse_entry_param e; ZINIT(e);
- int err;
-
- INFO("[simple.lookup %p] parent=%lu, name=`%s'", fs, parent, name);
-
- // find the matching node
- for (node = fs->inode_table; node->inode > 0; node++) {
- if (node->parent == parent && strcmp(node->name, name) == 0)
- break;
-
- }
-
- // did we find it?
- if (node->inode) {
- // set up the entry
- e.ino = node->inode;
- e.generation = 0x01;
- _simple_stat(&e.attr, node);
- e.attr_timeout = CACHE_TIMEOUT;
- e.entry_timeout = CACHE_TIMEOUT;
-
- // reply
- if ((err = fuse_reply_entry(req, &e)))
- EERROR(err, "fuse_reply_entry");
-
- } else {
- // not found
- err = ENOENT;
- goto error;
- }
-
- // success
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
-
-static void simple_getattr (fuse_req_t req, fuse_ino_t ino, struct fuse_file_info *fi) {
- struct simple_fs *fs = fuse_req_userdata(req);
- const struct simple_node *node;
- struct stat stbuf; ZINIT(stbuf);
- int err;
-
- INFO("[simple.getattr %p] ino=%lu", fs, ino);
-
- // look up the node
- if ((node = _simple_get_ino(fs, ino)) == NULL)
- EERROR(err = EINVAL, "bad inode");
-
- // set up the stbuf
- _simple_stat(&stbuf, node);
-
- // reply
- if ((err = fuse_reply_attr(req, &stbuf, CACHE_TIMEOUT)))
- EERROR(err, "fuse_reply_attr");
-
- // suceccss
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
-
-static void simple_readlink (fuse_req_t req, fuse_ino_t ino) {
- struct simple_fs *fs = fuse_req_userdata(req);
- const struct simple_node *node;
- int err;
-
- INFO("[simple.readlink %p] ino=%lu", fs, ino);
-
- // look up the node
- if ((node = _simple_get_ino(fs, ino)) == NULL)
- EERROR(err = EINVAL, "bad inode");
-
- // check that it's a symlink
- if (node->mode_type != S_IFLNK)
- EERROR(err = EINVAL, "bad mode");
-
- // return the contents
- if ((err = fuse_reply_readlink(req, node->data)))
- EERROR(err, "fuse_reply_readlink");
-
- // suceccss
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-
-}
-
-static void simple_readdir (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
- struct simple_fs *fs = fuse_req_userdata(req);
- const struct simple_node *dir_node, *node;
- struct dirbuf buf;
- int err;
-
- INFO("[simple.readdir] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi);
-
- // look up the inode
- if ((dir_node = _simple_get_ino(fs, ino)) == NULL)
- EERROR(err = EINVAL, "bad inode");
-
- // check that it's a dir
- if (dir_node->mode_type != S_IFDIR)
- EERROR(err = ENOTDIR, "bad mode");
-
- // fill in the dirbuf
- if (dirbuf_init(&buf, size, off))
- ERROR("failed to init dirbuf");
-
- // add . and ..
- // we set the next offset to 2, because all dirent offsets will be larger than that
- err = dirbuf_add(req, &buf, 0, 1, ".", dir_node->inode, S_IFDIR )
- || dirbuf_add(req, &buf, 1, 2, "..", dir_node->inode, S_IFDIR );
-
- if (err != 0)
- EERROR(err, "failed to add . and .. dirents");
-
- // look up all child nodes
- for (node = fs->inode_table; node->inode; node++) {
- // skip non-children
- if (node->parent != dir_node->inode)
- continue;
-
- // child node offsets are just inode + 2
- if ((err = dirbuf_add(req, &buf, node->inode + 2, node->inode + 3, node->name, node->inode, node->mode_type)) < 0)
- EERROR(err, "failed to add dirent for inode=%lu", node->inode);
-
- // stop if it's full
- if (err > 0)
- break;
- }
-
- // send it
- if ((err = -dirbuf_done(req, &buf)))
- EERROR(err, "failed to send buf");
-
- // success
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
-
-static void simple_read (fuse_req_t req, fuse_ino_t ino, size_t size, off_t off, struct fuse_file_info *fi) {
- struct simple_fs *fs = fuse_req_userdata(req);
- const struct simple_node *node;
- int err ;
-
- // fi is unused
- (void) fi;
-
- INFO("[simple.read] ino=%lu, size=%zu, off=%zu, fi=%p", ino, size, off, fi);
-
- // look up the inode
- if ((node = _simple_get_ino(fs, ino)) == NULL)
- EERROR(err = EINVAL, "bad inode");
-
- // check that it's a dir
- if (node->mode_type != S_IFREG)
- EERROR(err = (node->mode_type == S_IFDIR ? EISDIR : EINVAL), "bad mode");
-
- // seek past EOF?
- if (off >= strlen(node->data)) {
- // offset is out-of-file, so return EOF
- if ((err = fuse_reply_buf(req, NULL, 0)))
- EERROR(err, "fuse_reply_buf size=0");
-
- } else {
- // reply with the requested file data
- if ((err = fuse_reply_buf(req, node->data + off, MIN(strlen(node->data) - off, size))))
- EERROR(err, "fuse_reply_buf buf=%p + %zu, size=MIN(%zu, %zu)", node->data, off, strlen(node->data) - off, size);
- }
-
- // success
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
-
-
-/*
- * Define our fuse_lowlevel_ops struct.
- */
-static struct fuse_lowlevel_ops simple_ops = {
- .lookup = simple_lookup,
-
- .getattr = simple_getattr,
-
- .readlink = simple_readlink,
-
- .readdir = simple_readdir,
-
- .read = simple_read,
-};
-
-struct fuse_lowlevel_ops *simple_init () {
- return &simple_ops;
-}
-
-struct simple_fs *simple_new (const struct simple_node *node_list) {
- struct simple_fs *fs = NULL;
- const struct simple_node *node;
-
- // generate
- if ((fs = calloc(1, sizeof(*fs))) == NULL)
- ERROR("calloc");
-
- // remember node_list
- fs->inode_count = 0;
- fs->inode_table = node_list;
-
- // validate it
- for (node = fs->inode_table; node->inode; node++) {
- // update inode_count
- fs->inode_count++;
-
- // check that parent is valid
- assert(node->inode == fs->inode_count);
- assert(node->parent < node->inode);
- }
-
- // success
- return fs;
-
-error:
- return NULL;
-}
--- a/src/simple.h Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-#ifndef SIMPLE_H
-#define SIMPLE_H
-
-/*
- * A simple static in-memory filesystem structure.
- */
-
-#include "evfuse.h"
-
-/*
- * A simple file/dir.
- */
-struct simple_node {
- // inode number
- fuse_ino_t inode;
-
- // mode
- mode_t mode_type;
- mode_t mode_perm;
-
- // parent node
- fuse_ino_t parent;
-
- // name
- const char *name;
-
- // data
- const char *data;
-};
-
-/*
- * General information.
- */
-struct simple_fs;
-
-/*
- * Initialize simple, and get the fuse_lowlevel_ops.
- */
-struct fuse_lowlevel_ops *simple_init ();
-
-/*
- * Create a new simple_fs.
- */
-struct simple_fs *simple_new (const struct simple_node *node_list);
-
-#endif /* SIMPLE_H */
--- a/src/simple_hello.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,77 +0,0 @@
-
-#include <event2/event.h>
-
-#include "lib/log.h"
-#include "lib/signals.h"
-#include "evfuse.h"
-#include "simple.h"
-
-static struct hello {
- struct event_base *ev_base;
-
- struct signals *signals;
-
- struct simple_fs *fs;
-
- struct evfuse *ev_fuse;
-
-} ctx;
-
-static struct simple_node node_list[] = {
- { 1, S_IFDIR, 0555, 0, NULL, NULL },
- { 2, S_IFREG, 0444, 1, "hello", "Hello World!\n" },
- { 3, S_IFREG, 0444, 1, "foo", "Foo\n" },
- { 4, S_IFREG, 0444, 1, "bar", "Bar\n" },
- { 5, S_IFDIR, 0555, 1, "test", NULL },
- { 6, S_IFREG, 0444, 5, "file0", "data0\n" },
- { 7, S_IFREG, 0444, 5, "file1", "data1\n" },
- { 8, S_IFLNK, 0444, 1, "lnk0", "test/file0" },
- { 0, 0, 0, 0, NULL, NULL },
-};
-
-int main (int argc, char **argv) {
- struct fuse_args fuse_args = FUSE_ARGS_INIT(argc, argv);
-
- // 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");
-
- // setup fs
- if ((ctx.fs = simple_new(node_list)) == NULL)
- ERROR("simple_new");
-
- // open fuse
- if ((ctx.ev_fuse = evfuse_new(ctx.ev_base, &fuse_args, simple_init(), ctx.fs)) == 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_free(ctx.ev_fuse);
-
-/*
- if (ctx.fs)
- simple_close(ctx.fs);
-*/
-
- 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/url_test.c Tue Nov 18 02:06:52 2008 +0200
+++ /dev/null Thu Jan 01 00:00:00 1970 +0000
@@ -1,198 +0,0 @@
-
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-
-#include "lib/url.h"
-
-#define FAIL(...) do { printf("FAIL: "); printf(__VA_ARGS__); printf("\n"); return -1; } while (0)
-
-struct url_schema
- basic_http = { 1, { "http" } },
- svn_ssh = { 2, { "svn", "ssh" } },
- schema_unix = { 1, { "unix" } }
- ;
-
-struct url_opts
- opts_single = { 1, { { "key0", "val0" } } },
- opts_multi = { 2, { { "key0", "val0" }, { "key1", "val1" } } },
- opts_nullval = { 1, { { "keyN", NULL } } }
- ;
-
-struct url_test {
- const char *url;
- const struct url expected;
-} url_tests[] = {
- { "localhost:http", {
- NULL, NULL, NULL, "localhost", "http", NULL, NULL
- } },
-
- { "http://example.com/path", {
- &basic_http, NULL, NULL, "example.com", NULL, "path", NULL
- } },
-
- { "svn+ssh://user:passwd@someplace:someport/something", {
- &svn_ssh, "user", "passwd", "someplace", "someport", "something"
- } },
-
- { "user@:service/", {
- NULL, "user", NULL, NULL, "service", NULL
- } },
-
- { "unix:////tmp/foo.sock", {
- &schema_unix, NULL, NULL, NULL, NULL, "/tmp/foo.sock"
- } },
-
- { "unix:///tmp/foo.sock", {
- &schema_unix, NULL, NULL, NULL, NULL, "tmp/foo.sock"
- } },
-
- { "/tmp/foo.sock", {
- NULL, NULL, NULL, NULL, NULL, "tmp/foo.sock"
- } },
-
- { "?key0=val0", {
- NULL, NULL, NULL, NULL, NULL, NULL, &opts_single
- } },
-
- { "http://foo.com/index.php?key0=val0&key1=val1", {
- &basic_http, NULL, NULL, "foo.com", NULL, "index.php", &opts_multi
- } },
-
- { "example.org:81/?keyN", {
- NULL, NULL, NULL, "example.org", "81", NULL, &opts_nullval
- } },
-
- { NULL, { } },
-};
-
-int cmp_url_str (const char *field, const char *test, const char *real) {
- if (!test) {
- if (real)
- FAIL("%s shouldn't be present", field);
-
- } else if (!real) {
- FAIL("%s is missing", field);
-
- } else {
- if (strcmp(test, real) != 0)
- FAIL("%s differs: %s -> %s", field, test, real);
- }
-
- // ok
- return 0;
-}
-
-int cmp_url (const struct url *test, const struct url *real) {
- int i;
-
- // test schema
- if (!test->schema) {
- if (real->schema)
- FAIL("test has no schema, but real does");
-
- } else if (!real->schema) {
- FAIL("test has a schema, but real doesn't");
-
- } else {
- if (test->schema->count != test->schema->count)
- FAIL("inconsistent scheme count");
-
- for (i = 0; i < test->schema->count; i++) {
- if (strcmp(test->schema->list[i], real->schema->list[i]) != 0)
- FAIL("differing scheme #%d", i);
- }
- }
-
- // test username
- if (cmp_url_str("username", test->username, real->username))
- goto error;
-
- // test password
- if (cmp_url_str("password", test->password, real->password))
- goto error;
-
- // test hostname
- if (cmp_url_str("hostname", test->hostname, real->hostname))
- goto error;
-
- // test service
- if (cmp_url_str("service", test->service, real->service))
- goto error;
-
- // test path
- if (cmp_url_str("path", test->path, real->path))
- goto error;
-
- // test query
- if (!test->opts) {
- if (real->opts)
- FAIL("test has no opts, but real does");
-
- } else if (!real->opts) {
- FAIL("test has opts, but real doesn't");
-
- } else {
- if (test->opts->count != test->opts->count)
- FAIL("inconsistent opts count");
-
- for (i = 0; i < test->opts->count; i++) {
- if (cmp_url_str("opt key", test->opts->list[i].key, real->opts->list[i].key))
- FAIL("differing opt key #%d", i);
-
- if (cmp_url_str("opt value", test->opts->list[i].value, real->opts->list[i].value))
- FAIL("differing opt value #%d", i);
- }
- }
-
- // ok
- return 0;
-
-error:
- return -1;
-}
-
-void usage (const char *exec_name) {
- printf("Usage: %s\n\n\tNo arguments are accepted\n", exec_name);
-
- exit(EXIT_FAILURE);
-}
-
-int main (int argc, char **argv) {
- const struct url_test *test;
- struct url url;
-
- if (argc > 1)
- usage(argv[0]);
-
- // run the tests
- for (test = url_tests; test->url; test++) {
- // first output the URL we are handling...
- printf("%-80s - ", test->url);
- fflush(stdout);
-
- // parse the URL
- memset(&url, 0, sizeof(url));
-
- if (url_parse(&url, test->url)) {
- printf("FATAL: url_parse failed\n");
- return EXIT_FAILURE;
- }
-
- // compare it
- if (cmp_url(&test->expected, &url)) {
- printf("\texpected: ");
- url_dump(&test->expected, stdout);
-
- printf("\tresult: ");
- url_dump(&url, stdout);
-
- } else {
- printf("OK\n\t");
- url_dump(&url, stdout);
- }
-
- printf("\n");
- }
-}
-
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/util.c Sun Mar 08 00:19:12 2009 +0200
@@ -0,0 +1,331 @@
+#include <stdlib.h>
+#include <assert.h>
+
+#include "internal.h"
+#include "lib/log.h"
+#include "lib/misc.h"
+
+#define _PARAM_TYPE_CASE(typenam) case EVSQL_PARAM_ ## typenam: return #typenam
+
+#define _PARAM_VAL_BUF_MAX 120
+#define _PARAM_VAL_CASE(typenam, ...) case EVSQL_PARAM_ ## typenam: if (param->data_raw) ret = snprintf(buf, _PARAM_VAL_BUF_MAX, __VA_ARGS__); else return "(null)"; break
+
+const char *evsql_param_type (const struct evsql_query_param *param) {
+ switch (param->type) {
+ _PARAM_TYPE_CASE (INVALID );
+ _PARAM_TYPE_CASE (NULL_ );
+ _PARAM_TYPE_CASE (BINARY );
+ _PARAM_TYPE_CASE (STRING );
+ _PARAM_TYPE_CASE (UINT16 );
+ _PARAM_TYPE_CASE (UINT32 );
+ _PARAM_TYPE_CASE (UINT64 );
+ default: return "???";
+ }
+}
+
+
+static const char *evsql_param_val (const struct evsql_query_param *param) {
+ static char buf[_PARAM_VAL_BUF_MAX];
+ int ret;
+
+ switch (param->type) {
+ _PARAM_VAL_CASE (INVALID, "???" );
+ _PARAM_VAL_CASE (NULL_, "(null)" );
+ _PARAM_VAL_CASE (BINARY, "%zu:%s", param->length, "..." );
+ _PARAM_VAL_CASE (STRING, "%s", param->data_raw );
+ _PARAM_VAL_CASE (UINT16, "%hu", (unsigned short int) ntohs(param->data.uint16) );
+ _PARAM_VAL_CASE (UINT32, "%lu", (unsigned long int) ntohl(param->data.uint32) );
+ _PARAM_VAL_CASE (UINT64, "%llu", (unsigned long long int) ntohq(param->data.uint64) );
+ default: return "???";
+ }
+
+ return buf;
+}
+
+int evsql_params_clear (struct evsql_query_params *params) {
+ struct evsql_query_param *param;
+
+ for (param = params->list; param->type; param++)
+ param->data_raw = NULL;
+
+ return 0;
+}
+
+int evsql_param_null (struct evsql_query_params *params, size_t param) {
+ struct evsql_query_param *p = ¶ms->list[param];
+
+ p->data_raw = NULL;
+
+ return 0;
+}
+
+int evsql_param_binary (struct evsql_query_params *params, size_t param, const char *ptr, size_t len) {
+ struct evsql_query_param *p = ¶ms->list[param];
+
+ assert(p->type == EVSQL_PARAM_BINARY);
+
+ p->data_raw = ptr;
+ p->length = len;
+
+ return 0;
+}
+
+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_uint16 (struct evsql_query_params *params, size_t param, uint16_t uval) {
+ struct evsql_query_param *p = ¶ms->list[param];
+
+ assert(p->type == EVSQL_PARAM_UINT16);
+
+ p->data.uint16 = htons(uval);
+ p->data_raw = (const char *) &p->data.uint16;
+ p->length = sizeof(uval);
+
+ 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;
+}
+
+void evsql_query_debug (const char *sql, const struct evsql_query_params *params) {
+ const struct evsql_query_param *param;
+ size_t param_count = 0, idx = 0;
+
+ // count the params
+ for (param = params->list; param->type; param++)
+ param_count++;
+
+ DEBUG("sql: %s", sql);
+ DEBUG("params: %zu", param_count);
+
+ for (param = params->list; param->type; param++) {
+ DEBUG("\t%2zu : %8s = %s", ++idx, evsql_param_type(param), evsql_param_val(param));
+ }
+}
+
+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) {
+ *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));
+
+ *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_binlen (const struct evsql_result_info *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))
+ goto error;
+
+ if (*ptr == NULL) {
+ assert(nullok);
+ return 0;
+ }
+
+ if (size && real_size != size)
+ ERROR("[%zu:%zu] field size mismatch: %zu -> %zu", row, col, size, real_size);
+
+ return 0;
+
+error:
+ return -1;
+}
+
+int evsql_result_string (const struct evsql_result_info *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))
+ goto error;
+
+ assert(real_size == strlen(*ptr));
+
+ return 0;
+
+error:
+ return -1;
+}
+
+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_binlen(res, row, col, &data, sizeof(*uval), nullok))
+ goto error;
+
+ if (!data)
+ return 0;
+
+ 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_binlen(res, row, col, &data, sizeof(*uval), nullok))
+ goto error;
+
+ if (!data)
+ return 0;
+
+ 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_binlen(res, row, col, &data, sizeof(*uval), nullok))
+ goto error;
+
+ if (!data)
+ return 0;
+
+ 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");
+ }
+}
+
+const char *evsql_conn_error (struct evsql_conn *conn) {
+ switch (conn->evsql->type) {
+ case EVSQL_EVPQ:
+ if (!conn->engine.evpq)
+ return "unknown error (no conn)";
+
+ return evpq_error_message(conn->engine.evpq);
+
+ default:
+ FATAL("res->evsql->type");
+ }
+}
+
+const char *evsql_trans_error (struct evsql_trans *trans) {
+ if (trans->conn == NULL)
+ return "unknown error (no trans conn)";
+
+ return evsql_conn_error(trans->conn);
+}
+