PyObject *
pycbc_Bucket__end_pipeline(pycbc_Bucket *self)
{
    PyObject *rv;
    int ii;

    if (!self->pipeline_queue) {
        PYCBC_EXC_WRAP(PYCBC_EXC_PIPELINE, 0,
                       "No pipeline in progress");
        return NULL;
    }

    rv = self->pipeline_queue;

    if (!self->nremaining) {
        goto GT_DONE;
    }

    pycbc_oputil_wait_common(self);

    pycbc_assert(self->nremaining == 0);

    for (ii = 0; ii < PyList_GET_SIZE(self->pipeline_queue); ii++) {
        PyObject *retitem;
        pycbc_MultiResult *mres =
                (pycbc_MultiResult *)PyList_GET_ITEM(self->pipeline_queue, ii);

        if (pycbc_multiresult_maybe_raise(mres)) {
            rv = NULL;
            break;
        }

        /** Returns new reference to something */
        retitem = pycbc_multiresult_get_result(mres);
        if (retitem != (PyObject *)mres) {
            PyList_SetItem(self->pipeline_queue, ii, retitem);
        } else {
            Py_DECREF(mres);
        }
    }


    GT_DONE:
    if (rv) {
        Py_INCREF(rv);
        pycbc_assert(rv == self->pipeline_queue);
    }

    Py_XDECREF(self->pipeline_queue);
    self->pipeline_queue = NULL;

    return rv;
}
static PyObject *
make_error_tuple(void)
{
    PyObject *type, *value, *traceback;
    PyObject *ret;

    pycbc_assert(PyErr_Occurred());

    PyErr_Fetch(&type, &value, &traceback);
    PyErr_Clear();

    if (value == NULL) {
        value = Py_None; Py_INCREF(value);
    }
    if (traceback == NULL) {
        traceback = Py_None; Py_INCREF(traceback);
    }

    ret = PyTuple_New(3);
    /** Steal references from PyErr_Fetch() */
    PyTuple_SET_ITEM(ret, 0, type);
    PyTuple_SET_ITEM(ret, 1, value);
    PyTuple_SET_ITEM(ret, 2, traceback);

    return ret;
}
static PyObject *
Bucket__close(pycbc_Bucket *self)
{
    lcb_error_t err;

    if (self->flags & PYCBC_CONN_F_CLOSED) {
        Py_RETURN_NONE;
    }

    self->flags |= PYCBC_CONN_F_CLOSED;

    lcb_destroy(self->instance);

    if (self->iopswrap) {
        Py_XDECREF(self->iopswrap);
        self->iopswrap = NULL;
    }

    err = lcb_create(&self->instance, NULL);
    pycbc_assert(err == LCB_SUCCESS);
    if (err != LCB_SUCCESS) {
        PYCBC_EXC_WRAP(PYCBC_EXC_LCBERR,
                       err,
                       "Internal error while closing object");
        return NULL;
    }

    Py_RETURN_NONE;
}
void
pycbc_schedule_dtor_event(pycbc_Bucket *self)
{
    struct dtor_info_st *dti;

    if ((self->flags & PYCBC_CONN_F_ASYNC_DTOR) == 0) {
        return;
    }

    pycbc_assert(self->instance);
    dti = malloc(sizeof(*dti));
    if (!dti) {
        fprintf(stderr,
                "[PYCBC] Couldn't allocate memory for libcouchbase async "
                "destruction. Instance will leak\n");

    } else {
        dti->iowrap = self->iopswrap;
        dti->dtorcb = self->dtorcb;
        dti->conncb = self->conncb;
    }

    lcb_set_destroy_callback(self->instance, dtor_callback);
    lcb_destroy_async(self->instance, dti);

    self->instance = NULL;
    self->iopswrap = NULL;
    self->dtorcb = NULL;
    self->conncb = NULL;
}
void
pycbc_viewresult_step(pycbc_ViewResult *vres, pycbc_MultiResult *mres,
                      pycbc_Bucket *bucket, int force_callback)
{
    if ((bucket->flags & PYCBC_CONN_F_ASYNC) &&
            should_call_async(vres, force_callback)) {
        pycbc_AsyncResult *ares = (pycbc_AsyncResult*)mres;
        PyObject *args = PyTuple_Pack(1, mres);
        PyObject *result;

        pycbc_assert(ares->callback);

        result = PyObject_CallObject(ares->callback, args);
        Py_XDECREF(result);
        if (!result) {
            PyErr_Print();
        }

        Py_DECREF(args);

        Py_DECREF(vres->rows);
        vres->rows = PyList_New(0);
    }

    if (!bucket->nremaining) {
        lcb_breakout(bucket->instance);
    }
}
static void
maybe_breakout(pycbc_Connection *self)
{
    pycbc_assert(self->nremaining);

    if (!--self->nremaining) {
        lcb_breakout(self->instance);
    }
}
static void
error_callback(lcb_t instance, lcb_error_t err, const char *msg)
{
    PyObject *errtuple;
    PyObject *result;
    pycbc_Connection *self = (pycbc_Connection*) lcb_get_cookie(instance);

    CB_THR_END(self);

    pycbc_assert(self->errors);
    errtuple = Py_BuildValue("(i,s)", err, msg);
    pycbc_assert(errtuple);
    result = PyObject_CallMethod(self->errors, "append", "(O)", errtuple);
    pycbc_assert(result);
    Py_DECREF(errtuple);
    Py_DECREF(result);

    CB_THR_BEGIN(self);
}
Exemple #8
0
/**
 * Fetches a bunch of results from the network. Returns False when
 * no more results remain.
 */
