1d540f44创建于 2021年9月23日历史提交
/*
 * Copyright (c) 2020 Huawei Technologies Co.,Ltd.
 *
 * openGauss is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *
 *          http://license.coscl.org.cn/MulanPSL2
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * -------------------------------------------------------------------------
 *
 * autoanalyzer.cpp
 *	   routines for processing auto-analyze in optimizer
 * IDENTIFICATION
 *        src/gausskernel/optimizer/util/autoanalyzer.cpp
 *
 * -------------------------------------------------------------------------
 */
#include "postgres.h"
#include "knl/knl_variable.h"

#include "commands/dbcommands.h"
#include "executor/node/nodeModifyTable.h"
#include "miscadmin.h"
#include "optimizer/autoanalyzer.h"
#include "postmaster/postmaster.h"
#include "storage/buf/bufmgr.h"
#include "utils/acl.h"
#include "utils/builtins.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/resowner.h"

/* AutoAnalyze */
#define CHAR_BUF_SIZE 512
#define AUTOANALYZE_LOCKWAIT_TIMEOUT 10000 /* 10s */
static int autoAnalyzeFreeProcess = 10;

/* Constructor */
AutoAnaProcess::AutoAnaProcess(Relation rel) : m_pgconn(NULL), m_res(NULL)
{
    m_query = NIL;

    StringInfoData str_lockwait_timeout;
    initStringInfo(&str_lockwait_timeout);
    appendStringInfo(&str_lockwait_timeout, "set lockwait_timeout=%d ;", AUTOANALYZE_LOCKWAIT_TIMEOUT);
    m_query = lappend(m_query, str_lockwait_timeout.data);

    StringInfoData str_allow_concurrent_tuple_update;
    initStringInfo(&str_allow_concurrent_tuple_update);
    appendStringInfo(&str_allow_concurrent_tuple_update, "set allow_concurrent_tuple_update='off';");
    m_query = lappend(m_query, str_allow_concurrent_tuple_update.data);

    StringInfoData str_max_query_retry_times;
    initStringInfo(&str_max_query_retry_times);
    appendStringInfo(&str_max_query_retry_times, "set max_query_retry_times=0;");
    m_query = lappend(m_query, str_max_query_retry_times.data);

    StringInfoData str_analyze_command;
    initStringInfo(&str_analyze_command);
    appendStringInfo(&str_analyze_command,
        "analyze %s.%s ;",
        quote_identifier(get_namespace_name(rel->rd_rel->relnamespace)),
        quote_identifier(NameStr(rel->rd_rel->relname)));
    m_query = lappend(m_query, str_analyze_command.data);
}

AutoAnaProcess::~AutoAnaProcess()
{
    if (t_thrd.utils_cxt.CurrentResourceOwner == NULL) {
        m_res = NULL;
    }
    PQclear(m_res);
    PQfinish(m_pgconn);
    if (m_query != NIL) {
        list_free_deep(m_query);
    }
    m_pgconn = NULL;
    m_res = NULL;
    m_query = NIL;
}

bool AutoAnaProcess::executeSQLCommand(char* queryString)
{
    bool result = false;
    bool ImmediateInterruptOK_Old = t_thrd.int_cxt.ImmediateInterruptOK;
    /* Allow cancel/die interrupts to be processed while waiting */
    t_thrd.int_cxt.ImmediateInterruptOK = true;
    CHECK_FOR_INTERRUPTS();
    m_res = PQexec(m_pgconn, queryString);
    t_thrd.int_cxt.ImmediateInterruptOK = ImmediateInterruptOK_Old;

    if (PQresultStatus(m_res) != PGRES_COMMAND_OK) {
        elog(DEBUG2, "[AUTO-ANALYZE] autoanalyze failed: %s, error: %s", queryString, PQerrorMessage(m_pgconn));
    } else {
        result = true;
        elog(DEBUG2, "[AUTO-ANALYZE] autoanalyze sucess: %s", queryString);
    }
    PQclear(m_res);
    m_res = NULL;
    return result;
}

bool AutoAnaProcess::run()
{
    char conninfo[CHAR_BUF_SIZE];
    int ret;
    bool result = false;
    ListCell* lc = NULL;

    foreach (lc, m_query) {
        char* cmd = (char*)lfirst(lc);
        elog(DEBUG2, "[AUTO-ANALYZE] autoanalyze start: %s", cmd);
    }

    ret = snprintf_s(conninfo,
        sizeof(conninfo),
        sizeof(conninfo) - 1,
        "dbname=%s port=%d application_name='auto_analyze' enable_ce=1 ",
        get_and_check_db_name(u_sess->proc_cxt.MyDatabaseId, true),
        g_instance.attr.attr_network.PostPortNumber);
    securec_check_ss_c(ret, "\0", "\0");

    m_pgconn = PQconnectdb(conninfo);

    /* check to see that the backend connection was successfully made */
    if (PQstatus(m_pgconn) == CONNECTION_OK) {
        foreach (lc, m_query) {
            char* cmd = (char*)lfirst(lc);
            result = executeSQLCommand(cmd);
            if (!result) {
                break;
            }
        }
    } else {
        elog(DEBUG2, "[AUTO-ANALYZE] connection to database failed: %s", PQerrorMessage(m_pgconn));
    }

    PQclear(m_res);
    PQfinish(m_pgconn);
    m_res = NULL;
    m_pgconn = NULL;
    return result;
}

