src/query.c
author Tero Marttila <terom@fixme.fi>
Wed, 01 May 2013 00:11:39 +0300
changeset 66 fdd7f995af6c
parent 58 02e539965ef4
permissions -rw-r--r--
MIT license

#include "internal.h"
#include "lib/error.h"
#include "lib/misc.h"

#include <stdlib.h>
#include <assert.h>

/*
 * Initialize params->types/values/lengths/formats, params->count, params->result_format based on the given args
 */
static int _evsql_query_params_init_pq (struct evsql_query_params_pq *params, size_t param_count, enum evsql_item_format result_format) {
    // set count
    params->count = param_count;

    // allocate vertical storage for the parameters
    if (0
        
        ||  !(params->types     = calloc(param_count, sizeof(Oid)))
        ||  !(params->values    = calloc(param_count, sizeof(char *)))
        ||  !(params->lengths   = calloc(param_count, sizeof(int)))
        ||  !(params->formats   = calloc(param_count, sizeof(int)))
        ||  !(params->item_vals = calloc(param_count, sizeof(union evsql_item_value)))
    )
        ERROR("calloc");

    // result format
    switch (result_format) {
        case EVSQL_FMT_TEXT:
            params->result_format = 0; break;

        case EVSQL_FMT_BINARY:
            params->result_format = 1; break;

        default:
            FATAL("params.result_fmt: %d", result_format);
    }
    
    // good
    return 0;

error:
    return -1;
}

struct evsql_query *evsql_query (struct evsql *evsql, struct evsql_trans *trans, const char *command, evsql_query_cb query_fn, void *cb_arg) {
    struct evsql_query *query = NULL;
    
    // alloc new query
    if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL)
        goto error;
    
    // just execute the command string directly
    if (_evsql_query_enqueue(evsql, trans, query, command))
        goto error;

    // ok
    return query;

error:
    _evsql_query_free(query);

    return NULL;
}

struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans, 
    const char *command, const struct evsql_query_params *params, 
    evsql_query_cb query_fn, void *cb_arg
) {
    struct evsql_query *query = NULL;
    const struct evsql_item *param;
    size_t count = 0, idx;
    
    // alloc new query
    if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL)
        goto error;

    // count the params
    for (param = params->list; param->info.type; param++) 
        count++;
    
    // initialize params
    _evsql_query_params_init_pq(&query->params, count, params->result_format);

    // transform
    for (param = params->list, idx = 0; param->info.type; param++, idx++) {
        // set for NULLs, otherwise not
        query->params.types[idx] = (param->bytes || param->flags.has_value) ? 0 : EVSQL_PQ_ARBITRARY_TYPE_OID;

        // scalar values
        query->params.item_vals[idx] = param->value;
        
        // values
        // point this at the value stored in the item_vals union if flagged as such
        query->params.values[idx] = param->flags.has_value ? (const char *) &query->params.item_vals[idx] : param->bytes;

        // lengths
        query->params.lengths[idx] = param->length;
        
        // XXX: this assumes that format is FMT_BINARY...
        query->params.formats[idx] = param->info.format;
    }

    // execute it
    if (_evsql_query_enqueue(evsql, trans, query, command))
        goto error;

#ifdef DEBUG_ENABLED
    // debug it?
    DEBUG("evsql.%p: enqueued query=%p on trans=%p", evsql, query, trans);
    evsql_query_debug(command, params);
#endif /* DEBUG_ENABLED */

    // ok
    return query;

error:
    _evsql_query_free(query);
    
    return NULL;
}

struct evsql_query *evsql_query_exec (struct evsql *evsql, struct evsql_trans *trans, 
    const struct evsql_query_info *query_info,
    evsql_query_cb query_fn, void *cb_arg,
    ...
) {
    va_list vargs;
    struct evsql_query *query = NULL;
    const struct evsql_item_info *param;
    size_t count = 0, idx;
    err_t err = 1;

    // varargs
    va_start(vargs, cb_arg);
    
    // alloc new query
    if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL)
        goto error;

    // count the params
    for (param = query_info->params; param->type; param++) 
        count++;
    
    // initialize params
    _evsql_query_params_init_pq(&query->params, count, EVSQL_FMT_BINARY);

    // transform
    for (param = query_info->params, idx = 0; param->type; param++, idx++) {
        // default type to 0 (implicit)
        query->params.types[idx] = 0;

        // default format to binary
        query->params.formats[idx] = EVSQL_FMT_BINARY;

        // consume argument
        switch (param->type) {
            case EVSQL_TYPE_NULL_: {
                // explicit type + text fmt
                query->params.types[idx] = EVSQL_PQ_ARBITRARY_TYPE_OID;
                query->params.values[idx] = NULL;
                query->params.lengths[idx] = 0;
                query->params.formats[idx] = EVSQL_FMT_TEXT;
            } break;

            case EVSQL_TYPE_BINARY: {
                struct evsql_item_binary item = va_arg(vargs, struct evsql_item_binary);
                
                // value + explicit len
                query->params.values[idx] = item.ptr;
                query->params.lengths[idx] = item.len;
            } break;

            case EVSQL_TYPE_STRING: {
                const char *str = va_arg(vargs, const char *);

                // value + automatic length, text format
                query->params.values[idx] = str;
                query->params.lengths[idx] = 0;
                query->params.formats[idx] = EVSQL_FMT_TEXT;
            } break;
            
            case EVSQL_TYPE_UINT16: {
                // XXX: uint16_t is passed as `int'?
                uint16_t uval = va_arg(vargs, int);

                if (uval != (int16_t) uval)
                    ERROR("param $%zu: uint16 overflow: %d", idx + 1, uval);
                
                // network-byte-order value + explicit len
                query->params.item_vals[idx].uint16 = htons(uval);
                query->params.values[idx] = (const char *) &query->params.item_vals[idx];
                query->params.lengths[idx] = sizeof(uint16_t);
            } break;
            
            case EVSQL_TYPE_UINT32: {
                uint32_t uval = va_arg(vargs, uint32_t);

                if (uval != (int32_t) uval)
                    ERROR("param $%zu: uint32 overflow: %ld", idx + 1, (unsigned long) uval);
                
                // network-byte-order value + explicit len
                query->params.item_vals[idx].uint32 = htonl(uval);
                query->params.values[idx] = (const char *) &query->params.item_vals[idx];
                query->params.lengths[idx] = sizeof(uint32_t);
            } break;

            case EVSQL_TYPE_UINT64: {
                uint64_t uval = va_arg(vargs, uint64_t);

                if (uval != (int64_t) uval)
                    ERROR("param $%zu: uint16 overflow: %lld", idx + 1, (unsigned long long) uval);
                
                // network-byte-order value + explicit len
                query->params.item_vals[idx].uint64 = htonq(uval);
                query->params.values[idx] = (const char *) &query->params.item_vals[idx];
                query->params.lengths[idx] = sizeof(uint64_t);
            } break;
            
            default: 
                FATAL("param $%zu: invalid type: %d", idx + 1, param->type);
        }
    }

    // execute it
    if (_evsql_query_enqueue(evsql, trans, query, query_info->sql))
        goto error;
    
    // no error, fallthrough for va_end
    err = 0;

error:
    // possible cleanup
    if (err)
        _evsql_query_free(query);
    
    // end varargs
    va_end(vargs);
    
    // return 
    return err ? NULL : query;
}


void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query) {
    assert(query);

    if (trans) {
        // must be the right query
        assert(trans->query == query);
    }

    // just strip the callback and wait for it to complete as normal
    query->cb_fn = NULL;
}