PyObject *
pycbc_HttpResult__fetch(pycbc_HttpResult *self)
{
    lcb_error_t err;
    PyObject *ret = NULL;

    if (-1 == pycbc_oputil_conn_lock(self->parent)) {
        return NULL;
    }

    if (!self->htreq) {
        ret = Py_None;
        Py_INCREF(ret);
        goto GT_RET;
    }

    if (self->parent->flags & PYCBC_CONN_F_ASYNC) {
        PYCBC_EXC_WRAP(PYCBC_EXC_ARGUMENTS, 0,
                       "_fetch() should not be called with an async "
                       "connection");
        goto GT_RET;
    } else if (self->parent->pipeline_queue) {
        PYCBC_EXC_WRAP(PYCBC_EXC_PIPELINE, 0,
                       "HTTP requests cannot be executed in pipeline context");
    }

    if (!self->rowsbuf) {
        self->rowsbuf = PyList_New(0);
    }

    err = pycbc_oputil_wait_common(self->parent);

    if (err != LCB_SUCCESS) {
        PYCBC_EXCTHROW_WAIT(err);
        goto GT_RET;

    } else {
        if (maybe_raise(self)) {
            goto GT_RET;
        }

        ret = self->rowsbuf;
        self->rowsbuf = NULL;
    }

    if (!pycbc_assert(self->parent->nremaining == 0)) {
        fprintf(stderr, "Remaining count unexpected. Adjusting");
        self->parent->nremaining = 0;
    }

    GT_RET:
    pycbc_oputil_conn_unlock(self->parent);
    return ret;
}
static void
invoke_endure_test_notification(pycbc_Bucket *self, pycbc_Result *resp)
{
    PyObject *ret;
    PyObject *argtuple = Py_BuildValue("(O)", resp);
    ret = PyObject_CallObject(self->dur_testhook, argtuple);
    pycbc_assert(ret);

    Py_XDECREF(ret);
    Py_XDECREF(argtuple);
}
static void
cb_thr_begin(pycbc_Bucket *self)
{
    if (Py_REFCNT(self) > 1) {
        Py_DECREF(self);
        PYCBC_CONN_THR_BEGIN(self);
        return;
    }

    pycbc_assert(self->unlock_gil == 0);
    Py_DECREF(self);
}
int
pycbc_tc_encode_key(pycbc_Bucket *conn,
                    PyObject **key,
                    void **buf,
                    size_t *nbuf)
{
    int rv;
    Py_ssize_t plen;

    PyObject *orig_key;
    PyObject *new_key = NULL;

    if (!conn->tc) {
        return encode_common(key, buf, nbuf, PYCBC_FMT_UTF8);
    }

    orig_key = *key;
    pycbc_assert(orig_key);

    rv = do_call_tc(conn, orig_key, NULL, &new_key, ENCODE_KEY);

    if (new_key == NULL || rv < 0) {
        return -1;
    }

    rv = PyBytes_AsStringAndSize(new_key, (char**)buf, &plen);

    if (rv == -1) {
        PYCBC_EXC_WRAP_KEY(PYCBC_EXC_ENCODING,
                           0,
                           "Couldn't convert encoded key to bytes. It is "
                           "possible that the Transcoder.encode_key method "
                           "returned an unexpected value",
                           new_key);

        Py_XDECREF(new_key);
        return -1;
    }

    if (plen == 0) {
        PYCBC_EXC_WRAP_KEY(PYCBC_EXC_ENCODING,
                           0,
                           "Transcoder.encode_key returned an empty string",
                           new_key);
        Py_XDECREF(new_key);
        return -1;
    }

    *nbuf = plen;
    *key = new_key;
    return 0;
}
static void
destroy_event_common(lcb_io_opt_t io, void *arg)
{
    pycbc_Event *ev = arg;
    lcb_U32 dummy = 0;
    pycbc_assert(ev->state != PYCBC_EVSTATE_ACTIVE);

    modify_event_python(PYCBC_IOW_FROM_IOPS(io), ev, PYCBC_EVACTION_CLEANUP,
                        0, &dummy);

    ev->state = PYCBC_EVSTATE_FREED;
    Py_DECREF(ev);
}
Exemple #13
0
static void
add_data(pycbc_HttpResult *htres, const void *data, size_t ndata)
{
    PyObject *o;

    if (!ndata) {
        return;
    }

    if (htres->format == PYCBC_FMT_JSON) {
        o = PyUnicode_FromStringAndSize(data, ndata);

    } else {
        o = PyBytes_FromStringAndSize(data, ndata);
    }

    pycbc_assert(htres->http_data);
    pycbc_assert(o);

    PyList_Append(htres->http_data, o);
    Py_XDECREF(o);
}
/**
 * This is only called if 'o' is not bytes
 */