void AutoAnaProcess::tear_down()
{
    delete u_sess->analyze_cxt.autoanalyze_process;
    u_sess->analyze_cxt.autoanalyze_process = NULL;
}

/*
 * check_conditions
 *		check the user privilege for autoanalyze, or if temp table
 */
bool AutoAnaProcess::check_conditions(Relation rel)
{
    /* if the rel is invalid, just return false */
    if (!rel)
        return false;

    /* if the rel is temp, just return false */
    if (RelationIsLocalTemp(rel))
        return false;

    /*
     * If rel is in read only mode(none redistribution scenario), we skip analyze
     * the relation.
     */
    if (!u_sess->attr.attr_sql.enable_cluster_resize && RelationInClusterResizingReadOnly(rel))
        return false;

    AclResult aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_VACUUM);
    if (aclresult != ACLCHECK_OK && !(pg_class_ownercheck(RelationGetRelid(rel), GetUserId()) ||
            (pg_database_ownercheck(u_sess->proc_cxt.MyDatabaseId, GetUserId()) && !rel->rd_rel->relisshared))) {
        return false;
    }

    return true;
}

bool AutoAnaProcess::runAutoAnalyze(Relation rel)
{
    bool getProcess = false;
    bool result = false;
    TimestampTz start_time = 0;

    /* need to show auto-analyze time in explain analyze */
    if (u_sess->analyze_cxt.autoanalyze_timeinfo) {
        start_time = GetCurrentTimestamp();
    }

    /* 1 check conditions */
    if (!check_conditions(rel)) {
        elog(DEBUG2, "[AUTO-ANALYZE] check analyze conditions failed: table \"%s\"", NameStr(rel->rd_rel->relname));
        return result;
    }

    /* 2 get free aa process */
    (void)LWLockAcquire(AutoanalyzeLock, LW_EXCLUSIVE);
    if (autoAnalyzeFreeProcess > 0) {
        getProcess = true;
        autoAnalyzeFreeProcess--;
    }
    (void)LWLockRelease(AutoanalyzeLock);

    if (!getProcess) {
        /* no free process */
        elog(DEBUG2, "[AUTO-ANALYZE] no free autoanalyze process");
        return result;
    }

    /* 3 run analyze */
    Assert(u_sess->analyze_cxt.autoanalyze_process == NULL);
    u_sess->analyze_cxt.autoanalyze_process = New(CurrentMemoryContext) AutoAnaProcess(rel);
    result = u_sess->analyze_cxt.autoanalyze_process->run();
    tear_down();

    /* 4 release free aa process */
    LWLockAcquire(AutoanalyzeLock, LW_EXCLUSIVE);
    autoAnalyzeFreeProcess++;
    LWLockRelease(AutoanalyzeLock);

    /* 5 show auto-analyze time in explain analyze when success */
    if (u_sess->analyze_cxt.autoanalyze_timeinfo && result) {
        long secs;
        long msecs;
        int usecs;

        TimestampDifference(start_time, GetCurrentTimestamp(), &secs, &usecs);
        msecs = usecs / 1000L;
        msecs = secs * 1000 + msecs;
        usecs = usecs % 1000;

        MemoryContext oldcontext = MemoryContextSwitchTo(u_sess->temp_mem_cxt);
        appendStringInfo(u_sess->analyze_cxt.autoanalyze_timeinfo,
            "\"%s.%s\" %ld.%03dms ",
            get_namespace_name(rel->rd_rel->relnamespace),
            NameStr(rel->rd_rel->relname),
            msecs,
            usecs);
        (void)MemoryContextSwitchTo(oldcontext);
    }

    return result;
}

void AutoAnaProcess::cancelAutoAnalyze()
{
    /* auto-analyzing now */
    if (u_sess->analyze_cxt.autoanalyze_process != NULL) {
        tear_down();

        /* release free aa process */
        (void)LWLockAcquire(AutoanalyzeLock, LW_EXCLUSIVE);
        autoAnalyzeFreeProcess++;
        LWLockRelease(AutoanalyzeLock);
    }

    /* clean explain info if exists */
    if (u_sess->analyze_cxt.autoanalyze_timeinfo != NULL) {
        pfree_ext(u_sess->analyze_cxt.autoanalyze_timeinfo->data);
        pfree_ext(u_sess->analyze_cxt.autoanalyze_timeinfo);
        u_sess->analyze_cxt.autoanalyze_timeinfo = NULL;
    }
}

/* Just for query cancel */
void CancelAutoAnalyze()
{
    AutoAnaProcess::cancelAutoAnalyze();
}