forked from huawei/openGauss-server
1725 lines
60 KiB
C++
Executable File
1725 lines
60 KiB
C++
Executable File
/*
|
|
* 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.
|
|
* -------------------------------------------------------------------------
|
|
*
|
|
* instr_statement.cpp
|
|
* functions for full/slow SQL
|
|
*
|
|
* IDENTIFICATION
|
|
* src/gausskernel/cbb/instruments/statement/instr_statement.cpp
|
|
*
|
|
* -------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
#include "instruments/instr_statement.h"
|
|
#include "instruments/instr_handle_mgr.h"
|
|
#include "instruments/instr_unique_sql.h"
|
|
#include "instruments/instr_slow_query.h"
|
|
#include "postgres.h"
|
|
#include "pgxc/pgxc.h"
|
|
#include "pgstat.h"
|
|
#include "pgxc/poolutils.h"
|
|
#include "fmgr.h"
|
|
#include "miscadmin.h"
|
|
#include "utils/memutils.h"
|
|
#include "storage/proc.h"
|
|
#include "storage/latch.h"
|
|
#include "storage/ipc.h"
|
|
#include "catalog/pg_database.h"
|
|
#include "gssignal/gs_signal.h"
|
|
#include "utils/guc.h"
|
|
#include "utils/ps_status.h"
|
|
#include "utils/elog.h"
|
|
#include "utils/memprot.h"
|
|
#include "tcop/dest.h"
|
|
#include "tcop/tcopprot.h"
|
|
#include "gs_thread.h"
|
|
#include "access/heapam.h"
|
|
#include "utils/rel.h"
|
|
#include "utils/postinit.h"
|
|
#include "utils/snapmgr.h"
|
|
#include "workload/gscgroup.h"
|
|
#include "libpq/pqsignal.h"
|
|
#include "pgxc/groupmgr.h"
|
|
#include "funcapi.h"
|
|
#include "libpq/ip.h"
|
|
#include "storage/lock/lock.h"
|
|
#include "nodes/makefuncs.h"
|
|
#include "catalog/indexing.h"
|
|
#include "commands/dbcommands.h"
|
|
#include "utils/builtins.h"
|
|
#include "commands/explain.h"
|
|
#include "utils/fmgroids.h"
|
|
#include "utils/relcache.h"
|
|
#include "commands/copy.h"
|
|
#include "instruments/instr_func_control.h"
|
|
|
|
#define MAX_SLOW_QUERY_RETENSION_DAYS 604800
|
|
#define MAX_FULL_SQL_RETENSION_SEC 86400
|
|
#define IS_NULL_STR(str) ((str) == NULL || (str)[0] == '\0')
|
|
#define STRING_MAX_LEN 256
|
|
|
|
#define STATEMENT_DETAILS_HEAD_SIZE (1 + 1) /* [VERSION] + [TRUNCATED] */
|
|
#define INSTR_STMT_UNIX_DOMAIN_PORT (-1)
|
|
#define INSTR_STATEMENT_ATTRNUM 52
|
|
|
|
/* lock/lwlock's detail information */
|
|
typedef struct {
|
|
StmtDetailType type; /* statement details kind. */
|
|
const LOCKTAG *locktag; /* for Lock, this field is used. */
|
|
uint16 lwlockId; /* for LWLock, this field is used. */
|
|
int lockmode; /* lockmode for this request. */
|
|
} StmtLockDetail;
|
|
|
|
static bytea* get_statement_detail(StatementDetail *text, bool oom);
|
|
static bool is_valid_detail_record(uint32 bytea_data_len, const char *details, uint32 details_len);
|
|
|
|
static List* split_levels_into_list(const char* levels)
|
|
{
|
|
List *result = NIL;
|
|
char *str = pstrdup(levels);
|
|
char *first_ch = str;
|
|
int len = (int)strlen(str) + 1;
|
|
|
|
for (int i = 0; i < len; i++) {
|
|
if (str[i] == ',' || str[i] == '\0') {
|
|
/* replace ',' with '\0'. */
|
|
str[i] = '\0';
|
|
|
|
/* copy this into result. */
|
|
result = lappend(result, pstrdup(first_ch));
|
|
|
|
/* move to the head of next string. */
|
|
first_ch = str + i + 1;
|
|
}
|
|
}
|
|
pfree(str);
|
|
|
|
return result;
|
|
}
|
|
|
|
static StatLevel name2level(const char *name)
|
|
{
|
|
if (pg_strcasecmp(name, "OFF") == 0) {
|
|
return STMT_TRACK_OFF;
|
|
} else if (pg_strcasecmp(name, "L0") == 0) {
|
|
return STMT_TRACK_L0;
|
|
} else if (pg_strcasecmp(name, "L1") == 0) {
|
|
return STMT_TRACK_L1;
|
|
} else if (pg_strcasecmp(name, "L2") == 0) {
|
|
return STMT_TRACK_L2;
|
|
} else {
|
|
return LEVEL_INVALID;
|
|
}
|
|
}
|
|
|
|
bool check_statement_stat_level(char** newval, void** extra, GucSource source)
|
|
{
|
|
/* parse level */
|
|
List *l = split_levels_into_list(*newval);
|
|
|
|
if (list_length(l) != STATEMENT_SQL_KIND) {
|
|
GUC_check_errdetail("attr num:%d is error,track_stmt_stat_level attr is 2", l->length);
|
|
list_free_deep(l);
|
|
return false;
|
|
}
|
|
|
|
int full_level = name2level((char*)linitial(l));
|
|
int slow_level = name2level((char*)lsecond(l));
|
|
|
|
list_free_deep(l);
|
|
if (full_level == LEVEL_INVALID || slow_level == LEVEL_INVALID) {
|
|
GUC_check_errdetail("invalid input syntax");
|
|
return false;
|
|
}
|
|
|
|
*extra = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), STATEMENT_SQL_KIND * sizeof(int));
|
|
((int*)(*extra))[0] = full_level;
|
|
((int*)(*extra))[1] = slow_level;
|
|
|
|
return true;
|
|
}
|
|
|
|
void assign_statement_stat_level(const char* newval, void* extra)
|
|
{
|
|
int *level = (int*) extra;
|
|
|
|
u_sess->statement_cxt.statement_level[0] = level[0];
|
|
u_sess->statement_cxt.statement_level[1] = level[1];
|
|
}
|
|
|
|
|
|
void JobStatementIAm(void)
|
|
{
|
|
t_thrd.role = TRACK_STMT_WORKER;
|
|
}
|
|
|
|
bool IsStatementFlushProcess(void)
|
|
{
|
|
return t_thrd.role == TRACK_STMT_WORKER;
|
|
}
|
|
|
|
/* SIGHUP handler for statement flush thread */
|
|
static void statement_sighup_handler(SIGNAL_ARGS)
|
|
{
|
|
t_thrd.statement_cxt.got_SIGHUP = true;
|
|
}
|
|
|
|
static void statement_exit(SIGNAL_ARGS)
|
|
{
|
|
t_thrd.statement_cxt.need_exit = true;
|
|
die(postgres_signal_arg);
|
|
}
|
|
|
|
static void SetThrdCxt(void)
|
|
{
|
|
t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(TopMemoryContext,
|
|
"MessageContext",
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
|
|
/* Create the memory context we will use in the main loop. */
|
|
t_thrd.mem_cxt.mask_password_mem_cxt = AllocSetContextCreate(TopMemoryContext,
|
|
"MaskPasswordCtx",
|
|
ALLOCSET_DEFAULT_MINSIZE,
|
|
ALLOCSET_DEFAULT_INITSIZE,
|
|
ALLOCSET_DEFAULT_MAXSIZE);
|
|
}
|
|
|
|
static void ReloadInfo()
|
|
{
|
|
/* Reload configuration if we got SIGHUP from the postmaster. */
|
|
if (t_thrd.statement_cxt.got_SIGHUP) {
|
|
t_thrd.statement_cxt.got_SIGHUP = false;
|
|
ProcessConfigFile(PGC_SIGHUP);
|
|
}
|
|
|
|
if (IsGotPoolReload()) {
|
|
processPoolerReload();
|
|
ResetGotPoolReload(false);
|
|
}
|
|
}
|
|
|
|
static void ProcessSignal(void)
|
|
{
|
|
/*
|
|
* Ignore all signals usually bound to some action in the postmaster,
|
|
* except SIGHUP, SIGTERM and SIGQUIT.
|
|
*/
|
|
(void)gspqsignal(SIGHUP, statement_sighup_handler);
|
|
(void)gspqsignal(SIGINT, SIG_IGN);
|
|
(void)gspqsignal(SIGTERM, statement_exit); /* cancel current query and exit */
|
|
(void)gspqsignal(SIGQUIT, quickdie);
|
|
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
|
|
(void)gspqsignal(SIGALRM, SIG_IGN);
|
|
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
|
(void)gspqsignal(SIGUSR2, SIG_IGN);
|
|
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
|
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
|
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
|
(void)gspqsignal(SIGCONT, SIG_DFL);
|
|
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
|
|
|
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
|
(void)gs_signal_unblock_sigusr2();
|
|
if (u_sess->proc_cxt.MyProcPort->remote_host)
|
|
pfree(u_sess->proc_cxt.MyProcPort->remote_host);
|
|
u_sess->proc_cxt.MyProcPort->remote_host = pstrdup("localhost");
|
|
|
|
t_thrd.wlm_cxt.thread_node_group = &g_instance.wlm_cxt->MyDefaultNodeGroup; // initialize the default value
|
|
t_thrd.wlm_cxt.thread_climgr = &t_thrd.wlm_cxt.thread_node_group->climgr;
|
|
t_thrd.wlm_cxt.thread_srvmgr = &t_thrd.wlm_cxt.thread_node_group->srvmgr;
|
|
}
|
|
|
|
#define SET_NAME_VALUES(str, attr) \
|
|
do { \
|
|
if (str == NULL) { \
|
|
nulls[attr] = true; \
|
|
} else { \
|
|
values[attr] = DirectFunctionCall1(namein, CStringGetDatum(str)); \
|
|
} \
|
|
} while (0)
|
|
|
|
#define SET_TEXT_VALUES(str, attr) \
|
|
do { \
|
|
if (str == NULL) { \
|
|
nulls[attr] = true; \
|
|
} else { \
|
|
values[attr] = CStringGetTextDatum(str); \
|
|
} \
|
|
} while (0)
|
|
|
|
static void set_stmt_row_activity_cache_io(StatementStatContext* statementInfo, Datum values[], int* i)
|
|
{
|
|
/* row activity */
|
|
values[(*i)++] = Int64GetDatum(statementInfo->row_activity.returned_rows);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->row_activity.tuples_fetched);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->row_activity.tuples_returned);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->row_activity.tuples_inserted);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->row_activity.tuples_updated);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->row_activity.tuples_deleted);
|
|
|
|
/* cache IO */
|
|
values[(*i)++] = Int64GetDatum(statementInfo->cache_io.blocks_fetched);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->cache_io.blocks_hit);
|
|
}
|
|
|
|
static void set_stmt_basic_info(const knl_u_statement_context* statementCxt, StatementStatContext* statementInfo,
|
|
Datum values[], bool nulls[], int* i)
|
|
{
|
|
/* query basic info */
|
|
values[(*i)++] = Int64GetDatum(statementInfo->unique_query_id);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->debug_query_id);
|
|
SET_TEXT_VALUES(statementInfo->query, (*i)++);
|
|
values[(*i)++] = TimestampTzGetDatum(statementInfo->start_time);
|
|
values[(*i)++] = TimestampTzGetDatum(statementInfo->finish_time);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->slow_query_threshold);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->txn_id);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->tid);
|
|
values[(*i)++] = Int64GetDatum(statementCxt->session_id);
|
|
|
|
/* parse info */
|
|
values[(*i)++] = Int64GetDatum(statementInfo->parse.soft_parse);
|
|
values[(*i)++] = Int64GetDatum(statementInfo->parse.hard_parse);
|
|
|
|
SET_TEXT_VALUES(statementInfo->query_plan, (*i)++);
|
|
}
|
|
|
|
static void set_stmt_lock_summary(const LockSummaryStat *lock_summary, Datum values[], int* i)
|
|
{
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lock_cnt);
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lock_time);
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lock_wait_cnt);
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lock_wait_time);
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lock_max_cnt);
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lwlock_cnt);
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lwlock_wait_cnt);
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lwlock_time);
|
|
values[(*i)++] = Int64GetDatum(lock_summary->lwlock_wait_time);
|
|
}
|
|
|
|
static HeapTuple GetStatementTuple(Relation rel, StatementStatContext* statementInfo,
|
|
const knl_u_statement_context* statementCxt)
|
|
{
|
|
int i = 0;
|
|
Datum values[INSTR_STATEMENT_ATTRNUM];
|
|
bool nulls[INSTR_STATEMENT_ATTRNUM] = {false};
|
|
errno_t rc = memset_s(nulls, sizeof(nulls), 0, sizeof(nulls));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
/* get statment tuple */
|
|
SET_NAME_VALUES(statementCxt->db_name, i++);
|
|
SET_NAME_VALUES(statementInfo->schema_name, i++);
|
|
values[i++] = UInt32GetDatum(statementInfo->unique_sql_cn_id);
|
|
SET_NAME_VALUES(statementCxt->user_name, i++);
|
|
|
|
/* client info */
|
|
SET_TEXT_VALUES(statementInfo->application_name, i++);
|
|
SET_TEXT_VALUES(statementCxt->client_addr, i++);
|
|
|
|
if (statementCxt->client_port != INSTR_STMT_NULL_PORT)
|
|
values[i++] = Int32GetDatum(statementCxt->client_port);
|
|
else
|
|
nulls[i++] = true;
|
|
|
|
set_stmt_basic_info(statementCxt, statementInfo, values, nulls, &i);
|
|
set_stmt_row_activity_cache_io(statementInfo, values, &i);
|
|
|
|
/* time info */
|
|
for (int num = 0; num < TOTAL_TIME_INFO_TYPES; num++) {
|
|
if (num == NET_SEND_TIME)
|
|
continue;
|
|
values[i++] = Int64GetDatum(statementInfo->timeModel[num]);
|
|
}
|
|
|
|
/* net info */
|
|
int idx = 0;
|
|
while (idx < TOTAL_NET_INFO_TYPES) {
|
|
char netInfo[STRING_MAX_LEN];
|
|
uint64 time = (uint64)statementInfo->networkInfo[idx++];
|
|
uint64 n_calls = (uint64)statementInfo->networkInfo[idx++];
|
|
uint64 size = (uint64)statementInfo->networkInfo[idx++];
|
|
rc = sprintf_s(netInfo, sizeof(netInfo), "{\"time\":%lu, \"n_calls\":%lu, \"size\":%lu}",
|
|
time, n_calls, size);
|
|
securec_check_ss(rc, "\0", "\0");
|
|
values[i++] = CStringGetTextDatum(netInfo);
|
|
}
|
|
|
|
/* lock summary */
|
|
set_stmt_lock_summary(&statementInfo->lock_summary, values, &i);
|
|
|
|
/* lock detail */
|
|
if (unlikely(statementInfo->details.oom)) {
|
|
values[i++] = PointerGetDatum(get_statement_detail(&statementInfo->details, true));
|
|
} else if (statementInfo->details.n_items == 0) {
|
|
nulls[i++] = true;
|
|
} else {
|
|
values[i++] = PointerGetDatum(get_statement_detail(&statementInfo->details, false));
|
|
}
|
|
|
|
/* is slow sql */
|
|
values[i++] = BoolGetDatum(
|
|
(statementInfo->finish_time - statementInfo->start_time >= statementInfo->slow_query_threshold &&
|
|
statementInfo->slow_query_threshold >= 0) ? true : false);
|
|
SET_TEXT_VALUES(statementInfo->trace_id, i++);
|
|
Assert(INSTR_STATEMENT_ATTRNUM == i);
|
|
return heap_form_tuple(RelationGetDescr(rel), values, nulls);
|
|
}
|
|
|
|
static RangeVar* InitStatementRel()
|
|
{
|
|
RangeVar* relrv = makeRangeVar(NULL, NULL, -1);
|
|
relrv->relname = pstrdup("statement_history");
|
|
relrv->schemaname = pstrdup("pg_catalog");
|
|
relrv->catalogname = pstrdup("postgres");
|
|
relrv->relpersistence = RELPERSISTENCE_UNLOGGED;
|
|
return relrv;
|
|
}
|
|
|
|
bool check_statement_retention_time(char** newval, void** extra, GucSource source)
|
|
{
|
|
/* Do str copy and remove space. */
|
|
char* strs = TrimStr(*newval);
|
|
if (IS_NULL_STR(strs))
|
|
return false;
|
|
const char* delim = ",";
|
|
List *res = NULL;
|
|
char* next_token = NULL;
|
|
|
|
/* Get slow query retention days */
|
|
char* token = strtok_s(strs, delim, &next_token);
|
|
while (token != NULL) {
|
|
res = lappend(res, TrimStr(token));
|
|
token = strtok_s(NULL, delim, &next_token);
|
|
}
|
|
pfree(strs);
|
|
if (res->length != STATEMENT_SQL_KIND) {
|
|
GUC_check_errdetail("attr num:%d is error,track_stmt_retention_time attr is 2", res->length);
|
|
return false;
|
|
}
|
|
|
|
int full_sql_retention_sec = 0;
|
|
int slow_query_retention_days = 0;
|
|
/* get full sql retention sec */
|
|
if (!StrToInt32((char*)linitial(res), &full_sql_retention_sec) ||
|
|
!StrToInt32((char*)lsecond(res), &slow_query_retention_days)) {
|
|
GUC_check_errdetail("invalid input syntax");
|
|
return false;
|
|
}
|
|
|
|
if (slow_query_retention_days < 0 || slow_query_retention_days > MAX_SLOW_QUERY_RETENSION_DAYS) {
|
|
GUC_check_errdetail("slow_query_retention_days:%d is out of range [%d, %d].",
|
|
slow_query_retention_days, 0, MAX_SLOW_QUERY_RETENSION_DAYS);
|
|
return false;
|
|
}
|
|
if (full_sql_retention_sec < 0 || full_sql_retention_sec > MAX_FULL_SQL_RETENSION_SEC) {
|
|
GUC_check_errdetail("full_sql_retention_sec:%d is out of range [%d, %d].",
|
|
full_sql_retention_sec, 0, MAX_FULL_SQL_RETENSION_SEC);
|
|
return false;
|
|
}
|
|
list_free_deep(res);
|
|
|
|
*extra = MemoryContextAlloc(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX), STATEMENT_SQL_KIND * sizeof(int));
|
|
((int*)(*extra))[0] = full_sql_retention_sec;
|
|
((int*)(*extra))[1] = slow_query_retention_days;
|
|
return true;
|
|
}
|
|
|
|
void assign_statement_retention_time(const char* newval, void* extra)
|
|
{
|
|
int *level = (int*) extra;
|
|
t_thrd.statement_cxt.full_sql_retention_time = level[0];
|
|
t_thrd.statement_cxt.slow_sql_retention_time = level[1];
|
|
}
|
|
|
|
static void CleanStatementByIdx(Relation rel, Oid statementTimeIndexId,
|
|
int retentionTime, bool isSlowSQL)
|
|
{
|
|
int index_num = 2;
|
|
AttrNumber Anum_statement_history_indrelid = 11;
|
|
AttrNumber Anum_statement_history_slow_sql_id = 51;
|
|
ScanKeyData key[index_num];
|
|
SysScanDesc indesc = NULL;
|
|
HeapTuple tup = NULL;
|
|
TimestampTz minTimestamp = (int64)GetCurrentTimestamp() - retentionTime * USECS_PER_SEC;
|
|
ScanKeyInit(&key[0], Anum_statement_history_indrelid, BTLessStrategyNumber,
|
|
F_TIMESTAMP_LE, TimestampGetDatum(minTimestamp));
|
|
ScanKeyInit(&key[1], Anum_statement_history_slow_sql_id, BTEqualStrategyNumber,
|
|
F_BOOLEQ, BoolGetDatum(isSlowSQL));
|
|
indesc = systable_beginscan(rel, statementTimeIndexId, true, SnapshotSelf, index_num, key);
|
|
while (HeapTupleIsValid(tup = systable_getnext(indesc))) {
|
|
simple_heap_delete(rel, &tup->t_self);
|
|
}
|
|
systable_endscan(indesc);
|
|
}
|
|
|
|
static void StartCleanWorker(int* count)
|
|
{
|
|
int maxCleanInterval = 600; // 600 * 100ms = 1min
|
|
if (*count < maxCleanInterval || g_instance.stat_cxt.instr_stmt_is_cleaning) {
|
|
return;
|
|
}
|
|
SendPostmasterSignal(PMSIGNAL_START_CLEAN_STATEMENT);
|
|
g_instance.stat_cxt.instr_stmt_is_cleaning = true;
|
|
*count = 0;
|
|
}
|
|
/*
|
|
* The statement_history table should be cleaned up twice
|
|
* In the first time, all records less than minStatementTimestamp are cleared,
|
|
* and the last time is based on whether the records are slow query or full sql
|
|
* If the retention time of slow SQL is longer than that of full SQL,
|
|
* we only need to clean up the full SQL record on the last clean-up
|
|
*/
|
|
static void CleanStatementTable()
|
|
{
|
|
List* statement_index = NULL;
|
|
MemoryContext oldcxt = CurrentMemoryContext;
|
|
PG_TRY();
|
|
{
|
|
StartTransactionCommand();
|
|
PushActiveSnapshot(GetTransactionSnapshot());
|
|
/* get statement_history relation and index oid */
|
|
RangeVar* relrv = InitStatementRel();
|
|
Relation rel = heap_openrv(relrv, RowExclusiveLock);
|
|
if (rel == NULL || (statement_index = RelationGetIndexList(rel)) == NULL) {
|
|
heap_close(rel, RowExclusiveLock);
|
|
ereport(ERROR, (errcode(ERRCODE_INTERNAL_ERROR),
|
|
errmsg("get statement_history relation failed")));
|
|
}
|
|
Oid statementTimeIndexId = linitial_oid(statement_index);
|
|
/* clean up statement_history table */
|
|
CleanStatementByIdx(rel, statementTimeIndexId, t_thrd.statement_cxt.slow_sql_retention_time, true);
|
|
CleanStatementByIdx(rel, statementTimeIndexId, t_thrd.statement_cxt.full_sql_retention_time, false);
|
|
heap_close(rel, RowExclusiveLock);
|
|
PopActiveSnapshot();
|
|
CommitTransactionCommand();
|
|
}
|
|
PG_CATCH();
|
|
{
|
|
(void)MemoryContextSwitchTo(oldcxt);
|
|
ErrorData *edata = CopyErrorData();
|
|
ereport(WARNING, (errcode(ERRCODE_WARNING),
|
|
errmsg("[Statement] delete statement_history table failed, cause: %s", edata->message)));
|
|
FlushErrorState();
|
|
FreeErrorData(edata);
|
|
PopActiveSnapshot();
|
|
AbortCurrentTransaction();
|
|
}
|
|
PG_END_TRY();
|
|
}
|
|
|
|
static void FlushStatementToTable(StatementStatContext* suspendList, const knl_u_statement_context* statementCxt)
|
|
{
|
|
Assert (suspendList != NULL);
|
|
HeapTuple tuple = NULL;
|
|
StatementStatContext *flushItem = suspendList;
|
|
|
|
MemoryContext oldcxt = CurrentMemoryContext;
|
|
uint32 saveInterruptHoldoffCount = t_thrd.int_cxt.InterruptHoldoffCount;
|
|
PG_TRY();
|
|
{
|
|
StartTransactionCommand();
|
|
PushActiveSnapshot(GetTransactionSnapshot());
|
|
RangeVar* relrv = InitStatementRel();
|
|
Relation rel = heap_openrv(relrv, RowExclusiveLock);
|
|
while (flushItem != NULL) {
|
|
tuple = GetStatementTuple(rel, flushItem, statementCxt);
|
|
(void)simple_heap_insert(rel, tuple);
|
|
CatalogUpdateIndexes(rel, tuple);
|
|
heap_freetuple_ext(tuple);
|
|
flushItem = (StatementStatContext *)flushItem->next;
|
|
}
|
|
heap_close(rel, RowExclusiveLock);
|
|
PopActiveSnapshot();
|
|
CommitTransactionCommand();
|
|
}
|
|
PG_CATCH();
|
|
{
|
|
t_thrd.int_cxt.InterruptHoldoffCount = saveInterruptHoldoffCount;
|
|
(void)MemoryContextSwitchTo(oldcxt);
|
|
ErrorData* edata = NULL;
|
|
edata = CopyErrorData();
|
|
FlushErrorState();
|
|
|
|
ereport(WARNING, (errmodule(MOD_INSTR),
|
|
errmsg("[Statement] flush suspend list to statement_history failed, reason: '%s'", edata->message)));
|
|
FreeErrorData(edata);
|
|
PopActiveSnapshot();
|
|
AbortCurrentTransaction();
|
|
}
|
|
PG_END_TRY();
|
|
}
|
|
|
|
static void FlushAllStatement()
|
|
{
|
|
PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.BackendStatusArray;
|
|
for (int i = 1; i <= BackendStatusArray_size; i++) {
|
|
StatementStatContext *suspendList = NULL;
|
|
int suspendCnt = 0;
|
|
|
|
/* Prevent the release of statementcxt memory when the session exits */
|
|
(void)syscalllockAcquire(&beentry->statement_cxt_lock);
|
|
|
|
knl_u_statement_context* statementCxt = (knl_u_statement_context*)beentry->statement_cxt;
|
|
if (statementCxt != NULL) {
|
|
/* read suspend statement list head to local */
|
|
(void)syscalllockAcquire(&statementCxt->list_protect);
|
|
suspendList = (StatementStatContext*)statementCxt->suspendStatementList;
|
|
suspendCnt = statementCxt->suspend_count;
|
|
statementCxt->suspendStatementList = NULL;
|
|
statementCxt->suspend_count = 0;
|
|
(void)syscalllockRelease(&statementCxt->list_protect);
|
|
}
|
|
|
|
if (suspendList != NULL) {
|
|
/* flush statement list info to statement_history table */
|
|
FlushStatementToTable(suspendList, statementCxt);
|
|
|
|
/* append list to free list */
|
|
(void)syscalllockAcquire(&statementCxt->list_protect);
|
|
void *freeList = statementCxt->toFreeStatementList;
|
|
statementCxt->toFreeStatementList = suspendList;
|
|
while (suspendList->next != NULL) {
|
|
suspendList = (StatementStatContext *)suspendList->next;
|
|
}
|
|
suspendList->next = freeList;
|
|
statementCxt->free_count += suspendCnt;
|
|
(void)syscalllockRelease(&statementCxt->list_protect);
|
|
}
|
|
|
|
(void)syscalllockRelease(&beentry->statement_cxt_lock);
|
|
beentry++;
|
|
}
|
|
}
|
|
|
|
static void StatementFlush()
|
|
{
|
|
const int flush_usleep_interval = 100000;
|
|
int count = 0;
|
|
|
|
while (!t_thrd.statement_cxt.need_exit && ENABLE_STATEMENT_TRACK) {
|
|
ReloadInfo();
|
|
ereport(DEBUG1, (errmodule(MOD_INSTR), errmsg("[Statement] start to flush statemnts.")));
|
|
StartCleanWorker(&count);
|
|
|
|
HOLD_INTERRUPTS();
|
|
/* flush all session's statement info to statement_history table */
|
|
FlushAllStatement();
|
|
RESUME_INTERRUPTS();
|
|
|
|
count++;
|
|
ereport(DEBUG1, (errmodule(MOD_INSTR), errmsg("[Statement] flush statemnts finished.")));
|
|
/* report statement_history state to pgstat */
|
|
if (OidIsValid(u_sess->proc_cxt.MyDatabaseId))
|
|
pgstat_report_stat(true);
|
|
pg_usleep(flush_usleep_interval);
|
|
}
|
|
}
|
|
|
|
NON_EXEC_STATIC void StatementFlushMain()
|
|
{
|
|
char username[NAMEDATALEN] = {'\0'};
|
|
|
|
/* we are a postmaster subprocess now */
|
|
IsUnderPostmaster = true;
|
|
JobStatementIAm();
|
|
/* reset MyProcPid */
|
|
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
|
/* record Start Time for logging */
|
|
t_thrd.proc_cxt.MyStartTime = time(NULL);
|
|
|
|
t_thrd.proc_cxt.MyProgName = "Statement flush thread";
|
|
u_sess->attr.attr_common.application_name = pstrdup("Statement flush thread");
|
|
SetProcessingMode(InitProcessing);
|
|
ProcessSignal();
|
|
|
|
/* Early initialization */
|
|
BaseInit();
|
|
#ifndef EXEC_BACKEND
|
|
InitProcess();
|
|
#endif
|
|
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser((char*)pstrdup(DEFAULT_DATABASE), InvalidOid, username);
|
|
t_thrd.proc_cxt.PostInit->InitStatementWorker();
|
|
SetProcessingMode(NormalProcessing);
|
|
on_shmem_exit(PGXCNodeCleanAndRelease, 0);
|
|
|
|
/* Identify myself via ps */
|
|
init_ps_display("statement flush process", "", "", "");
|
|
SetThrdCxt();
|
|
/* Create a resource owner to keep track of our resources. */
|
|
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "Statement Flush",
|
|
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX));
|
|
u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp();
|
|
Reset_Pseudo_CurrentUserId();
|
|
|
|
/* initialize current pool handles, it's also only once */
|
|
exec_init_poolhandles();
|
|
pgstat_bestart();
|
|
pgstat_report_appname("statement flush thread");
|
|
ereport(LOG, (errmsg("statement flush thread start")));
|
|
pgstat_report_activity(STATE_IDLE, NULL);
|
|
|
|
/* flush statement into statement_history table */
|
|
StatementFlush();
|
|
gs_thread_exit(0);
|
|
}
|
|
|
|
|
|
static void SetupSignal(void)
|
|
{
|
|
(void)gspqsignal(SIGHUP, SIG_IGN);
|
|
(void)gspqsignal(SIGINT, SIG_IGN);
|
|
(void)gspqsignal(SIGTERM, die); /* cancel current query and exit */
|
|
(void)gspqsignal(SIGQUIT, quickdie);
|
|
(void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler);
|
|
(void)gspqsignal(SIGALRM, SIG_IGN);
|
|
(void)gspqsignal(SIGPIPE, SIG_IGN);
|
|
(void)gspqsignal(SIGUSR2, SIG_IGN);
|
|
(void)gspqsignal(SIGCHLD, SIG_DFL);
|
|
(void)gspqsignal(SIGTTIN, SIG_DFL);
|
|
(void)gspqsignal(SIGTTOU, SIG_DFL);
|
|
(void)gspqsignal(SIGCONT, SIG_DFL);
|
|
(void)gspqsignal(SIGWINCH, SIG_DFL);
|
|
|
|
gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL);
|
|
(void)gs_signal_unblock_sigusr2();
|
|
if (u_sess->proc_cxt.MyProcPort->remote_host)
|
|
pfree(u_sess->proc_cxt.MyProcPort->remote_host);
|
|
u_sess->proc_cxt.MyProcPort->remote_host = pstrdup("localhost");
|
|
|
|
t_thrd.wlm_cxt.thread_node_group = &g_instance.wlm_cxt->MyDefaultNodeGroup; // initialize the default value
|
|
t_thrd.wlm_cxt.thread_climgr = &t_thrd.wlm_cxt.thread_node_group->climgr;
|
|
t_thrd.wlm_cxt.thread_srvmgr = &t_thrd.wlm_cxt.thread_node_group->srvmgr;
|
|
}
|
|
|
|
NON_EXEC_STATIC void CleanStatementMain()
|
|
{
|
|
char username[NAMEDATALEN] = {'\0'};
|
|
|
|
/* we are a postmaster subprocess now */
|
|
IsUnderPostmaster = true;
|
|
t_thrd.role = TRACK_STMT_CLEANER;
|
|
/* reset MyProcPid */
|
|
t_thrd.proc_cxt.MyProcPid = gs_thread_self();
|
|
/* record Start Time for logging */
|
|
t_thrd.proc_cxt.MyStartTime = time(NULL);
|
|
|
|
t_thrd.proc_cxt.MyProgName = "Clean Statement thread";
|
|
u_sess->attr.attr_common.application_name = pstrdup("Clean Statement thread");
|
|
SetProcessingMode(InitProcessing);
|
|
SetupSignal();
|
|
|
|
/* Early initialization */
|
|
BaseInit();
|
|
#ifndef EXEC_BACKEND
|
|
InitProcess();
|
|
#endif
|
|
t_thrd.proc_cxt.PostInit->SetDatabaseAndUser((char*)pstrdup(DEFAULT_DATABASE), InvalidOid, username);
|
|
t_thrd.proc_cxt.PostInit->InitStatementWorker();
|
|
SetProcessingMode(NormalProcessing);
|
|
on_shmem_exit(PGXCNodeCleanAndRelease, 0);
|
|
|
|
/* Identify myself via ps */
|
|
init_ps_display("clean statement process", "", "", "");
|
|
SetThrdCxt();
|
|
/* Create a resource owner to keep track of our resources. */
|
|
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "clean Statement",
|
|
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX));
|
|
u_sess->proc_cxt.MyProcPort->SessionStartTime = GetCurrentTimestamp();
|
|
Reset_Pseudo_CurrentUserId();
|
|
|
|
/* initialize current pool handles, it's also only once */
|
|
exec_init_poolhandles();
|
|
pgstat_bestart();
|
|
pgstat_report_appname("clean statement thread");
|
|
ereport(LOG, (errmsg("clean statement thread start")));
|
|
pgstat_report_activity(STATE_IDLE, NULL);
|
|
|
|
/* clean statement_history table */
|
|
CleanStatementTable();
|
|
g_instance.stat_cxt.instr_stmt_is_cleaning = false;
|
|
proc_exit(0);
|
|
}
|
|
|
|
|
|
size_t get_statement_detail_size(StmtDetailType type)
|
|
{
|
|
switch (type) {
|
|
case LOCK_START:
|
|
return LOCK_START_DETAIL_BUFSIZE;
|
|
case LOCK_END:
|
|
return LOCK_END_DETAIL_BUFSIZE;
|
|
case LOCK_WAIT_START:
|
|
return LOCK_WAIT_START_DETAIL_BUFSIZE;
|
|
case LOCK_WAIT_END:
|
|
return LOCK_WAIT_END_DETAIL_BUFSIZE;
|
|
case LOCK_RELEASE:
|
|
return LOCK_RELEASE_START_DETAIL_BUFSIZE;
|
|
case LWLOCK_START:
|
|
return LWLOCK_START_DETAIL_BUFSIZE;
|
|
case LWLOCK_END:
|
|
return LWLOCK_END_DETAIL_BUFSIZE;
|
|
case LWLOCK_WAIT_START:
|
|
return LWLOCK_WAIT_START_DETAIL_BUFSIZE;
|
|
case LWLOCK_WAIT_END:
|
|
return LWLOCK_WAIT_END_DETAIL_BUFSIZE;
|
|
case LWLOCK_RELEASE:
|
|
return LWLOCK_RELEASE_START_DETAIL_BUFSIZE;
|
|
case TYPE_INVALID:
|
|
default:
|
|
return INVALID_DETAIL_BUFSIZE;
|
|
}
|
|
}
|
|
|
|
static void* get_stmt_lock_detail(const StmtLockDetail *detail, TimestampTz curTime, size_t *size)
|
|
{
|
|
void *info = NULL;
|
|
|
|
switch (detail->type) {
|
|
case LOCK_START:
|
|
case LOCK_WAIT_START:
|
|
case LOCK_RELEASE:
|
|
info = (LockEventStartInfo *)palloc0_noexcept(sizeof(LockEventStartInfo));
|
|
if (info == NULL) {
|
|
break;
|
|
}
|
|
((LockEventStartInfo *)info)->eventType = (char)detail->type;
|
|
((LockEventStartInfo *)info)->timestamp = curTime;
|
|
((LockEventStartInfo *)info)->tag = *detail->locktag;
|
|
((LockEventStartInfo *)info)->mode = (LOCKMODE)detail->lockmode;
|
|
*size = sizeof(LockEventStartInfo);
|
|
break;
|
|
case LWLOCK_START:
|
|
case LWLOCK_WAIT_START:
|
|
case LWLOCK_RELEASE:
|
|
info = (LWLockEventStartInfo *)palloc0_noexcept(sizeof(LWLockEventStartInfo));
|
|
if (info == NULL) {
|
|
break;
|
|
}
|
|
((LWLockEventStartInfo *)info)->eventType = (char)detail->type;
|
|
((LWLockEventStartInfo *)info)->timestamp = curTime;
|
|
((LWLockEventStartInfo *)info)->id = detail->lwlockId;
|
|
((LWLockEventStartInfo *)info)->mode = (LWLockMode)detail->lockmode;
|
|
*size = sizeof(LWLockEventStartInfo);
|
|
break;
|
|
case LOCK_END:
|
|
case LOCK_WAIT_END:
|
|
case LWLOCK_END:
|
|
case LWLOCK_WAIT_END:
|
|
info = (LockEventEndInfo *)palloc0_noexcept(sizeof(LockEventEndInfo));
|
|
if (info == NULL) {
|
|
break;
|
|
}
|
|
((LockEventEndInfo *)info)->eventType = (char)detail->type;
|
|
((LockEventEndInfo *)info)->timestamp = curTime;
|
|
*size = sizeof(LockEventEndInfo);
|
|
break;
|
|
case TYPE_INVALID:
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return info;
|
|
}
|
|
|
|
static bool statement_extend_detail_item(StatementStatContext *ssctx, bool first)
|
|
{
|
|
MemoryContext oldcontext = MemoryContextSwitchTo(u_sess->statement_cxt.stmt_stat_cxt);
|
|
StatementDetailItem *item = (StatementDetailItem *)palloc0_noexcept(sizeof(StatementDetailItem));
|
|
(void)MemoryContextSwitchTo(oldcontext);
|
|
|
|
if (item == NULL) {
|
|
ereport(LOG, (errmodule(MOD_INSTR), errmsg("[Statement] detail is lost due to OOM.")));
|
|
ssctx->details.oom = true;
|
|
return false;
|
|
}
|
|
|
|
if (first) {
|
|
ssctx->details.head = item;
|
|
ssctx->details.tail = item;
|
|
// update list info
|
|
ssctx->details.n_items = 1;
|
|
ssctx->details.head->buf[0] = (char)STATEMENT_DETAIL_VERSION;
|
|
ssctx->details.head->buf[1] = (char)STATEMENT_DETAIL_NOT_TRUNCATED;
|
|
ssctx->details.cur_pos = STATEMENT_DETAILS_HEAD_SIZE;
|
|
} else {
|
|
ssctx->details.tail->next = item;
|
|
ssctx->details.tail = item;
|
|
// update list info
|
|
ssctx->details.n_items++;
|
|
ssctx->details.cur_pos = 0;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void *palloc_statement_detail(StmtDetailType type)
|
|
{
|
|
void *info = NULL;
|
|
switch (type) {
|
|
case LOCK_START:
|
|
case LOCK_WAIT_START:
|
|
case LOCK_RELEASE:
|
|
info = (LockEventStartInfo *)palloc0_noexcept(sizeof(LockEventStartInfo));
|
|
break;
|
|
case LWLOCK_START:
|
|
case LWLOCK_WAIT_START:
|
|
case LWLOCK_RELEASE:
|
|
info = (LWLockEventStartInfo *)palloc0_noexcept(sizeof(LWLockEventStartInfo));
|
|
break;
|
|
case LOCK_END:
|
|
case LOCK_WAIT_END:
|
|
case LWLOCK_END:
|
|
case LWLOCK_WAIT_END:
|
|
info = (LockEventEndInfo *)palloc0_noexcept(sizeof(LockEventEndInfo));
|
|
break;
|
|
case TYPE_INVALID:
|
|
default:
|
|
break;
|
|
}
|
|
return info;
|
|
}
|
|
|
|
const char* get_statement_event_type(StmtDetailType type)
|
|
{
|
|
switch (type) {
|
|
case LOCK_START:
|
|
return "LOCK_START";
|
|
case LOCK_WAIT_START:
|
|
return "LOCK_WAIT_START";
|
|
case LOCK_RELEASE:
|
|
return "LOCK_RELEASE";
|
|
case LWLOCK_START:
|
|
return "LWLOCK_START";
|
|
case LWLOCK_WAIT_START:
|
|
return "LWLOCK_WAIT_START";
|
|
case LWLOCK_RELEASE:
|
|
return "LWLOCK_RELEASE";
|
|
case LOCK_END:
|
|
return "LOCK_END";
|
|
case LOCK_WAIT_END:
|
|
return "LOCK_WAIT_END";
|
|
case LWLOCK_END:
|
|
return "LWLOCK_END";
|
|
case LWLOCK_WAIT_END:
|
|
return "LWLOCK_WAIT_END";
|
|
case TYPE_INVALID:
|
|
default:
|
|
return "TYPE_INVALID";
|
|
}
|
|
}
|
|
|
|
static bool check_statement_detail_info_record(StatementStatContext *ssctx, const char *detail)
|
|
{
|
|
if (u_sess->attr.attr_common.track_stmt_details_size == 0 || ssctx->details.oom) {
|
|
return false;
|
|
}
|
|
|
|
if (ssctx->details.n_items > 0 && u_sess->attr.attr_common.track_stmt_details_size <
|
|
(ssctx->details.n_items - 1) * STATEMENT_DETAIL_BUFSIZE + ssctx->details.cur_pos) {
|
|
ssctx->details.head->buf[1] = (char)STATEMENT_DETAIL_TRUNCATED;
|
|
return false;
|
|
}
|
|
|
|
if (detail == NULL) {
|
|
ssctx->details.oom = true;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void statement_detail_info_record(StatementStatContext *ssctx, const char *detail, size_t detailSize)
|
|
{
|
|
errno_t rc;
|
|
|
|
if (!check_statement_detail_info_record(ssctx, detail))
|
|
return;
|
|
|
|
Assert(detailSize < STATEMENT_DETAIL_BUFSIZE);
|
|
|
|
if (ssctx->details.n_items <= 0) {
|
|
if (statement_extend_detail_item(ssctx, true)) {
|
|
rc = memcpy_s(ssctx->details.tail->buf + ssctx->details.cur_pos,
|
|
STATEMENT_DETAIL_BUFSIZE - ssctx->details.cur_pos, detail, detailSize);
|
|
securec_check(rc, "", "");
|
|
ssctx->details.cur_pos += detailSize;
|
|
}
|
|
} else if (ssctx->details.cur_pos + detailSize <= STATEMENT_DETAIL_BUFSIZE) {
|
|
rc = memcpy_s(ssctx->details.tail->buf + ssctx->details.cur_pos,
|
|
STATEMENT_DETAIL_BUFSIZE - ssctx->details.cur_pos, detail, detailSize);
|
|
securec_check(rc, "", "");
|
|
ssctx->details.cur_pos += detailSize;
|
|
} else {
|
|
size_t last = STATEMENT_DETAIL_BUFSIZE - ssctx->details.cur_pos;
|
|
|
|
if (last > 0) {
|
|
rc = memcpy_s(ssctx->details.tail->buf + ssctx->details.cur_pos, last, detail, last);
|
|
securec_check(rc, "", "");
|
|
}
|
|
|
|
if (statement_extend_detail_item(ssctx, false)) {
|
|
rc = memcpy_s(ssctx->details.tail->buf,
|
|
STATEMENT_DETAIL_BUFSIZE, detail + last, detailSize - last);
|
|
securec_check(rc, "", "");
|
|
ssctx->details.cur_pos += detailSize - last;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void update_stmt_lock_cnt(StatementStatContext *ssctx, StmtDetailType type, int lockmode)
|
|
{
|
|
switch (type) {
|
|
case LOCK_START:
|
|
ssctx->lock_summary.lock_cnt++;
|
|
break;
|
|
case LOCK_WAIT_START:
|
|
ssctx->lock_summary.lock_wait_cnt++;
|
|
break;
|
|
case LWLOCK_START:
|
|
ssctx->lock_summary.lwlock_cnt++;
|
|
break;
|
|
case LWLOCK_WAIT_START:
|
|
ssctx->lock_summary.lwlock_wait_cnt++;
|
|
break;
|
|
case LOCK_RELEASE:
|
|
ssctx->lock_summary.lock_hold_cnt--;
|
|
break;
|
|
case LOCK_END:
|
|
if (lockmode != NoLock) {
|
|
ssctx->lock_summary.lock_hold_cnt++;
|
|
ssctx->lock_summary.lock_max_cnt =
|
|
Max(ssctx->lock_summary.lock_max_cnt, ssctx->lock_summary.lock_hold_cnt);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void update_stmt_lock_time(StatementStatContext *ssctx, StmtDetailType type, TimestampTz curTime)
|
|
{
|
|
switch (type) {
|
|
case LOCK_START:
|
|
ssctx->lock_summary.lock_start_time = curTime;
|
|
break;
|
|
case LOCK_END:
|
|
ssctx->lock_summary.lock_time += curTime - ssctx->lock_summary.lock_start_time;
|
|
break;
|
|
case LOCK_WAIT_START:
|
|
ssctx->lock_summary.lock_wait_start_time = curTime;
|
|
break;
|
|
case LOCK_WAIT_END:
|
|
ssctx->lock_summary.lock_wait_time += curTime - ssctx->lock_summary.lock_wait_start_time;
|
|
break;
|
|
case LWLOCK_START:
|
|
ssctx->lock_summary.lwlock_start_time = curTime;
|
|
break;
|
|
case LWLOCK_END:
|
|
ssctx->lock_summary.lwlock_time += curTime - ssctx->lock_summary.lwlock_start_time;
|
|
break;
|
|
case LWLOCK_WAIT_START:
|
|
ssctx->lock_summary.lwlock_wait_start_time = curTime;
|
|
break;
|
|
case LWLOCK_WAIT_END:
|
|
ssctx->lock_summary.lwlock_wait_time += curTime - ssctx->lock_summary.lwlock_wait_start_time;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void instr_stmt_report_lock(StmtDetailType type, int lockmode, const LOCKTAG *locktag, uint16 lwlockId)
|
|
{
|
|
StatementStatContext *ssctx = (StatementStatContext *)u_sess->statement_cxt.curStatementMetrics;
|
|
|
|
if (likely(ssctx != NULL)) {
|
|
Assert(ssctx->level == STMT_TRACK_L0 ||
|
|
ssctx->level == STMT_TRACK_L1 || ssctx->level == STMT_TRACK_L2);
|
|
|
|
/* always capture the lock count */
|
|
update_stmt_lock_cnt(ssctx, type, lockmode);
|
|
|
|
if (ssctx->level != STMT_TRACK_L0) {
|
|
TimestampTz curTime = GetCurrentTimestamp();
|
|
|
|
update_stmt_lock_time(ssctx, type, curTime);
|
|
if (ssctx->level == STMT_TRACK_L2) {
|
|
size_t size = 0;
|
|
char *bytes = NULL;
|
|
StmtLockDetail detail = {type, locktag, lwlockId, lockmode};
|
|
|
|
bytes = (char*)get_stmt_lock_detail(&detail, curTime, &size);
|
|
statement_detail_info_record(ssctx, bytes, size);
|
|
pfree_ext(bytes);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
char *get_lock_mode_name(LOCKMODE mode)
|
|
{
|
|
switch (mode) {
|
|
case AccessShareLock:
|
|
return "AccessShareLock";
|
|
case RowShareLock:
|
|
return "RowShareLock";
|
|
case RowExclusiveLock:
|
|
return "RowExclusiveLock";
|
|
case ShareUpdateExclusiveLock:
|
|
return "ShareUpdateExclusiveLock";
|
|
case ShareLock:
|
|
return "ShareLock";
|
|
case ShareRowExclusiveLock:
|
|
return "ShareRowExclusiveLock";
|
|
case ExclusiveLock:
|
|
return "ExclusiveLock";
|
|
case AccessExclusiveLock:
|
|
return "AccessExclusiveLock";
|
|
case NoLock:
|
|
default:
|
|
return "INVALID";
|
|
}
|
|
}
|
|
|
|
void write_state_detail_lock_start(void *info, size_t *detailsCnt, StringInfo resultBuf, bool pretty)
|
|
{
|
|
errno_t ret = 0;
|
|
char formatStr[STATEMENT_DETAIL_BUF] = { 0 };
|
|
if (pretty) {
|
|
ret = strcpy_s(formatStr, STATEMENT_DETAIL_BUF, "'%lu'\t'%s'\t'%s'\t'%s'\t'%s'\n");
|
|
} else {
|
|
ret = strcpy_s(formatStr, STATEMENT_DETAIL_BUF, "'%lu'\t'%s'\t'%s'\t'%s'\t'%s',");
|
|
}
|
|
securec_check(ret, "\0", "\0");
|
|
|
|
char *lock_tag = LocktagToString(((LockEventStartInfo *)info)->tag);
|
|
appendStringInfo(resultBuf, formatStr, *detailsCnt,
|
|
get_statement_event_type((StmtDetailType)((LockEventStartInfo *)info)->eventType),
|
|
timestamptz_to_str(((LockEventStartInfo *)info)->timestamp),
|
|
lock_tag,
|
|
get_lock_mode_name(((LockEventStartInfo *)info)->mode));
|
|
pfree(lock_tag);
|
|
(*detailsCnt)++;
|
|
}
|
|
|
|
char *get_lwlock_mode_name(LWLockMode mode)
|
|
{
|
|
switch (mode) {
|
|
case LW_EXCLUSIVE:
|
|
return "LW_EXCLUSIVE";
|
|
case LW_SHARED:
|
|
return "LW_SHARED";
|
|
case LW_WAIT_UNTIL_FREE:
|
|
return "LW_WAIT_UNTIL_FREE";
|
|
default:
|
|
return "INVALID";
|
|
}
|
|
}
|
|
|
|
void write_state_detail_lwlock_start(void *info, size_t *detailsCnt, StringInfo resultBuf, bool pretty)
|
|
{
|
|
errno_t ret = 0;
|
|
char formatStr[STATEMENT_DETAIL_BUF] = { 0 };
|
|
if (pretty) {
|
|
ret = strcpy_s(formatStr, STATEMENT_DETAIL_BUF, "'%lu'\t'%s'\t'%s'\t'%s'\t'%s'\n");
|
|
} else {
|
|
ret = strcpy_s(formatStr, STATEMENT_DETAIL_BUF, "'%lu'\t'%s'\t'%s'\t'%s'\t'%s',");
|
|
}
|
|
securec_check(ret, "\0", "\0");
|
|
appendStringInfo(resultBuf, formatStr, *detailsCnt,
|
|
get_statement_event_type((StmtDetailType)((LWLockEventStartInfo *)info)->eventType),
|
|
timestamptz_to_str(((LWLockEventStartInfo *)info)->timestamp),
|
|
GetLWLockIdentifier(PG_WAIT_LWLOCK, ((LWLockEventStartInfo *)info)->id),
|
|
get_lwlock_mode_name(((LWLockEventStartInfo *)info)->mode));
|
|
(*detailsCnt)++;
|
|
}
|
|
|
|
void write_state_detail_end_event(void *info, size_t *detailsCnt, StringInfo resultBuf, bool pretty)
|
|
{
|
|
errno_t ret = 0;
|
|
char formatStr[STATEMENT_DETAIL_BUF] = { 0 };
|
|
if (pretty) {
|
|
ret = strcpy_s(formatStr, STATEMENT_DETAIL_BUF, "'%lu'\t'%s'\t'%s'\n");
|
|
} else {
|
|
ret = strcpy_s(formatStr, STATEMENT_DETAIL_BUF, "'%lu'\t'%s'\t'%s',");
|
|
}
|
|
securec_check(ret, "\0", "\0");
|
|
appendStringInfo(resultBuf, formatStr, *detailsCnt,
|
|
get_statement_event_type((StmtDetailType)((LockEventEndInfo *)info)->eventType),
|
|
timestamptz_to_str(((LockEventEndInfo *)info)->timestamp));
|
|
(*detailsCnt)++;
|
|
}
|
|
|
|
/* return true if run successfully */
|
|
bool write_statement_detail_text(void *info, StmtDetailType type, StringInfo resultBuf, size_t *detailsCnt, bool pretty)
|
|
{
|
|
bool result = false;
|
|
switch (type) {
|
|
case LOCK_START:
|
|
case LOCK_WAIT_START:
|
|
case LOCK_RELEASE:
|
|
write_state_detail_lock_start(info, detailsCnt, resultBuf, pretty);
|
|
result = true;
|
|
break;
|
|
case LWLOCK_START:
|
|
case LWLOCK_WAIT_START:
|
|
case LWLOCK_RELEASE:
|
|
write_state_detail_lwlock_start(info, detailsCnt, resultBuf, pretty);
|
|
result = true;
|
|
break;
|
|
case LOCK_END:
|
|
case LOCK_WAIT_END:
|
|
case LWLOCK_END:
|
|
case LWLOCK_WAIT_END:
|
|
write_state_detail_end_event(info, detailsCnt, resultBuf, pretty);
|
|
result = true;
|
|
break;
|
|
case TYPE_INVALID:
|
|
default:
|
|
break;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static bytea* get_statement_detail(StatementDetail *detail, bool oom)
|
|
{
|
|
text *result = NULL;
|
|
uint32 size;
|
|
|
|
Assert (detail != NULL && (oom || detail->n_items > 0));
|
|
|
|
if (oom) {
|
|
size = sizeof(uint32) + STATEMENT_DETAILS_HEAD_SIZE;
|
|
} else {
|
|
size = sizeof(uint32) + (uint32)((detail->n_items - 1) * STATEMENT_DETAIL_BUFSIZE) + detail->cur_pos;
|
|
}
|
|
|
|
result = (text*)palloc(size + VARHDRSZ);
|
|
SET_VARSIZE(result, size + VARHDRSZ);
|
|
|
|
int rc = memcpy_s(VARDATA(result), size, &size, sizeof(uint32));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
char *details = VARDATA(result) + sizeof(uint32);
|
|
if (oom) {
|
|
details[0] = (char)STATEMENT_DETAIL_VERSION;
|
|
details[1] = (char)STATEMENT_DETAIL_MISSING_OOM;
|
|
} else {
|
|
uint32 pos = 0;
|
|
size_t itemSize;
|
|
|
|
for (StatementDetailItem *item = detail->head; item != NULL; item = (StatementDetailItem *)(item->next)) {
|
|
itemSize = (item == detail->tail) ? detail->cur_pos : STATEMENT_DETAIL_BUFSIZE;
|
|
|
|
rc = memcpy_s(details + pos, (size - pos - sizeof(uint32)), item->buf, itemSize);
|
|
securec_check(rc, "\0", "\0");
|
|
pos += itemSize;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static void handle_detail_flag(char flag, StringInfo result)
|
|
{
|
|
if (flag == STATEMENT_DETAIL_TRUNCATED) {
|
|
appendStringInfoString(result, "Truncated...");
|
|
} else if (flag == STATEMENT_DETAIL_MISSING_OOM) {
|
|
appendStringInfoString(result, "Missing(OOM)...");
|
|
}
|
|
}
|
|
|
|
static char *decode_statement_detail_text(bytea *detail, const char *format, bool pretty)
|
|
{
|
|
if (strcmp(format, STATEMENT_DETAIL_FORMAT_STRING) != 0) {
|
|
ereport(WARNING, ((errmodule(MOD_INSTR), errmsg("decode format should be 'plaintext'!"))));
|
|
return NULL;
|
|
}
|
|
|
|
uint32 bytea_data_len = VARSIZE(detail) - VARHDRSZ;
|
|
if (bytea_data_len == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
char *details = (char *)VARDATA(detail);
|
|
uint32 detailsLen = 0;
|
|
int rc = memcpy_s(&detailsLen, sizeof(uint32), details, sizeof(uint32));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
if (!is_valid_detail_record(bytea_data_len, details, detailsLen)) {
|
|
return pstrdup("invalid_detail_header_format");
|
|
}
|
|
|
|
StringInfoData resultBuf;
|
|
initStringInfo(&resultBuf);
|
|
char flag = details[sizeof(uint32) + 1];
|
|
size_t pos = sizeof(uint32) + STATEMENT_DETAILS_HEAD_SIZE;
|
|
bool is_valid_record = true;
|
|
size_t detailsCnt = 1;
|
|
|
|
while (pos < detailsLen && is_valid_record) {
|
|
StmtDetailType itemType = (StmtDetailType)details[pos];
|
|
size_t size = get_statement_detail_size(itemType);
|
|
if (size == INVALID_DETAIL_BUFSIZE) {
|
|
is_valid_record = false;
|
|
break;
|
|
}
|
|
if (pos + size > detailsLen) {
|
|
/* invalid buf */
|
|
is_valid_record = false;
|
|
break;
|
|
}
|
|
void *info = palloc_statement_detail(itemType);
|
|
if (info == NULL) {
|
|
break;
|
|
}
|
|
rc = memcpy_s(info, size, details + pos, size);
|
|
securec_check(rc, "", "");
|
|
pos += size;
|
|
|
|
/* write to string */
|
|
is_valid_record = write_statement_detail_text(info, itemType, &resultBuf, &detailsCnt, pretty);
|
|
pfree(info);
|
|
}
|
|
if (!is_valid_record) {
|
|
pfree(resultBuf.data);
|
|
return pstrdup("invalid_detail_data_format");
|
|
}
|
|
|
|
/* after is_valid_detail_record called, resultBuf.len never equal to 0 */
|
|
resultBuf.data[resultBuf.len - 1] = ' ';
|
|
handle_detail_flag(flag, &resultBuf);
|
|
return resultBuf.data;
|
|
}
|
|
|
|
Datum statement_detail_decode(PG_FUNCTION_ARGS)
|
|
{
|
|
if (PG_ARGISNULL(0) || PG_ARGISNULL(1) || PG_ARGISNULL(2)) {
|
|
PG_RETURN_NULL();
|
|
}
|
|
bytea *detailText = DatumGetByteaP(PG_GETARG_DATUM(0));
|
|
char *format = TextDatumGetCString(PG_GETARG_DATUM(1));
|
|
bool pretty = PG_GETARG_BOOL(2);
|
|
|
|
char *detailStr = decode_statement_detail_text(detailText, format, pretty);
|
|
if (detailStr == NULL) {
|
|
PG_RETURN_NULL();
|
|
}
|
|
|
|
text* result = cstring_to_text(detailStr);
|
|
pfree(detailStr);
|
|
PG_RETURN_TEXT_P(result);
|
|
}
|
|
|
|
static void instr_stmt_check_need_update(bool* to_update_db_name,
|
|
bool* to_update_user_name, bool* to_update_client_addr)
|
|
{
|
|
PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.MyBEEntry;
|
|
|
|
if (u_sess->statement_cxt.db_name == NULL && OidIsValid(beentry->st_databaseid)) {
|
|
*to_update_db_name = true;
|
|
}
|
|
|
|
if (u_sess->statement_cxt.user_name == NULL && OidIsValid(beentry->st_userid)) {
|
|
*to_update_user_name = true;
|
|
}
|
|
|
|
if ((u_sess->statement_cxt.client_addr == NULL && u_sess->statement_cxt.client_port == INSTR_STMT_NULL_PORT) &&
|
|
(beentry->st_clientaddr.addr.ss_family == AF_INET || beentry->st_clientaddr.addr.ss_family == AF_INET6 ||
|
|
beentry->st_clientaddr.addr.ss_family == AF_UNIX)) {
|
|
*to_update_client_addr = true;
|
|
}
|
|
}
|
|
|
|
static void instr_stmt_update_client_info(const PgBackendStatus* beentry)
|
|
{
|
|
SockAddr zero_clientaddr;
|
|
errno_t rc = memset_s(&zero_clientaddr, sizeof(SockAddr), 0, sizeof(SockAddr));
|
|
securec_check(rc, "\0", "\0");
|
|
|
|
u_sess->statement_cxt.client_port = INSTR_STMT_NULL_PORT;
|
|
if (memcmp(&(beentry->st_clientaddr), &zero_clientaddr, sizeof(SockAddr)) != 0) {
|
|
if (beentry->st_clientaddr.addr.ss_family == AF_INET
|
|
#ifdef HAVE_IPV6
|
|
|| beentry->st_clientaddr.addr.ss_family == AF_INET6
|
|
#endif
|
|
) {
|
|
char client_host[NI_MAXHOST] = {0};
|
|
char client_port[NI_MAXSERV] = {0};
|
|
|
|
int ret = pg_getnameinfo_all(&beentry->st_clientaddr.addr, beentry->st_clientaddr.salen,
|
|
client_host, sizeof(client_host), client_port, sizeof(client_port),
|
|
NI_NUMERICHOST | NI_NUMERICSERV);
|
|
if (ret == 0) {
|
|
clean_ipv6_addr(beentry->st_clientaddr.addr.ss_family, client_host);
|
|
u_sess->statement_cxt.client_addr = pstrdup(client_host);
|
|
u_sess->statement_cxt.client_port = atoi(client_port);
|
|
}
|
|
} else if (beentry->st_clientaddr.addr.ss_family == AF_UNIX) {
|
|
u_sess->statement_cxt.client_port = INSTR_STMT_UNIX_DOMAIN_PORT;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Report basic information of statement, including:
|
|
* - database name
|
|
* - node name
|
|
* - user name
|
|
* - client connection
|
|
* - session ID
|
|
*/
|
|
void instr_stmt_report_basic_info()
|
|
{
|
|
if (t_thrd.shemem_ptr_cxt.MyBEEntry == NULL || u_sess->statement_cxt.stmt_stat_cxt == NULL) {
|
|
return;
|
|
}
|
|
|
|
bool to_update_db_name = false;
|
|
bool to_update_user_name = false;
|
|
bool to_update_client_addr = false;
|
|
instr_stmt_check_need_update(&to_update_db_name, &to_update_user_name, &to_update_client_addr);
|
|
|
|
PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.MyBEEntry;
|
|
if (u_sess->statement_cxt.session_id == 0) {
|
|
u_sess->statement_cxt.session_id = ENABLE_THREAD_POOL ? beentry->st_sessionid : beentry->st_procpid;
|
|
}
|
|
if (to_update_db_name || to_update_user_name || to_update_client_addr) {
|
|
ResourceOwner old_cur_owner = t_thrd.utils_cxt.CurrentResourceOwner;
|
|
t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(old_cur_owner, "Full/Slow SQL",
|
|
THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DFX));
|
|
|
|
MemoryContext old_ctx = MemoryContextSwitchTo(u_sess->statement_cxt.stmt_stat_cxt);
|
|
if (to_update_db_name) {
|
|
u_sess->statement_cxt.db_name = get_database_name(beentry->st_databaseid);
|
|
}
|
|
if (to_update_user_name) {
|
|
u_sess->statement_cxt.user_name = GetUserNameById(beentry->st_userid);
|
|
}
|
|
if (to_update_client_addr) {
|
|
instr_stmt_update_client_info(beentry);
|
|
}
|
|
(void)MemoryContextSwitchTo(old_ctx);
|
|
|
|
ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true);
|
|
ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_LOCKS, true, true);
|
|
ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true);
|
|
ResourceOwner tmpOwner = t_thrd.utils_cxt.CurrentResourceOwner;
|
|
t_thrd.utils_cxt.CurrentResourceOwner = old_cur_owner;
|
|
ResourceOwnerDelete(tmpOwner);
|
|
}
|
|
}
|
|
|
|
void instr_stmt_report_start_time()
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
CURRENT_STMT_METRIC_HANDLE->start_time = GetCurrentTimestamp();
|
|
}
|
|
|
|
void instr_stmt_report_finish_time()
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
CURRENT_STMT_METRIC_HANDLE->finish_time = GetCurrentTimestamp();
|
|
}
|
|
|
|
void instr_stmt_report_debug_query_id(uint64 debug_query_id)
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
CURRENT_STMT_METRIC_HANDLE->debug_query_id = debug_query_id;
|
|
}
|
|
|
|
void instr_stmt_report_trace_id(char *trace_id)
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
errno_t rc =
|
|
memcpy_s(CURRENT_STMT_METRIC_HANDLE->trace_id, MAX_TRACE_ID_SIZE, trace_id, strlen(trace_id) + 1);
|
|
securec_check(rc, "\0", "\0");
|
|
}
|
|
|
|
inline void instr_stmt_track_param_query(const char *query)
|
|
{
|
|
if (query == NULL) {
|
|
return;
|
|
}
|
|
if (CURRENT_STMT_METRIC_HANDLE->params == NULL) {
|
|
CURRENT_STMT_METRIC_HANDLE->query = pstrdup(query);
|
|
} else {
|
|
Size len = strlen(query) + strlen(CURRENT_STMT_METRIC_HANDLE->params) + 2;
|
|
CURRENT_STMT_METRIC_HANDLE->query = (char *)palloc(len * sizeof(char));
|
|
errno_t rc = sprintf_s(CURRENT_STMT_METRIC_HANDLE->query, len, "%s;%s",
|
|
query, CURRENT_STMT_METRIC_HANDLE->params);
|
|
securec_check_ss_c(rc, "\0", "\0");
|
|
pfree_ext(CURRENT_STMT_METRIC_HANDLE->params);
|
|
}
|
|
}
|
|
|
|
/* using unique query */
|
|
void instr_stmt_report_query(uint64 unique_query_id)
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
CURRENT_STMT_METRIC_HANDLE->unique_query_id = unique_query_id;
|
|
CURRENT_STMT_METRIC_HANDLE->unique_sql_cn_id = u_sess->unique_sql_cxt.unique_sql_cn_id;
|
|
|
|
if (likely(!is_local_unique_sql() || CURRENT_STMT_METRIC_HANDLE->query)) {
|
|
return;
|
|
}
|
|
|
|
MemoryContext old_ctx = MemoryContextSwitchTo(u_sess->statement_cxt.stmt_stat_cxt);
|
|
|
|
if (!u_sess->attr.attr_common.track_stmt_parameter) {
|
|
CURRENT_STMT_METRIC_HANDLE->query = FindCurrentUniqueSQL();
|
|
} else {
|
|
const char* query_string = NULL;
|
|
char* mask_string = NULL;
|
|
|
|
if (u_sess->unique_sql_cxt.curr_single_unique_sql != NULL) {
|
|
query_string = u_sess->unique_sql_cxt.curr_single_unique_sql;
|
|
} else {
|
|
query_string = t_thrd.postgres_cxt.debug_query_string;
|
|
}
|
|
|
|
if (query_string == NULL) {
|
|
return;
|
|
}
|
|
|
|
mask_string = maskPassword(query_string);
|
|
if (mask_string == NULL) {
|
|
mask_string = (char*)query_string;
|
|
}
|
|
|
|
instr_stmt_track_param_query(mask_string);
|
|
|
|
if (mask_string != query_string) {
|
|
pfree(mask_string);
|
|
}
|
|
}
|
|
|
|
(void)MemoryContextSwitchTo(old_ctx);
|
|
}
|
|
|
|
void instr_stmt_report_txid(uint64 txid)
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
CURRENT_STMT_METRIC_HANDLE->txn_id = txid;
|
|
}
|
|
|
|
/*
|
|
* Entry method to report stat at handle init stage
|
|
* - txn_id/basic info/debug query id
|
|
*/
|
|
void instr_stmt_report_stat_at_handle_init()
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
/* report transaction id */
|
|
if (CURRENT_STMT_METRIC_HANDLE->txn_id == InvalidTransactionId) {
|
|
instr_stmt_report_txid(GetCurrentTransactionIdIfAny());
|
|
}
|
|
|
|
/* save current statement stat level */
|
|
CURRENT_STMT_METRIC_HANDLE->level =
|
|
(StatLevel)Max(u_sess->statement_cxt.statement_level[0], u_sess->statement_cxt.statement_level[1]);
|
|
|
|
/* fill basic information */
|
|
instr_stmt_report_basic_info();
|
|
|
|
/* fill debug_query_id */
|
|
instr_stmt_report_debug_query_id(u_sess->debug_query_id);
|
|
|
|
/* when sql execute error on remote node, unique sql id will be reset,
|
|
* update unique sql info at commit stage will be late
|
|
*/
|
|
if (IsConnFromCoord() && CURRENT_STMT_METRIC_HANDLE->unique_query_id == 0 &&
|
|
u_sess->unique_sql_cxt.unique_sql_id != 0) {
|
|
instr_stmt_report_query(u_sess->unique_sql_cxt.unique_sql_id);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Entry method to report stat at handle commit stage
|
|
* - schema name/application name
|
|
*/
|
|
void instr_stmt_report_stat_at_handle_commit()
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
|
|
MemoryContext old_ctx = MemoryContextSwitchTo(u_sess->statement_cxt.stmt_stat_cxt);
|
|
CURRENT_STMT_METRIC_HANDLE->schema_name = pstrdup(u_sess->attr.attr_common.namespace_search_path);
|
|
CURRENT_STMT_METRIC_HANDLE->application_name = pstrdup(u_sess->attr.attr_common.application_name);
|
|
|
|
/* unit: microseconds */
|
|
CURRENT_STMT_METRIC_HANDLE->slow_query_threshold =
|
|
(int64)u_sess->attr.attr_storage.log_min_duration_statement * 1000;
|
|
|
|
CURRENT_STMT_METRIC_HANDLE->tid = t_thrd.proc_cxt.MyProcPid;
|
|
|
|
/* sql from remote node */
|
|
if (IsConnFromCoord() && CURRENT_STMT_METRIC_HANDLE->unique_query_id == 0 &&
|
|
u_sess->unique_sql_cxt.unique_sql_id != 0) {
|
|
instr_stmt_report_query(u_sess->unique_sql_cxt.unique_sql_id);
|
|
}
|
|
|
|
if (CURRENT_STMT_METRIC_HANDLE->txn_id == InvalidTransactionId) {
|
|
instr_stmt_report_txid(GetCurrentTransactionIdIfAny());
|
|
}
|
|
|
|
instr_stmt_report_finish_time();
|
|
(void)MemoryContextSwitchTo(old_ctx);
|
|
}
|
|
|
|
void instr_stmt_report_soft_parse(uint64 soft_parse)
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
CURRENT_STMT_METRIC_HANDLE->parse.soft_parse += soft_parse;
|
|
}
|
|
|
|
void instr_stmt_report_hard_parse(uint64 hard_parse)
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
CURRENT_STMT_METRIC_HANDLE->parse.hard_parse += hard_parse;
|
|
}
|
|
|
|
void instr_stmt_report_returned_rows(uint64 returned_rows)
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
CURRENT_STMT_METRIC_HANDLE->row_activity.returned_rows += returned_rows;
|
|
}
|
|
|
|
void instr_stmt_report_unique_sql_info(const PgStat_TableCounts *agg_table_stat,
|
|
const int64 timeInfo[], const uint64 *netInfo)
|
|
{
|
|
if (u_sess->unique_sql_cxt.unique_sql_id == 0 || u_sess->statement_cxt.curStatementMetrics == NULL) {
|
|
return;
|
|
}
|
|
|
|
StatementStatContext *ssctx = (StatementStatContext *)u_sess->statement_cxt.curStatementMetrics;
|
|
if (agg_table_stat != NULL) {
|
|
// row activity
|
|
(void)pg_atomic_fetch_add_u64(&ssctx->row_activity.tuples_fetched, (uint64)agg_table_stat->t_tuples_fetched);
|
|
(void)pg_atomic_fetch_add_u64(&ssctx->row_activity.tuples_returned, (uint64)agg_table_stat->t_tuples_returned);
|
|
|
|
(void)pg_atomic_fetch_add_u64(&ssctx->row_activity.tuples_inserted, (uint64)agg_table_stat->t_tuples_inserted);
|
|
(void)pg_atomic_fetch_add_u64(&ssctx->row_activity.tuples_updated, (uint64)agg_table_stat->t_tuples_updated);
|
|
(void)pg_atomic_fetch_add_u64(&ssctx->row_activity.tuples_deleted, (uint64)agg_table_stat->t_tuples_deleted);
|
|
|
|
// cache_io
|
|
(void)pg_atomic_fetch_add_u64(&ssctx->cache_io.blocks_fetched, (uint64)agg_table_stat->t_blocks_fetched);
|
|
(void)pg_atomic_fetch_add_u64(&ssctx->cache_io.blocks_hit, (uint64)agg_table_stat->t_blocks_hit);
|
|
}
|
|
|
|
if (timeInfo != NULL) {
|
|
for (int idx = 0; idx < TOTAL_TIME_INFO_TYPES; idx++) {
|
|
(void)gs_atomic_add_64(&ssctx->timeModel[idx], timeInfo[idx]);
|
|
}
|
|
}
|
|
|
|
if (netInfo != NULL) {
|
|
for (int i = 0; i < TOTAL_NET_INFO_TYPES; i++) {
|
|
(void)pg_atomic_fetch_add_u64(&ssctx->networkInfo[i], netInfo[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool instr_stmt_need_track_plan()
|
|
{
|
|
if (CURRENT_STMT_METRIC_HANDLE == NULL || CURRENT_STMT_METRIC_HANDLE->level <= STMT_TRACK_L0 ||
|
|
CURRENT_STMT_METRIC_HANDLE->level > STMT_TRACK_L2)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
void instr_stmt_report_query_plan(QueryDesc *queryDesc)
|
|
{
|
|
StatementStatContext *ssctx = (StatementStatContext *)u_sess->statement_cxt.curStatementMetrics;
|
|
if (queryDesc == NULL || ssctx == NULL || ssctx->level <= STMT_TRACK_L0
|
|
|| ssctx->level > STMT_TRACK_L2 || ssctx->plan_size != 0 || u_sess->statement_cxt.executer_run_level > 1) {
|
|
return;
|
|
}
|
|
/* when getting plan directly from CN, the plan is partial, deparse plan will be failed,
|
|
* it's reasonable, as CN has the full plan.
|
|
*/
|
|
if (u_sess->exec_cxt.under_stream_runtime && IS_PGXC_DATANODE) {
|
|
return;
|
|
}
|
|
|
|
ExplainState es;
|
|
explain_querydesc(&es, queryDesc);
|
|
|
|
MemoryContext oldcontext = MemoryContextSwitchTo(u_sess->statement_cxt.stmt_stat_cxt);
|
|
ssctx->query_plan = (char*)palloc0((Size)es.str->len + 1);
|
|
errno_t rc =
|
|
memcpy_s(ssctx->query_plan, (size_t)es.str->len, es.str->data, (size_t)es.str->len);
|
|
securec_check(rc, "\0", "\0");
|
|
ssctx->query_plan[es.str->len] = '\0';
|
|
ssctx->plan_size = (uint64)es.str->len + 1;
|
|
(void)MemoryContextSwitchTo(oldcontext);
|
|
|
|
ereport(DEBUG1,
|
|
(errmodule(MOD_INSTR), errmsg("exec_auto_explain %s %s to %lu",
|
|
ssctx->query_plan, queryDesc->sourceText, u_sess->unique_sql_cxt.unique_sql_id)));
|
|
pfree(es.str->data);
|
|
}
|
|
|
|
/* check the header and valid length of the detail binary data */
|
|
static bool is_valid_detail_record(uint32 bytea_data_len, const char *details, uint32 details_len)
|
|
{
|
|
/* VERSIZE(bytea) - VARHDRSZ should be > sizeof(uint32) + STATEMENT_DETAILS_HEAD_SIZE */
|
|
if (bytea_data_len <= (sizeof(uint32) + STATEMENT_DETAILS_HEAD_SIZE)) {
|
|
return false;
|
|
}
|
|
|
|
/* details binary length should be same with VERSIZE(bytea) - VARHDRSZ */
|
|
if (bytea_data_len != details_len) {
|
|
return false;
|
|
}
|
|
|
|
/* record version, should be [1, STATEMENT_DETAIL_VERSION] */
|
|
int32 version = (int32)details[sizeof(uint32)];
|
|
if (version == 0 || version > STATEMENT_DETAIL_VERSION) {
|
|
return false;
|
|
}
|
|
|
|
/* record flag area, should be
|
|
* STATEMENT_DETAIL_NOT_TRUNCATED/STATEMENT_DETAIL_TRUNCATED/STATEMENT_DETAIL_MISSING_OOM
|
|
*/
|
|
int32 flag = (int32)details[sizeof(uint32) + 1];
|
|
if (flag != STATEMENT_DETAIL_NOT_TRUNCATED &&
|
|
flag != STATEMENT_DETAIL_TRUNCATED &&
|
|
flag != STATEMENT_DETAIL_MISSING_OOM) {
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void instr_stmt_dynamic_change_level()
|
|
{
|
|
CHECK_STMT_HANDLE();
|
|
|
|
/* if find user defined statement level, dynamic change the statement track level */
|
|
StatLevel specified_level = instr_track_stmt_find_level();
|
|
if (specified_level <= STMT_TRACK_OFF) {
|
|
return;
|
|
}
|
|
|
|
/* at commit stage, for dynamic track utility, need a symbol to decide flush the handle or not */
|
|
CURRENT_STMT_METRIC_HANDLE->level = (specified_level > CURRENT_STMT_METRIC_HANDLE->level) ?
|
|
specified_level : CURRENT_STMT_METRIC_HANDLE->level;
|
|
CURRENT_STMT_METRIC_HANDLE->dynamic_track_level = specified_level;
|
|
ereport(DEBUG1, (errmodule(MOD_INSTR), errmsg("[Statement] change (%lu) track level to L%d",
|
|
u_sess->unique_sql_cxt.unique_sql_id, CURRENT_STMT_METRIC_HANDLE->level - 1)));
|
|
}
|