static PyObject*
convert_to_bytesobj(PyObject *o)
{
    PyObject *bytesobj = NULL;
    pycbc_assert(!PyBytes_Check(o));

    if (PyUnicode_Check(o)) {
        bytesobj = PyUnicode_AsUTF8String(o);
    }

    if (!bytesobj) {
        PYCBC_EXC_WRAP_OBJ(PYCBC_EXC_ENCODING,
                           0, "Couldn't convert object to bytes",
                           o);
    }
    return bytesobj;
}
/**
 * Fetches a bunch of results from the network. Returns False when
 * no more results remain.
 */
PyObject *
pycbc_HttpResult__fetch(pycbc_HttpResult *self)
{
    lcb_error_t err;
    PyObject *ret = NULL;

    if (-1 == pycbc_oputil_conn_lock(self->parent)) {
        return NULL;
    }

    if (!self->htreq) {
        ret = Py_None;
        Py_INCREF(ret);
        goto GT_RET;
    }

    if (!self->rowsbuf) {
        self->rowsbuf = PyList_New(0);
    }

    err = pycbc_oputil_wait_common(self->parent);

    if (err != LCB_SUCCESS) {
        PYCBC_EXCTHROW_WAIT(err);
        goto GT_RET;

    } else {
        if (maybe_raise(self)) {
            goto GT_RET;
        }

        ret = self->rowsbuf;
        self->rowsbuf = NULL;
    }

    if (!pycbc_assert(self->parent->nremaining == 0)) {
        fprintf(stderr, "Remaining count unexpected. Adjusting");
        self->parent->nremaining = 0;
    }

    GT_RET:
    pycbc_oputil_conn_unlock(self->parent);
    return ret;
}
/**
 * This callback does things a bit differently.
 * Instead of using a MultiResult, we use a single HttpResult object.
 * We won't ever have "multiple" http objects.
 */
