--- a/Makefile Thu Oct 16 22:56:29 2008 +0300
+++ b/Makefile Fri Oct 17 02:04:03 2008 +0300
@@ -20,7 +20,7 @@
# complex modules
EVSQL_OBJS = obj/evsql/evsql.o obj/evsql/util.o obj/evpq.o
-DBFS_OBJS = obj/dbfs/dbfs.o obj/dbfs/common.o obj/dbfs/core.o obj/dbfs/op_base.o obj/dbfs/dirop.o obj/dirbuf.o obj/dbfs/fileop.o
+DBFS_OBJS = obj/dbfs/dbfs.o obj/dbfs/common.o obj/dbfs/core.o obj/dbfs/op_base.o obj/dbfs/dirop.o obj/dirbuf.o obj/dbfs/fileop.o obj/dbfs/attr.o
# first target
all: ${BIN_PATHS}
@@ -44,7 +44,9 @@
# other targets
clean :
- -rm obj/* bin/* build/deps/*
+ -rm obj/*.o obj/*/*.o
+ -rm bin/*
+ -rm build/deps/*.d build/deps/*/*.d
clean-deps:
-rm build/deps/*/*.d
--- a/doc/fuse_db.sql Thu Oct 16 22:56:29 2008 +0300
+++ b/doc/fuse_db.sql Fri Oct 17 02:04:03 2008 +0300
@@ -1,11 +1,27 @@
-CREATE TABLE inodes (ino serial4 primary key, type char(3), mode int2, size int8);
-CREATE TABLE file_tree ("offset" serial4 primary key, name varchar(256) NOT NULL, parent int4 references inodes(ino) NOT NULL, inode int4 references inodes(ino) NOT NULL);
+CREATE TABLE inodes (
+ ino serial4 primary key,
+ type char(3) NOT NULL,
+ mode int2 NOT NULL,
+ data oid
+);
-INSERT INTO inodes VALUES (1, 'DIR', 365, 0);
-INSERT INTO inodes VALUES (2, 'REG', 292, 0);
-INSERT INTO file_tree (name, parent, inode) VALUES ('foo', 1, 2);
+CREATE TABLE file_tree (
+ "offset" serial4 primary key,
+ name varchar(256),
+ parent int4 references inodes(ino),
+ inode int4 references inodes(ino) NOT NULL
+);
-ALTER TABLE file_tree ALTER COLUMN name DROP NOT NULL;
-ALTER TABLE file_tree ALTER COLUMN parent DROP NOT NULL;
-INSERT INTO file_tree (name, parent, inode) VALUES (NULL, NULL, 1);
+INSERT INTO inodes VALUES
+ (1, 'DIR', 365, NULL),
+ (2, 'REG', 292, lo_create(0));
+INSERT INTO file_tree (name, parent, inode) VALUES
+ (NULL, NULL, 1 ),
+ ('foo', 1, 2 );
+
+CREATE FUNCTION lo_size (oid) RETURNS int4 LANGUAGE SQL STABLE RETURNS NULL ON NULL INPUT AS 'select lo_lseek(lo_open($1, 262144), 0, 2);';
+CREATE FUNCTION lo_pread (IN fd int4, IN len int4, IN "off" int4) RETURNS bytea LANGUAGE SQL STRICT AS 'select lo_lseek($1, $3, 0); select loread($1, $2);';
+CREATE FUNCTION lo_pwrite (IN fd int4, IN buf bytea, IN "off" int4) RETURNS int4 LANGUAGE SQL STRICT AS 'select lo_lseek($1, $3, 0); select lowrite($1, $2);';
+CREATE FUNCTION lo_otruncate (IN oid, IN len int4) RETURNS oid LANGUAGE SQL STRICT AS 'select lo_truncate(lo_open($1, 393216), $2); select $1;';
+
--- a/src/dbfs.c Thu Oct 16 22:56:29 2008 +0300
+++ b/src/dbfs.c Fri Oct 17 02:04:03 2008 +0300
@@ -17,7 +17,7 @@
#include "lib/signals.h"
#include "lib/misc.h"
-#define CONNINFO_DEFAULT "dbname=test"
+#define CONNINFO_DEFAULT "dbname=dbfs port=5433"
int main (int argc, char **argv) {
struct event_base *ev_base = NULL;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/dbfs/attr.c Fri Oct 17 02:04:03 2008 +0300
@@ -0,0 +1,175 @@
+
+#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("[dbfs.getattr %p] -> ino=%lu, stat follows", req, (unsigned long int) ino);
+
+ // stat attrs
+ if ((err = _dbfs_stat_info(&st, res, 0, 1)))
+ goto error;
+
+ // XXX: we don't have the ino
+ st.st_ino = ino;
+
+ // 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);
+ 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"
+ " GROUP BY inodes.ino, inodes.type, inodes.mode, data";
+
+ 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 (evsql_query_params(ctx->db, NULL, sql, ¶ms, _dbfs_attr_res, req) == NULL)
+ SERROR(err = EIO);
+
+ // XXX: handle interrupts
+
+ // 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);
+ 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_NOAGGREGATE,
+
+ 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 (evsql_query_params(ctx->db, NULL, sql_buf, ¶ms, _dbfs_attr_res, req) == NULL)
+ SERROR(err = EIO);
+
+ // XXX: handle interrupts
+
+ // wait
+ return;
+
+error:
+ if ((err = fuse_reply_err(req, err)))
+ EWARNING(err, "fuse_reply_err");
+}
--- a/src/dbfs/common.c Thu Oct 16 22:56:29 2008 +0300
+++ b/src/dbfs/common.c Fri Oct 17 02:04:03 2008 +0300
@@ -51,17 +51,17 @@
uint16_t mode;
uint32_t size = 0; // NULL for non-REG inodes
- uint64_t nlink;
+ uint64_t nlink = 0; // will be NULL for non-GROUP BY queries
const char *type;
// extract the data
- if (0
+ 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, 0 ) // count(*)
- )
- EERROR(err = EIO, "invalid db data");
+ || 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);
@@ -74,7 +74,7 @@
return 0;
error:
- return -1;
+ return err;
}
--- a/src/dbfs/core.c Thu Oct 16 22:56:29 2008 +0300
+++ b/src/dbfs/core.c Fri Oct 17 02:04:03 2008 +0300
@@ -1,9 +1,8 @@
+#include "dbfs.h"
#include "../lib/log.h"
#include "../lib/misc.h"
-#include "dbfs.h"
-
/*
* Core fs functionality like lookup, getattr
*/
@@ -28,7 +27,7 @@
INFO("[dbfs.lookup] -> ino=%u", ino);
// stat attrs
- if (_dbfs_stat_info(&e.attr, res, 0, 1))
+ if ((err = _dbfs_stat_info(&e.attr, res, 0, 1)))
goto error;
// other attrs
@@ -57,7 +56,7 @@
// query and params
const char *sql =
"SELECT"
- " inodes.ino, inodes.type, inodes.mode, dbfs_lo_size(data), count(*)"
+ " inodes.ino, " DBFS_STAT_COLS
" FROM file_tree INNER JOIN inodes ON (file_tree.inode = inodes.ino)"
" WHERE file_tree.parent = $1::int4 AND file_tree.name = $2::varchar"
" GROUP BY inodes.ino, inodes.type, inodes.mode, data";
@@ -90,74 +89,3 @@
EWARNING(err, "fuse_reply_err");
}
-void _dbfs_getattr_result (const struct evsql_result_info *res, void *arg) {
- struct fuse_req *req = arg;
- struct stat st; ZINIT(st);
- int err = 0;
-
- // check the results
- if ((err = _dbfs_check_res(res, 1, 4)))
- SERROR(err = (err == 1 ? ENOENT : EIO));
-
- INFO("[dbfs.getattr %p] -> (stat follows)", req);
-
- // stat attrs
- if (_dbfs_stat_info(&st, res, 0, 0))
- goto error;
-
- // XXX: we don't have the ino
- st.st_ino = 0;
-
- // reply
- if ((err = fuse_reply_attr(req, &st, CACHE_TIMEOUT)))
- 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);
- int err;
-
- (void) fi;
-
- INFO("[dbfs.getattr %p] ino=%lu", req, ino);
-
- const char *sql =
- "SELECT"
- " inodes.type, inodes.mode, dbfs_lo_size(data), count(*)"
- " FROM inodes"
- " WHERE inodes.ino = $1::int4"
- " GROUP BY inodes.type, inodes.mode, data";
-
- 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 (evsql_query_params(ctx->db, NULL, sql, ¶ms, _dbfs_getattr_result, req) == NULL)
- SERROR(err = EIO);
-
- // XXX: handle interrupts
-
- // wait
- return;
-
-error:
- if ((err = fuse_reply_err(req, err)))
- EWARNING(err, "fuse_reply_err");
-}
-
--- a/src/dbfs/dbfs.c Thu Oct 16 22:56:29 2008 +0300
+++ b/src/dbfs/dbfs.c Fri Oct 17 02:04:03 2008 +0300
@@ -13,10 +13,11 @@
.lookup = dbfs_lookup,
.getattr = dbfs_getattr,
+ .setattr = dbfs_setattr,
.open = dbfs_open,
.read = dbfs_read,
- // .write = dbfs_write,
+ .write = dbfs_write,
.flush = dbfs_flush,
.release = dbfs_release,
--- a/src/dbfs/dbfs.h Thu Oct 16 22:56:29 2008 +0300
+++ b/src/dbfs/dbfs.h Fri Oct 17 02:04:03 2008 +0300
@@ -28,6 +28,10 @@
// 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, lo_size(data), count(*) "
+#define DBFS_STAT_COLS_NOAGGREGATE " inodes.type, inodes.mode, lo_size(data), NULL "
+
/*
* Convert the CHAR(4) inodes.type from SQL into a mode_t.
*
--- a/src/dbfs/fileop.c Thu Oct 16 22:56:29 2008 +0300
+++ b/src/dbfs/fileop.c Fri Oct 17 02:04:03 2008 +0300
@@ -139,7 +139,7 @@
SERROR(err = EIO);
// get the data
- if (evsql_result_buf(res, 0, 0, &buf, &size, 0))
+ if (evsql_result_binary(res, 0, 0, &buf, &size, 0))
SERROR(err = EIO);
INFO("[dbfs.read %p:%p] -> size=%zu", fop, fop->base.req, size);
@@ -208,8 +208,83 @@
dbfs_op_fail(&fop->base, err);
}
+void dbfs_write_res (const struct evsql_result_info *res, void *arg) {
+ struct dbfs_fileop *fop = 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("[dbfs.write %p:%p] -> size=%lu", fop, fop->base.req, (long unsigned int) size);
+
+ // send it
+ if ((err = fuse_reply_write(fop->base.req, size)))
+ EERROR(err, "fuse_reply_write");
+
+ // ok, req handled
+ if ((err = dbfs_op_req_done(&fop->base)))
+ goto error;
+
+ // good, fallthrough
+ err = 0;
+
+error:
+ if (err)
+ dbfs_op_fail(&fop->base, 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);
+ struct dbfs_fileop *fop;
+ int err;
+
+ // get the op
+ if ((fop = (struct dbfs_fileop *) dbfs_op_req(req, ino, fi)) == NULL)
+ return;
+ // log
+ INFO("[dbfs.write %p:%p] ino=%lu, size=%zu, off=%lu, fi->flags=%04X", fop, req, ino, size, off, fi->flags);
+
+ // query
+ const char *sql =
+ "SELECT"
+ " lo_pwrite($1::int4, $2::bytea, $3::int4)";
+
+ static struct evsql_query_params params = EVSQL_PARAMS(EVSQL_FMT_BINARY) {
+ EVSQL_PARAM ( UINT32 ), // fd
+ EVSQL_PARAM ( BINARY ), // buf
+ EVSQL_PARAM ( UINT32 ), // off
+
+ EVSQL_PARAMS_END
+ };
+
+ // build params
+ if (0
+ || evsql_param_uint32(¶ms, 0, fop->lo_fd)
+ || evsql_param_binary(¶ms, 1, buf, size)
+ || evsql_param_uint32(¶ms, 2, off)
+ )
+ SERROR(err = EIO);
+
+ // query
+ if (evsql_query_params(ctx->db, fop->base.trans, sql, ¶ms, dbfs_write_res, fop) == NULL)
+ SERROR(err = EIO);
+
+ // ok, wait for the info results
+ return;
+
+error:
+ // fail it
+ dbfs_op_fail(&fop->base, err);
}
void dbfs_flush (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi) {
--- a/src/dbfs/ops.h Thu Oct 16 22:56:29 2008 +0300
+++ b/src/dbfs/ops.h Fri Oct 17 02:04:03 2008 +0300
@@ -11,6 +11,9 @@
void dbfs_lookup (struct fuse_req *req, fuse_ino_t parent, const char *name);
void dbfs_getattr (struct fuse_req *req, fuse_ino_t ino, struct fuse_file_info *fi);
+/* setattr.c */
+void dbfs_setattr(struct fuse_req *req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi);
+
/* 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);
@@ -19,6 +22,7 @@
/* 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);
--- a/src/evsql.h Thu Oct 16 22:56:29 2008 +0300
+++ b/src/evsql.h Fri Oct 17 02:04:03 2008 +0300
@@ -47,8 +47,9 @@
enum evsql_param_type {
EVSQL_PARAM_INVALID,
- EVSQL_PARAM_NULL,
-
+ EVSQL_PARAM_NULL_,
+
+ EVSQL_PARAM_BINARY,
EVSQL_PARAM_STRING,
EVSQL_PARAM_UINT16,
EVSQL_PARAM_UINT32,
@@ -213,8 +214,18 @@
/*
* 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_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
@@ -231,8 +242,7 @@
// 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_buf (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok);
-int evsql_result_binary (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t size, int nullok);
+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
--- a/src/evsql/evsql.c Thu Oct 16 22:56:29 2008 +0300
+++ b/src/evsql/evsql.c Fri Oct 17 02:04:03 2008 +0300
@@ -852,7 +852,7 @@
// allocate the vertical storage for the parameters
if (0
-// !(query->params.types = calloc(query->params.count, sizeof(Oid)))
+ || !(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)))
@@ -861,8 +861,8 @@
// transform
for (param = params->list, idx = 0; param->type; param++, idx++) {
- // `types` stays NULL
- // query->params.types[idx] = 0;
+ // `set for NULLs, otherwise not
+ query->params.types[idx] = param->data_raw ? 0 : 16;
// values
query->params.values[idx] = param->data_raw;
@@ -870,8 +870,8 @@
// lengths
query->params.lengths[idx] = param->length;
- // formats, binary if length is nonzero
- query->params.formats[idx] = param->length ? 1 : 0;
+ // formats, binary if length is nonzero, but text for NULLs
+ query->params.formats[idx] = param->length && param->data_raw ? 1 : 0;
}
// result format
--- a/src/evsql/util.c Thu Oct 16 22:56:29 2008 +0300
+++ b/src/evsql/util.c Fri Oct 17 02:04:03 2008 +0300
@@ -1,9 +1,66 @@
#include <assert.h>
#include "evsql.h"
-#include "../lib/error.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_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];
@@ -15,6 +72,18 @@
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];
@@ -27,6 +96,22 @@
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";
@@ -64,7 +149,7 @@
}
}
-int evsql_result_buf (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok) {
+int evsql_result_binary (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t *size, int nullok) {
*ptr = NULL;
switch (res->evsql->type) {
@@ -92,11 +177,16 @@
return -1;
}
-int evsql_result_binary (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, size_t size, int nullok) {
- size_t real_size;
+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_buf(res, row, col, ptr, &real_size, nullok))
+ 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);
@@ -108,14 +198,24 @@
}
int evsql_result_string (const struct evsql_result_info *res, size_t row, size_t col, const char **ptr, int nullok) {
- return evsql_result_binary(res, row, col, ptr, 0, nullok);
+ 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_binary(res, row, col, &data, sizeof(*uval), nullok))
+ if (evsql_result_binlen(res, row, col, &data, sizeof(*uval), nullok))
goto error;
if (!data)
@@ -138,7 +238,7 @@
const char *data;
int32_t sval;
- if (evsql_result_binary(res, row, col, &data, sizeof(*uval), nullok))
+ if (evsql_result_binlen(res, row, col, &data, sizeof(*uval), nullok))
goto error;
if (!data)
@@ -161,7 +261,7 @@
const char *data;
int64_t sval;
- if (evsql_result_binary(res, row, col, &data, sizeof(*uval), nullok))
+ if (evsql_result_binlen(res, row, col, &data, sizeof(*uval), nullok))
goto error;
if (!data)
--- a/src/lib/log.h Thu Oct 16 22:56:29 2008 +0300
+++ b/src/lib/log.h Fri Oct 17 02:04:03 2008 +0300
@@ -40,6 +40,7 @@
// various kinds of ways to handle an error, 2**3 of them, *g*
#define info(...) _generic_err( LOG_DISPLAY_STDOUT, NULL, 0, __VA_ARGS__ )
+#define info_nonl(...) _generic_err( LOG_DISPLAY_STDOUT | LOG_DISPLAY_NONL, NULL, 0, __VA_ARGS__ )
#define error(...) _generic_err( LOG_DISPLAY_STDERR, NULL, 0, __VA_ARGS__ )
#define err_exit(...) _generic_err_exit( LOG_DISPLAY_STDERR, NULL, 0, __VA_ARGS__ )
#define perr(...) _generic_err( LOG_DISPLAY_STDERR | LOG_DISPLAY_PERR, NULL, 0, __VA_ARGS__ )
@@ -85,6 +86,7 @@
#if INFO_ENABLED
#define INFO(...) info(__VA_ARGS__)
+#define INFON(...) info_nonl(__VA_ARGS__)
#else
#define INFO(...) (void) (__VA_ARGS__)
#endif