src/result.c
changeset 58 02e539965ef4
parent 51 c65d0f4c3bff
child 63 76a782abddca
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/result.c	Sun Mar 08 01:33:45 2009 +0200
@@ -0,0 +1,258 @@
+
+#include "internal.h"
+#include "lib/error.h"
+#include "lib/misc.h"
+
+#include <stdlib.h>
+#include <assert.h>
+
+const char *evsql_result_error (const struct evsql_result *res) {
+    if (!res->error)
+        return "No error";
+
+    switch (res->evsql->type) {
+        case EVSQL_EVPQ:
+            if (!res->result.pq)
+                return "unknown error (no result)";
+            
+            return PQresultErrorMessage(res->result.pq);
+
+        default:
+            FATAL("res->evsql->type");
+    }
+
+}
+
+size_t evsql_result_rows (const struct evsql_result *res) {
+    switch (res->evsql->type) {
+        case EVSQL_EVPQ:
+            return PQntuples(res->result.pq);
+
+        default:
+            FATAL("res->evsql->type");
+    }
+}
+
+size_t evsql_result_cols (const struct evsql_result *res) {
+    switch (res->evsql->type) {
+        case EVSQL_EVPQ:
+            return PQnfields(res->result.pq);
+
+        default:
+            FATAL("res->evsql->type");
+    }
+}
+
+size_t evsql_result_affected (const struct evsql_result *res) {
+    switch (res->evsql->type) {
+        case EVSQL_EVPQ:
+            // XXX: errors?
+            return strtol(PQcmdTuples(res->result.pq), NULL, 10);
+
+        default:
+            FATAL("res->evsql->type");
+    }
+}
+
+
+int evsql_result_null (const struct evsql_result *res, size_t row, size_t col) {
+    switch (res->evsql->type) {
+        case EVSQL_EVPQ:
+            return PQgetisnull(res->result.pq, row, col);
+
+        default:
+            FATAL("res->evsql->type");
+    }
+}
+
+int evsql_result_field (const struct evsql_result *res, size_t row, size_t col, const char **ptr, size_t *size) {
+    *ptr = NULL;
+
+    switch (res->evsql->type) {
+        case EVSQL_EVPQ:
+            if (PQfformat(res->result.pq, col) != 1)
+                ERROR("[%zu:%zu] PQfformat is not binary: %d", row, col, PQfformat(res->result.pq, col));
+    
+            *size = PQgetlength(res->result.pq, row, col);
+            *ptr  = PQgetvalue(res->result.pq, row, col);
+
+            return 0;
+
+        default:
+            FATAL("res->evsql->type");
+    }
+
+error:
+    return -1;
+}
+
+err_t evsql_result_check (struct evsql_result *res) {
+    // so simple...
+    return res->error ? EIO : 0;
+}
+
+err_t evsql_result_begin (struct evsql_result_info *info, struct evsql_result *res) {
+    struct evsql_item_info *col;
+    size_t cols = 0, nrows;
+    err_t err;
+
+    // count info columns
+    for (col = info->columns; col->type; col++)
+        cols++;
+
+    // number of rows returned/affected
+    nrows = evsql_result_rows(res) || evsql_result_affected(res);
+
+    // did the query fail outright?
+    if (res->error)
+        // dump error message
+        NXERROR(err = EIO, evsql_result_error(res));
+
+/*
+    // SELECT/DELETE/UPDATE WHERE didn't match any rows -> ENOENT
+    if (nrows == 0)
+        XERROR(err = ENOENT, "no rows returned/affected");
+*/
+
+    // correct number of columns
+    if (evsql_result_cols(res) != cols)
+        XERROR(err = EINVAL, "wrong number of columns: %zu, should be %zu", evsql_result_cols(res), cols);
+    
+    // assign
+    res->info = info;
+    res->row_offset = 0;
+
+    // good
+    return 0;
+
+error:
+    return err;
+
+}
+
+int evsql_result_next (struct evsql_result *res, ...) {
+    va_list vargs;
+    struct evsql_item_info *col;
+    size_t col_idx, row_idx = res->row_offset;
+    err_t err;
+    
+    // ensure that evsql_result_begin has been called
+    assert(res->info);
+    
+    // check if we're past the end
+    if (row_idx >= evsql_result_rows(res))
+        return 0;
+    
+    // varargs
+    va_start(vargs, res);
+
+    for (col = res->info->columns, col_idx = 0; col->type; col++, col_idx++) {
+        const char *value = NULL;
+        size_t length = 0;
+        
+        // check for NULLs, then try and get the field value
+        if (evsql_result_null(res, row_idx, col_idx)) {
+            if (!col->flags.null_ok)
+                XERROR(err = EINVAL, "r%zu:c%zu: NULL", row_idx, col_idx);
+
+        } else if (evsql_result_field(res, row_idx, col_idx, &value, &length)) {
+            SERROR(err = EINVAL);
+
+        }
+        
+        // read the arg
+        switch (col->type) {
+            case EVSQL_TYPE_BINARY: {
+                struct evsql_item_binary *item_ptr = va_arg(vargs, struct evsql_item_binary *);
+
+                if (value) {
+                    item_ptr->ptr = value;
+                    item_ptr->len = length;
+                }
+            } break;
+
+            case EVSQL_TYPE_STRING: {
+                const char **str_ptr = va_arg(vargs, const char **);
+
+                if (value) {
+                    *str_ptr = value;
+                }
+
+            } break;
+
+            case EVSQL_TYPE_UINT16: {
+                uint16_t *uval_ptr = va_arg(vargs, uint16_t *);
+
+                if (!value) break;
+
+                if (length != sizeof(uint16_t)) XERROR(err = EINVAL, "r%zu:c%zu: wrong size for uint16_t: %zu", row_idx, col_idx, length);
+
+                int16_t sval = ntohs(*((int16_t *) value));
+
+                if (sval < 0) XERROR(err = ERANGE, "r%zu:c%zu: out of range for uint16_t: %hd", row_idx, col_idx, (signed short) sval);
+
+                *uval_ptr = sval;
+            } break;
+            
+            case EVSQL_TYPE_UINT32: {
+                uint32_t *uval_ptr = va_arg(vargs, uint32_t *);
+
+                if (!value) break;
+
+                if (length != sizeof(uint32_t)) XERROR(err = EINVAL, "r%zu:c%zu: wrong size for uint32_t: %zu", row_idx, col_idx, length);
+
+                int32_t sval = ntohl(*((int32_t *) value));
+
+                if (sval < 0) XERROR(err = ERANGE, "r%zu:c%zu: out of range for uint32_t: %ld", row_idx, col_idx, (signed long) sval);
+
+                *uval_ptr = sval;
+            } break;
+            
+            case EVSQL_TYPE_UINT64: {
+                uint64_t *uval_ptr = va_arg(vargs, uint64_t *);
+
+                if (!value) break;
+
+                if (length != sizeof(uint64_t)) XERROR(err = EINVAL, "r%zu:c%zu: wrong size for uint64_t: %zu", row_idx, col_idx, length);
+
+                int64_t sval = ntohq(*((int64_t *) value));
+
+                if (sval < 0) XERROR(err = ERANGE, "r%zu:c%zu: out of range for uint64_t: %lld", row_idx, col_idx, (signed long long) sval);
+
+                *uval_ptr = sval;
+            } break;
+            
+            default:
+                XERROR(err = EINVAL, "r%zu:c%zu: invalid type: %d", row_idx, col_idx, col->type);
+        }
+    }
+
+    // advance row index
+    res->row_offset++;
+
+    // row handled succesfully
+    return 1;
+
+error:
+    return -err;
+}
+
+void evsql_result_end (struct evsql_result *res) {
+    // not much more to it...
+    evsql_result_free(res);
+}
+
+void evsql_result_free (struct evsql_result *res) {
+    // note that the result itself might be NULL...
+    // in the case of internal-error results, these may be free'd multiple times!
+    switch (res->evsql->type) {
+        case EVSQL_EVPQ:
+            if (res->result.pq)
+                return PQclear(res->result.pq);
+
+        default:
+            FATAL("res->evsql->type");
+    }
+}
+
+