static void
http_complete_callback(lcb_http_request_t req,
                       lcb_t instance,
                       const void *cookie,
                       lcb_error_t err,
                       const lcb_http_resp_t *resp)
{
    pycbc_HttpResult *htres = (pycbc_HttpResult*)cookie;

    htres->htreq = NULL;

    if (!htres->parent) {
        return;
    }

    htres->rc = err;
    htres->htcode = resp->v.v0.status;

    if (htres->htflags & PYCBC_HTRES_F_CHUNKED) {
        /** No data here */
        if (!pycbc_assert(resp->v.v0.nbytes == 0)) {
            fprintf(stderr, "Unexpected payload in HTTP response callback\n");
        }

        if (!htres->parent->nremaining) {
            lcb_breakout(instance);
        }
        return;
    }


    PYCBC_CONN_THR_END(htres->parent);

    if (!--htres->parent->nremaining) {
        lcb_breakout(instance);
    }

    get_data(htres, resp->v.v0.bytes, resp->v.v0.nbytes);
    get_headers(htres, resp);

    PYCBC_CONN_THR_BEGIN(htres->parent);
    (void)instance;
    (void)req;
}
Exemple #17
0
static void
finalize_data(pycbc_HttpResult *htres)
{
    PyObject *sep;
    PyObject *res;

    if (htres->format != PYCBC_FMT_JSON && htres->format != PYCBC_FMT_UTF8) {
        return;
    }

    if (!PyObject_IsTrue(htres->http_data)) {
        return;
    }

    sep = pycbc_SimpleStringZ("");
    res = PyUnicode_Join(sep, htres->http_data);

    Py_DECREF(sep);

    /* Set the bytes */
    Py_DECREF(htres->http_data);
    htres->http_data = res;


    if (htres->format == PYCBC_FMT_JSON) {
        PyObject *args = Py_BuildValue("(O)", htres->http_data);

        pycbc_assert(PyErr_Occurred() == NULL);
        res = PyObject_CallObject(pycbc_helpers.json_decode, args);

        if (res) {
            Py_XDECREF(htres->http_data);
            htres->http_data = res;

        } else {
            PyErr_Clear();
        }

        Py_XDECREF(args);
    }
}
static void
operation_completed(pycbc_Bucket *self, pycbc_MultiResult *mres)
{
    pycbc_assert(self->nremaining);
    --self->nremaining;

    if ((self->flags & PYCBC_CONN_F_ASYNC) == 0) {
        if (!self->nremaining) {
            lcb_breakout(self->instance);
        }
        return;
    }

    if (mres) {
        pycbc_AsyncResult *ares;
        ares = (pycbc_AsyncResult *)mres;
        if (--ares->nops) {
            return;
        }
        pycbc_asyncresult_invoke(ares);
    }
}
/**
 * Call this function for each callback. Note that even if this function
 * returns nonzero, CB_THR_BEGIN() must still be called, and the `conn`
 * and `mres` out parameters are considered valid
 * @param resp base response object
 * @param[out] conn the bucket object
 * @param[out] res the result object for the individual operation
 * @param restype What type should `res` be if it needs to be created
 * @param[out] mres the context for the current operation
 * @return 0 if operation processing may proceed, nonzero if operation
 * processing has completed. In both cases the `conn` and `mres` paramters
 * are valid, however.
 */
static int
get_common_objects(const lcb_RESPBASE *resp, pycbc_Bucket **conn,
    pycbc_Result **res, int restype, pycbc_MultiResult **mres)

{
    PyObject *hkey;
    PyObject *mrdict;
    int rv;

    pycbc_assert(pycbc_multiresult_check(resp->cookie));
    *mres = (pycbc_MultiResult*)resp->cookie;
    *conn = (*mres)->parent;

    CB_THR_END(*conn);

    rv = pycbc_tc_decode_key(*conn, resp->key, resp->nkey, &hkey);

    if (rv < 0) {
        pycbc_multiresult_adderr(*mres);
        return -1;
    }

    mrdict = pycbc_multiresult_dict(*mres);

    *res = (pycbc_Result*)PyDict_GetItem(mrdict, hkey);

    if (*res) {
        int exists_ok = (restype & RESTYPE_EXISTS_OK) ||
                ( (*mres)->mropts & PYCBC_MRES_F_UALLOCED);

        if (!exists_ok) {
            if ((*conn)->flags & PYCBC_CONN_F_WARNEXPLICIT) {
                PyErr_WarnExplicit(PyExc_RuntimeWarning,
                                   "Found duplicate key",
                                   __FILE__, __LINE__,
                                   "couchbase._libcouchbase",
                                   NULL);

            } else {
                PyErr_WarnEx(PyExc_RuntimeWarning,
                             "Found duplicate key",
                             1);
            }
            /**
             * We need to destroy the existing object and re-create it.
             */
            PyDict_DelItem(mrdict, hkey);
            *res = NULL;

        } else {
            Py_XDECREF(hkey);
        }
    }

    if (*res == NULL) {
        /* Now, get/set the result object */
        if ( (*mres)->mropts & PYCBC_MRES_F_ITEMS) {
            *res = (pycbc_Result*)pycbc_item_new(*conn);

        } else if (restype & RESTYPE_BASE) {
            *res = (pycbc_Result*)pycbc_result_new(*conn);

        } else if (restype & RESTYPE_OPERATION) {
            *res = (pycbc_Result*)pycbc_opresult_new(*conn);

        } else if (restype & RESTYPE_VALUE) {
            *res = (pycbc_Result*)pycbc_valresult_new(*conn);

        } else {
            abort();
        }

        PyDict_SetItem(mrdict, hkey, (PyObject*)*res);

        (*res)->key = hkey;
        Py_DECREF(*res);
    }

    if (resp->rc) {
        (*res)->rc = resp->rc;
    }

    if (resp->rc != LCB_SUCCESS) {
        (*mres)->all_ok = 0;
    }

    return 0;
}
static int
encode_common(PyObject **o, void **buf, size_t *nbuf, lcb_uint32_t flags)
{
    PyObject *bytesobj;
    Py_ssize_t plen;
    int rv;

    if (flags == PYCBC_FMT_UTF8) {
#if PY_MAJOR_VERSION == 2
        if (PyString_Check(*o)) {
#else
        if (0) {
#endif
            bytesobj = *o;
            Py_INCREF(*o);
        } else {
            if (!PyUnicode_Check(*o)) {
                PYCBC_EXC_WRAP_OBJ(PYCBC_EXC_ENCODING,
                                   0, "Must be unicode or string", *o);
                return -1;
            }
            bytesobj = PyUnicode_AsUTF8String(*o);
        }

    } else if (flags == PYCBC_FMT_BYTES) {
        if (PyBytes_Check(*o) || PyByteArray_Check(*o)) {
            bytesobj = *o;
            Py_INCREF(*o);

        } else {
            PYCBC_EXC_WRAP_OBJ(PYCBC_EXC_ENCODING, 0,
                               "Must be bytes or bytearray", *o);
            return -1;
        }

    } else {
        PyObject *args = NULL;
        PyObject *helper;

        if (flags == PYCBC_FMT_PICKLE) {
            helper = pycbc_helpers.pickle_encode;

        } else if (flags == PYCBC_FMT_JSON) {
            helper = pycbc_helpers.json_encode;

        } else {
            PYCBC_EXC_WRAP(PYCBC_EXC_ARGUMENTS, 0, "Unrecognized format");
            return -1;
        }

        args = PyTuple_Pack(1, *o);
        bytesobj = PyObject_CallObject(helper, args);
        Py_DECREF(args);

        if (!bytesobj) {
            PYCBC_EXC_WRAP_OBJ(PYCBC_EXC_ENCODING,
                               0, "Couldn't encode value", *o);
            return -1;
        }

        if (!PyBytes_Check(bytesobj)) {
            PyObject *old = bytesobj;
            bytesobj = convert_to_bytesobj(old);
            Py_DECREF(old);
            if (!bytesobj) {
                return -1;
            }
        }
    }

    if (PyByteArray_Check(bytesobj)) {
        *buf = PyByteArray_AS_STRING(bytesobj);
        plen = PyByteArray_GET_SIZE(bytesobj);
        rv = 0;
    } else {
        rv = PyBytes_AsStringAndSize(bytesobj, (char**)buf, &plen);
    }

    if (rv < 0) {
        Py_DECREF(bytesobj);
        PYCBC_EXC_WRAP(PYCBC_EXC_ENCODING, 0, "Couldn't encode value");
        return -1;
    }

    *nbuf = plen;
    *o = bytesobj;
    return 0;
}

static int
decode_common(PyObject **vp, const char *buf, size_t nbuf, lcb_uint32_t flags)
{
    PyObject *decoded = NULL;
    lcb_U32 c_flags, l_flags;

    c_flags = flags & PYCBC_FMT_COMMON_MASK;
    l_flags = flags & PYCBC_FMT_LEGACY_MASK;

    #define FMT_MATCHES(fmtbase) \
        (c_flags == PYCBC_FMT_COMMON_##fmtbase || l_flags == PYCBC_FMT_LEGACY_##fmtbase)

    if (FMT_MATCHES(UTF8)) {
        decoded = convert_to_string(buf, nbuf, CONVERT_MODE_UTF8_ONLY);
        if (!decoded) {
            return -1;
        }

    } else if (FMT_MATCHES(BYTES)) {
        GT_BYTES:
        decoded = convert_to_string(buf, nbuf, CONVERT_MODE_BYTES_ONLY);
        pycbc_assert(decoded);

    } else {
        PyObject *converter = NULL;
        PyObject *args = NULL;
        PyObject *first_arg = NULL;

        if (FMT_MATCHES(PICKLE)) {
            converter = pycbc_helpers.pickle_decode;
            first_arg = convert_to_string(buf, nbuf, CONVERT_MODE_BYTES_ONLY);
            pycbc_assert(first_arg);

        } else if (FMT_MATCHES(JSON)) {
            converter = pycbc_helpers.json_decode;
            first_arg = convert_to_string(buf, nbuf, CONVERT_MODE_UTF8_ONLY);

            if (!first_arg) {
                return -1;
            }

        } else {
            PyErr_Warn(PyExc_UserWarning, "Unrecognized flags. Forcing bytes");
            goto GT_BYTES;
        }

        pycbc_assert(first_arg);
        args = PyTuple_Pack(1, first_arg);
        decoded = PyObject_CallObject(converter, args);

        Py_DECREF(args);
        Py_DECREF(first_arg);
    }

    if (!decoded) {
        PyObject *bytes_tmp = PyBytes_FromStringAndSize(buf, nbuf);
        PYCBC_EXC_WRAP_OBJ(PYCBC_EXC_ENCODING, 0, "Failed to decode bytes",
                           bytes_tmp);
        Py_XDECREF(bytes_tmp);
        return -1;
    }

    *vp = decoded;
    return 0;

    #undef FMT_MATCHES
}
static int
get_common_objects(PyObject *cookie,
                   const void *key,
                   size_t nkey,
                   lcb_error_t err,
                   pycbc_Connection **conn,
                   pycbc_Result **res,
                   int restype,
                   pycbc_MultiResult **mres)

{
    PyObject *hkey;
    int rv;

    pycbc_assert(Py_TYPE(cookie) == &pycbc_MultiResultType);
    *mres = (pycbc_MultiResult*)cookie;
    *conn = (*mres)->parent;

    if (!(restype & RESTYPE_VARCOUNT)) {
        maybe_breakout(*conn);
    }

    CB_THR_END(*conn);

    rv = pycbc_tc_decode_key(*conn, key, nkey, &hkey);

    if (rv < 0) {
        push_fatal_error(*mres);
        return -1;
    }

    *res = (pycbc_Result*)PyDict_GetItem((PyObject*)*mres, hkey);

    if (*res) {
        int exists_ok = (restype & RESTYPE_EXISTS_OK) ||
                ( (*mres)->mropts & PYCBC_MRES_F_UALLOCED);

        if (!exists_ok) {
            if ((*conn)->flags & PYCBC_CONN_F_WARNEXPLICIT) {
                PyErr_WarnExplicit(PyExc_RuntimeWarning,
                                   "Found duplicate key",
                                   __FILE__, __LINE__,
                                   "couchbase._libcouchbase",
                                   NULL);

            } else {
                PyErr_WarnEx(PyExc_RuntimeWarning,
                             "Found duplicate key",
                             1);
            }
            /**
             * We need to destroy the existing object and re-create it.
             */
            PyDict_DelItem((PyObject*)*mres, hkey);
            *res = NULL;

        } else {
            Py_XDECREF(hkey);
        }

    }

    if (*res == NULL) {
        /**
         * Now, get/set the result object
         */
        if ( (*mres)->mropts & PYCBC_MRES_F_ITEMS) {
            *res = (pycbc_Result*)pycbc_item_new(*conn);

        } else if (restype & RESTYPE_BASE) {
            *res = (pycbc_Result*)pycbc_result_new(*conn);

        } else if (restype & RESTYPE_OPERATION) {
            *res = (pycbc_Result*)pycbc_opresult_new(*conn);

        } else if (restype & RESTYPE_VALUE) {
            *res = (pycbc_Result*)pycbc_valresult_new(*conn);

        } else {
            abort();
        }

        PyDict_SetItem((PyObject*)*mres, hkey, (PyObject*)*res);

        (*res)->key = hkey;
        Py_DECREF(*res);
    }

    if (err) {
        (*res)->rc = err;
    }

    if (err != LCB_SUCCESS) {
        (*mres)->all_ok = 0;
    }

    return 0;
}