Bug #120165 mysql connector c xapi mysqlx_free wont' free all execute memory
Submitted: 27 Mar 7:24
Reporter: 芳彬 杨 Email Updates:
Status: Open Impact on me:
None 
Category:Connector / C Severity:S2 (Serious)
Version:9.6.0 and 8.0.33 OS:Any
Assigned to: CPU Architecture:Any
Tags: connector

[27 Mar 7:24] 芳彬 杨
Description:
1 using c xapi "mysqlx_get_session_from_options" create a session
2 using "mysqlx_sql execute" any statement such as "show databases;"
3 using "mysqlx_free" free the step 2's results
4 repeate step 2-3 many times, the memory will increase slowly...until the session closed

5 linux and windows both has problem

How to repeat:
here is my test code: run memoryTestOne and look up the memory occupy

bool MysqlApiTest::connect(mysqlx_session_t*& session_t, const bool withOpenssl,unsigned int& errcode, std::string& errstring)
{
    if (session_t != NULL)
    {
        std::cout << "session_t not null release first\n";
        try
        {
            mysqlx_session_close(session_t);
            session_t = NULL;
        }
        catch (...)
        {
            std::cout << "mysqlx_session_close(session_t); exception";
        }
        session_t = NULL;
    }

    errcode = 0;
    errstring = "";
    mysqlx_session_options_t *opt = NULL;
    mysqlx_error_t *error = NULL;
    opt = mysqlx_session_options_new();

    int optRes;
    if (withOpenssl)
    {
        optRes = mysqlx_session_option_set(opt,
            OPT_HOST(mIp.c_str()), OPT_PORT(mPort),
            OPT_USER(mUser.c_str()), OPT_PWD(mPwd.c_str()),
            OPT_DB(mDbName.c_str()),
            //OPT_SSL_MODE(SSL_MODE_DISABLED),
            // OPT_VIA_SSL_TIMEOUT(15),
            PARAM_END);
    }
    else {
        optRes = mysqlx_session_option_set(opt,
            OPT_HOST(mIp.c_str()), OPT_PORT(mPort),
            OPT_USER(mUser.c_str()), OPT_PWD(mPwd.c_str()),
            OPT_DB(mDbName.c_str()),
            OPT_SSL_MODE(SSL_MODE_DISABLED),
            PARAM_END);
    }

    session_t = mysqlx_get_session_from_options(opt, &error);
    mysqlx_free(opt);

    if (session_t == NULL)
    {
        std::cout << "session null" << std::endl;
        errcode = mysqlx_error_num(error);  //if option error, mCurErrcode = 0
        std::string str(mysqlx_error_message(error));
        errstring = str;
        std::cout << "code:" << errcode << ","<< str << std::endl;
        mysqlx_free(error);
        return false;
    }
    std::cout << "session create success" << std::endl;
    if (error != NULL) {
        mysqlx_free(error);
        error = NULL;
    }
    return true;
}
void MysqlApiTest::disconnect(mysqlx_session_t*& session_t)
{
    if (session_t == NULL)
    {
        std::cout << "session null when disconnect" << std::endl;
        return;
    }
    mysqlx_session_close(session_t);
    session_t = NULL;
}
void MysqlApiTest::memoryTestOne()
{
    
    const std::string tag = "[memoryTestOne]";
    std::cout<< tag << "begin.." << std::endl;
    mysqlx_session_t* session_t = NULL;
    unsigned int errcode = 0;
    std::string errstring;

    std::string stop;

    const bool needopenssl = false;
    if (!connect(session_t, needopenssl, errcode, errstring))
    {
        std::cout << tag << "connect fail, openssl:" << needopenssl << std::endl;
        if (!connect(session_t, !needopenssl, errcode, errstring))
        {
            std::cout << tag << "connect fail, openssl:" << !needopenssl << std::endl;
            return;
        }
    }
    //cpuusage(tag);
    bool needrun = true;
    std::cout << "please input any thing to continue, enter q to quit:" << std::endl;
    std::cin >> stop;
    if (stop == std::string("q"))
    {
        needrun = false;
    }
    unsigned int count = 0;
    while (needrun)
    {
        mysqlx_result_t* result = NULL;
        std::string sql = "SHOW DATABASES;";

        result = mysqlx_sql(session_t, sql.c_str(), sql.length());
        count++;
        if (result != NULL)
        {
            mysqlx_free(result);
            result = NULL;
            if(count % 5000 == 0)
                std::cout << tag << " mysqlx_sql:" << count << std::endl;
        } 
        else
        {
            errcode = mysqlx_error_num(session_t);
            const char* errorChar = mysqlx_error_message(session_t);
            if (errorChar != NULL)
            {
                errstring = errorChar;
            }
            std::cout << tag << "mysqlx_sql fail:" << errcode << "," << errstring << std::endl;

            if (checkIsDisconnectErr(errcode))
            {
                std::cout << tag << "correct sql fail: IsDisconnectErr" << std::endl;
                break;
            }
        }
        if (count % 10000 == 0)
        {
            std::cout << tag << count << " please input any thing to continue, enter q to quit:" << std::endl;
            std::cin >> stop;
            if (stop == std::string("q"))
            {
                needrun = false;
            }
            //cpuusage(tag);//you can look up the memory occupy
            std::this_thread::sleep_for(std::chrono::seconds(2));
        }
        
    }
    disconnect(session_t);
}

Suggested fix:
In fact, the memory is occupyed by "mysqlx_stmt_struct" in session:

see this function, the stmt has not been released after mysqlx_sql and mysqlx_free for result won't free this too:

in fact the stmt could not be released here because the result will released together.

mysqlx_result_struct * STDCALL mysqlx_sql(mysqlx_session_struct *sess,
                                        const char *query,
                                        size_t query_len)
{
  SAFE_EXCEPTION_BEGIN(sess, NULL)

  mysqlx_stmt_struct *stmt = sess->sql_query(query, query_len);
  mysqlx_result_struct *res = mysqlx_execute(stmt);
  if (res == NULL)
    SET_ERROR_FROM_STMT(sess, stmt, NULL);

  return res;
  SAFE_EXCEPTION_END(sess, NULL)
}