<commit_before>/**
 *     Copyright 2013 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 **/

#include "pycbc.h"
#include "pycbc_http.h"
#define CB_THREADS

#ifdef CB_THREADS

static void
cb_thr_end(pycbc_Bucket *self)
{
    PYCBC_CONN_THR_END(self);
    Py_INCREF((PyObject *)self);
}

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);
}

#define CB_THR_END                 \
    PYCBC_DEBUG_LOG("cb_thr_end"); \
    cb_thr_end
#define CB_THR_BEGIN                 \
    PYCBC_DEBUG_LOG("cb_thr_begin"); \
    cb_thr_begin
#else
#define CB_THR_END(x)
#define CB_THR_BEGIN(x)
#endif


enum {
    RESTYPE_BASE = 1 << 0,
    RESTYPE_VALUE = 1 << 1,
    RESTYPE_OPERATION = 1 << 2,

    /* Extra flag indicating it's ok if it already exists */
    RESTYPE_EXISTS_OK = 1 << 3,

    /* Don't modify "remaining" count */
    RESTYPE_VARCOUNT = 1 << 4,

    /* return an array (whose value you can indicate
     * with one of the flags above) */
    RESTYPE_ARRAY = 1 << 5
};

/* Returns true if an error has been added... */

static int maybe_push_operr(pycbc_MultiResult *mres,
                            pycbc_Result *res,
                            lcb_STATUS err,
                            int check_enoent,
                            pycbc_debug_info debug_info)
{
    pycbc_stack_context_handle parent_context =
            res ? (res->tracing_context ? res->tracing_context->parent : NULL)
                : NULL;
    PYCBC_DEBUG_LOG_CONTEXT(parent_context, "maybe_push_operr")
    if (err == LCB_SUCCESS || mres->errop) {
        return 0;
    }
    if (parent_context) {
        PYCBC_DEBUG_LOG_CONTEXT(parent_context, "maybe_push_operr")
        pycbc_Result_propagate_context(
                res, res->tracing_context, mres ? mres->parent : NULL);
    }
    if (check_enoent && (mres->mropts & PYCBC_MRES_F_QUIET) &&
        (err == LCB_ERR_DOCUMENT_NOT_FOUND ||
         err == LCB_ERR_SUBDOC_PATH_NOT_FOUND)) {
        return 0;
    }

    mres->errop = (PyObject *)res;
    if (pycbc_debug_info_is_valid(&debug_info)) {
        PyObject *py_debug_info = NULL;
        if (!res->tracing_output) {
            res->tracing_output = PyDict_New();
        }
        {
            py_debug_info = PyDict_GetItemString(res->tracing_output,
                                                 PYCBC_DEBUG_INFO_STR);
            if (!py_debug_info) {
                py_debug_info = PyDict_New();
                PyDict_SetItemString(res->tracing_output,
                                     PYCBC_DEBUG_INFO_STR,
                                     py_debug_info);
            }
            pycbc_dict_add_text_kv(py_debug_info, "FILE", debug_info.FILE);
            pycbc_dict_add_text_kv(py_debug_info, "FUNC", debug_info.FUNC);
            pycbc_set_kv_ull_str(
                    py_debug_info, "LINE", (lcb_uint64_t)debug_info.LINE);
            PYCBC_DECREF(py_debug_info);
        };
    }
    Py_INCREF(mres->errop);
    return 1;
}

pycbc_debug_info pycbc_build_debug_info(const char *FILE,
                                        const char *FUNC,
                                        int line)
{
    return (pycbc_debug_info){FILE, FUNC, line};
}

#define PYCBC_BUILD_DEBUG_INFO \
    pycbc_build_debug_info(__FILE__, __FUNCTION_NAME__, __LINE__)
#define MAYBE_PUSH_OPERR(mres, res, err, check_enoent) \
    maybe_push_operr(mres, res, err, check_enoent, PYCBC_BUILD_DEBUG_INFO)

static void operation_completed3(pycbc_Bucket *self,
                                 pycbc_MultiResult *mres,
                                 pycbc_enhanced_err_info *err_info)
{
    pycbc_assert(self->nremaining);
    --self->nremaining;
    if (mres) {
        mres->err_info = err_info;
        Py_XINCREF(err_info);
    }
    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, err_info);
    }
}

void pycbc_enhanced_err_register_entry(PyObject **dict,
                                       const char *key,
                                       const char *value)<commit_msg>PYCBC-1029: Fix memory leak with KV multi() methods<commit_after>/**
 *     Copyright 2013 Couchbase, Inc.
 *
 *   Licensed under the Apache License, Version 2.0 (the "License");
 *   you may not use this file except in compliance with the License.
 *   You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *   Unless required by applicable law or agreed to in writing, software
 *   distributed under the License is distributed on an "AS IS" BASIS,
 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *   See the License for the specific language governing permissions and
 *   limitations under the License.
 **/

#include "pycbc.h"
#include "pycbc_http.h"
#define CB_THREADS

#ifdef CB_THREADS

static void
cb_thr_end(pycbc_Bucket *self)
{
    PYCBC_CONN_THR_END(self);
    Py_INCREF((PyObject *)self);
}

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);
}

#define CB_THR_END                 \
    PYCBC_DEBUG_LOG("cb_thr_end"); \
    cb_thr_end
#define CB_THR_BEGIN                 \
    PYCBC_DEBUG_LOG("cb_thr_begin"); \
    cb_thr_begin
#else
#define CB_THR_END(x)
#define CB_THR_BEGIN(x)
#endif


enum {
    RESTYPE_BASE = 1 << 0,
    RESTYPE_VALUE = 1 << 1,
    RESTYPE_OPERATION = 1 << 2,

    /* Extra flag indicating it's ok if it already exists */
    RESTYPE_EXISTS_OK = 1 << 3,

    /* Don't modify "remaining" count */
    RESTYPE_VARCOUNT = 1 << 4,

    /* return an array (whose value you can indicate
     * with one of the flags above) */
    RESTYPE_ARRAY = 1 << 5
};

/* Returns true if an error has been added... */

static int maybe_push_operr(pycbc_MultiResult *mres,
                            pycbc_Result *res,
                            lcb_STATUS err,
                            int check_enoent,
                            pycbc_debug_info debug_info)
{
    pycbc_stack_context_handle parent_context =
            res ? (res->tracing_context ? res->tracing_context->parent : NULL)
                : NULL;
    PYCBC_DEBUG_LOG_CONTEXT(parent_context, "maybe_push_operr")
    if (err == LCB_SUCCESS || mres->errop) {
        return 0;
    }
    if (parent_context) {
        PYCBC_DEBUG_LOG_CONTEXT(parent_context, "maybe_push_operr")
        pycbc_Result_propagate_context(
                res, res->tracing_context, mres ? mres->parent : NULL);
    }
    if (check_enoent && (mres->mropts & PYCBC_MRES_F_QUIET) &&
        (err == LCB_ERR_DOCUMENT_NOT_FOUND ||
         err == LCB_ERR_SUBDOC_PATH_NOT_FOUND)) {
        return 0;
    }

    mres->errop = (PyObject *)res;
    if (pycbc_debug_info_is_valid(&debug_info)) {
        PyObject *py_debug_info = NULL;
        if (!res->tracing_output) {
            res->tracing_output = PyDict_New();
        }
        {
            py_debug_info = PyDict_GetItemString(res->tracing_output,
                                                 PYCBC_DEBUG_INFO_STR);
            if (!py_debug_info) {
                py_debug_info = PyDict_New();
                PyDict_SetItemString(res->tracing_output,
                                     PYCBC_DEBUG_INFO_STR,
                                     py_debug_info);
            }
            pycbc_dict_add_text_kv(py_debug_info, "FILE", debug_info.FILE);
            pycbc_dict_add_text_kv(py_debug_info, "FUNC", debug_info.FUNC);
            pycbc_set_kv_ull_str(
                    py_debug_info, "LINE", (lcb_uint64_t)debug_info.LINE);
            PYCBC_DECREF(py_debug_info);
        };
    }
    Py_INCREF(mres->errop);
    return 1;
}

pycbc_debug_info pycbc_build_debug_info(const char *FILE,
                                        const char *FUNC,
                                        int line)
{
    return (pycbc_debug_info){FILE, FUNC, line};
}

#define PYCBC_BUILD_DEBUG_INFO \
    pycbc_build_debug_info(__FILE__, __FUNCTION_NAME__, __LINE__)
#define MAYBE_PUSH_OPERR(mres, res, err, check_enoent) \
    maybe_push_operr(mres, res, err, check_enoent, PYCBC_BUILD_DEBUG_INFO)

static void operation_completed3(pycbc_Bucket *self,
                                 pycbc_MultiResult *mres,
                                 pycbc_enhanced_err_info *err_info)
{
    pycbc_assert(self->nremaining);
    --self->nremaining;
    if (mres) {
        /**
         * If reusing the multi-result, decrement the error info
         * prior to resetting to a new value, otherwise the 
         * previous error info dict will leak.
         */
        if(mres->err_info != NULL){
            Py_XDECREF(mres->err_info);
        }
        mres->err_info = err_info;
        Py_XINCREF(err_info);
    }
    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, err_info);
    }
}

void pycbc_enhanced_err_register_entry(PyObject **dict,
                                       const char *key,
                                       const char *value)
{'fraction_non_alphanumeric': 0.09397730578993307, 'fraction_numerical': 0.006012995829696441, 'mean_word_length': 2.039198349543177, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 49, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 22, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '756037', 'n_tokens_mistral': 3518, 'n_tokens_neox': 3379, 'n_words': 700}