terom@44: terom@58: #include "internal.h" terom@58: #include "lib/error.h" terom@58: #include "lib/misc.h" terom@45: terom@45: #include terom@45: #include terom@44: terom@44: /* terom@44: * Initialize params->types/values/lengths/formats, params->count, params->result_format based on the given args terom@44: */ terom@44: static int _evsql_query_params_init_pq (struct evsql_query_params_pq *params, size_t param_count, enum evsql_item_format result_format) { terom@44: // set count terom@44: params->count = param_count; terom@44: terom@44: // allocate vertical storage for the parameters terom@44: if (0 terom@44: terom@45: || !(params->types = calloc(param_count, sizeof(Oid))) terom@45: || !(params->values = calloc(param_count, sizeof(char *))) terom@45: || !(params->lengths = calloc(param_count, sizeof(int))) terom@45: || !(params->formats = calloc(param_count, sizeof(int))) terom@45: || !(params->item_vals = calloc(param_count, sizeof(union evsql_item_value))) terom@44: ) terom@44: ERROR("calloc"); terom@44: terom@44: // result format terom@44: switch (result_format) { terom@44: case EVSQL_FMT_TEXT: terom@45: params->result_format = 0; break; terom@44: terom@44: case EVSQL_FMT_BINARY: terom@45: params->result_format = 1; break; terom@44: terom@44: default: terom@44: FATAL("params.result_fmt: %d", result_format); terom@44: } terom@44: terom@44: // good terom@44: return 0; terom@44: terom@44: error: terom@44: return -1; terom@44: } terom@44: terom@44: struct evsql_query *evsql_query (struct evsql *evsql, struct evsql_trans *trans, const char *command, evsql_query_cb query_fn, void *cb_arg) { terom@44: struct evsql_query *query = NULL; terom@44: terom@44: // alloc new query terom@44: if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL) terom@44: goto error; terom@44: terom@44: // just execute the command string directly terom@44: if (_evsql_query_enqueue(evsql, trans, query, command)) terom@44: goto error; terom@44: terom@44: // ok terom@44: return query; terom@44: terom@44: error: terom@44: _evsql_query_free(query); terom@44: terom@44: return NULL; terom@44: } terom@44: terom@44: struct evsql_query *evsql_query_params (struct evsql *evsql, struct evsql_trans *trans, terom@44: const char *command, const struct evsql_query_params *params, terom@44: evsql_query_cb query_fn, void *cb_arg terom@44: ) { terom@44: struct evsql_query *query = NULL; terom@44: const struct evsql_item *param; terom@44: size_t count = 0, idx; terom@44: terom@44: // alloc new query terom@44: if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL) terom@44: goto error; terom@44: terom@44: // count the params terom@44: for (param = params->list; param->info.type; param++) terom@44: count++; terom@44: terom@44: // initialize params terom@45: _evsql_query_params_init_pq(&query->params, count, params->result_format); terom@44: terom@44: // transform terom@44: for (param = params->list, idx = 0; param->info.type; param++, idx++) { terom@48: // set for NULLs, otherwise not terom@48: query->params.types[idx] = (param->bytes || param->flags.has_value) ? 0 : EVSQL_PQ_ARBITRARY_TYPE_OID; terom@45: terom@45: // scalar values terom@45: query->params.item_vals[idx] = param->value; terom@44: terom@44: // values terom@45: // point this at the value stored in the item_vals union if flagged as such terom@45: query->params.values[idx] = param->flags.has_value ? (const char *) &query->params.item_vals[idx] : param->bytes; terom@44: terom@44: // lengths terom@44: query->params.lengths[idx] = param->length; terom@48: terom@48: // XXX: this assumes that format is FMT_BINARY... terom@48: query->params.formats[idx] = param->info.format; terom@44: } terom@44: terom@44: // execute it terom@44: if (_evsql_query_enqueue(evsql, trans, query, command)) terom@44: goto error; terom@44: terom@44: #ifdef DEBUG_ENABLED terom@44: // debug it? terom@44: DEBUG("evsql.%p: enqueued query=%p on trans=%p", evsql, query, trans); terom@44: evsql_query_debug(command, params); terom@44: #endif /* DEBUG_ENABLED */ terom@44: terom@44: // ok terom@44: return query; terom@44: terom@44: error: terom@44: _evsql_query_free(query); terom@44: terom@44: return NULL; terom@44: } terom@44: terom@44: struct evsql_query *evsql_query_exec (struct evsql *evsql, struct evsql_trans *trans, terom@44: const struct evsql_query_info *query_info, terom@44: evsql_query_cb query_fn, void *cb_arg, terom@44: ... terom@44: ) { terom@44: va_list vargs; terom@44: struct evsql_query *query = NULL; terom@44: const struct evsql_item_info *param; terom@44: size_t count = 0, idx; terom@44: err_t err = 1; terom@44: terom@44: // varargs terom@44: va_start(vargs, cb_arg); terom@44: terom@44: // alloc new query terom@44: if ((query = _evsql_query_new(evsql, trans, query_fn, cb_arg)) == NULL) terom@44: goto error; terom@44: terom@44: // count the params terom@44: for (param = query_info->params; param->type; param++) terom@44: count++; terom@44: terom@44: // initialize params terom@45: _evsql_query_params_init_pq(&query->params, count, EVSQL_FMT_BINARY); terom@44: terom@44: // transform terom@45: for (param = query_info->params, idx = 0; param->type; param++, idx++) { terom@44: // default type to 0 (implicit) terom@44: query->params.types[idx] = 0; terom@44: terom@44: // default format to binary terom@44: query->params.formats[idx] = EVSQL_FMT_BINARY; terom@44: terom@44: // consume argument terom@45: switch (param->type) { terom@44: case EVSQL_TYPE_NULL_: { terom@44: // explicit type + text fmt terom@44: query->params.types[idx] = EVSQL_PQ_ARBITRARY_TYPE_OID; terom@44: query->params.values[idx] = NULL; terom@44: query->params.lengths[idx] = 0; terom@44: query->params.formats[idx] = EVSQL_FMT_TEXT; terom@44: } break; terom@44: terom@44: case EVSQL_TYPE_BINARY: { terom@44: struct evsql_item_binary item = va_arg(vargs, struct evsql_item_binary); terom@44: terom@44: // value + explicit len terom@45: query->params.values[idx] = item.ptr; terom@45: query->params.lengths[idx] = item.len; terom@44: } break; terom@44: terom@44: case EVSQL_TYPE_STRING: { terom@44: const char *str = va_arg(vargs, const char *); terom@44: terom@44: // value + automatic length, text format terom@44: query->params.values[idx] = str; terom@44: query->params.lengths[idx] = 0; terom@44: query->params.formats[idx] = EVSQL_FMT_TEXT; terom@44: } break; terom@44: terom@44: case EVSQL_TYPE_UINT16: { terom@45: // XXX: uint16_t is passed as `int'? terom@45: uint16_t uval = va_arg(vargs, int); terom@44: terom@44: if (uval != (int16_t) uval) terom@44: ERROR("param $%zu: uint16 overflow: %d", idx + 1, uval); terom@44: terom@44: // network-byte-order value + explicit len terom@45: query->params.item_vals[idx].uint16 = htons(uval); terom@45: query->params.values[idx] = (const char *) &query->params.item_vals[idx]; terom@44: query->params.lengths[idx] = sizeof(uint16_t); terom@44: } break; terom@44: terom@44: case EVSQL_TYPE_UINT32: { terom@44: uint32_t uval = va_arg(vargs, uint32_t); terom@44: terom@44: if (uval != (int32_t) uval) terom@45: ERROR("param $%zu: uint32 overflow: %ld", idx + 1, (unsigned long) uval); terom@44: terom@44: // network-byte-order value + explicit len terom@45: query->params.item_vals[idx].uint32 = htonl(uval); terom@45: query->params.values[idx] = (const char *) &query->params.item_vals[idx]; terom@44: query->params.lengths[idx] = sizeof(uint32_t); terom@44: } break; terom@44: terom@44: case EVSQL_TYPE_UINT64: { terom@44: uint64_t uval = va_arg(vargs, uint64_t); terom@44: terom@44: if (uval != (int64_t) uval) terom@45: ERROR("param $%zu: uint16 overflow: %lld", idx + 1, (unsigned long long) uval); terom@44: terom@44: // network-byte-order value + explicit len terom@45: query->params.item_vals[idx].uint64 = htonq(uval); terom@45: query->params.values[idx] = (const char *) &query->params.item_vals[idx]; terom@44: query->params.lengths[idx] = sizeof(uint64_t); terom@44: } break; terom@44: terom@44: default: terom@45: FATAL("param $%zu: invalid type: %d", idx + 1, param->type); terom@44: } terom@44: } terom@44: terom@44: // execute it terom@45: if (_evsql_query_enqueue(evsql, trans, query, query_info->sql)) terom@44: goto error; terom@44: terom@44: // no error, fallthrough for va_end terom@44: err = 0; terom@44: terom@44: error: terom@44: // possible cleanup terom@44: if (err) terom@44: _evsql_query_free(query); terom@44: terom@44: // end varargs terom@44: va_end(vargs); terom@44: terom@44: // return terom@44: return err ? NULL : query; terom@44: } terom@44: terom@44: terom@44: void evsql_query_abort (struct evsql_trans *trans, struct evsql_query *query) { terom@44: assert(query); terom@44: terom@44: if (trans) { terom@44: // must be the right query terom@44: assert(trans->query == query); terom@44: } terom@44: terom@44: // just strip the callback and wait for it to complete as normal terom@44: query->cb_fn = NULL; terom@44: } terom@44: