GP日志打印函数中关于SQL的部份

Greenplum版本为6.17

正常SQL打印

在postgres.c中正常执行完SQL后会打印SQL信息(根据log_statement的设置区分是否打印),执行打印的步骤位于exec_simple_query函数中的1645行,信息如下:

/* Log immediately if dictated by log_statement */
if (check_log_statement(parsetree_list))
{
    ereport(LOG,
            (errmsg("statement: %s", query_string),
             errhidestmt(true),
             errdetail_execute(parsetree_list)));
    was_logged = true;
}

异常SQL打印

在postgres.c中调用打印两次错误日志信息,相关调用的位置在4979行。第一次打印是EmitErrorReport(),该函数是通过管道将日志信息传输给server/client。第二次打印是通过调用elog()函数,debug_query_string是全局变量,已被赋值为原始SQL。具体为:

/* Report the error to the client and/or server log */
EmitErrorReport();

/*
 * Make sure debug_query_string gets reset before we possibly clobber
 * the storage it points at.
 */
if (debug_query_string != NULL)
{
    elog(LOG, "An exception was encountered during the execution of statement: %s", debug_query_string);
    debug_query_string = NULL;
}

EmitErrorReport()位于elog.c的1815行,定义如下:

/*
 * Actual output of the top-of-stack error message
 *
 * In the ereport(ERROR) case this is called from PostgresMain (or not at all,
 * if the error is caught by somebody).  For all other severity levels this
 * is called by errfinish.
 */
void
EmitErrorReport(void)
{
	ErrorData  *edata = &errordata[errordata_stack_depth];
	MemoryContext oldcontext;

	recursion_depth++;
	CHECK_STACK_DEPTH();
	oldcontext = MemoryContextSwitchTo(edata->assoc_context);

	/* 
	 * CDB: Tidy up the message sent to client
	 *
	 * Strip trailing whitespace.
	 * Append file name and line numebr.
	 */
	if (edata->output_to_client)
		cdb_tidy_message(edata);

	/*
	 * Call hook before sending message to log.  The hook function is allowed
	 * to turn off edata->output_to_server, so we must recheck that afterward.
	 * Making any other change in the content of edata is not considered
	 * supported.
	 *
	 * Note: the reason why the hook can only turn off output_to_server, and
	 * not turn it on, is that it'd be unreliable: we will never get here at
	 * all if errstart() deems the message uninteresting.  A hook that could
	 * make decisions in that direction would have to hook into errstart(),
	 * where it would have much less information available.  emit_log_hook is
	 * intended for custom log filtering and custom log message transmission
	 * mechanisms.
	 */
	if (edata->output_to_server && emit_log_hook)
		(*emit_log_hook) (edata);

	/* Send to server log, if enabled */
	if (edata->output_to_server)
		send_message_to_server_log(edata);

	/* Send to client, if enabled */
	if (edata->output_to_client)
		send_message_to_frontend(edata);

	MemoryContextSwitchTo(oldcontext);
	recursion_depth--;
}

其中edata是日志信息相关的数据结构,emit_log_hook是预留给开发人员的函数指针,即钩子,该钩子声明为NULL,根据需要进行替换调用。

变量edata的数据结构:

/*
 * ErrorData holds the data accumulated during any one ereport() cycle.
 * Any non-NULL pointers must point to palloc'd data.
 * (The const pointers are an exception; we assume they point at non-freeable
 * constant strings.)
 */
typedef struct ErrorData
{
	int			elevel;			/* error level */
	bool		output_to_server;		/* will report to server log? */
	bool		output_to_client;		/* will report to client? */
	bool		show_funcname;	/* true to force funcname inclusion */
    bool        omit_location;  /* GPDB: don't add filename:line# and stack trace */
    bool        fatal_return;   /* GPDB: true => return instead of proc_exit() */
	bool		hide_stmt;		/* true to prevent STATEMENT: inclusion */
	const char *filename;		/* __FILE__ of ereport() call */
	int			lineno;			/* __LINE__ of ereport() call */
	const char *funcname;		/* __func__ of ereport() call */
	const char *domain;			/* message domain */
	const char *context_domain; /* message domain for context message */
	int			sqlerrcode;		/* encoded ERRSTATE */
	char	   *message;		/* primary error message */
	char	   *detail;			/* detail error message */
	char	   *detail_log;		/* detail error message for server log only */
	char	   *hint;			/* hint message */
	char	   *context;		/* context message */
	char	   *schema_name;	/* name of schema */
	char	   *table_name;		/* name of table */
	char	   *column_name;	/* name of column */
	char	   *datatype_name;	/* name of datatype */
	char	   *constraint_name;	/* name of constraint */
	int			cursorpos;		/* cursor index into query string */
	int			internalpos;	/* cursor index into internalquery */
	char	   *internalquery;	/* text of internally-generated query */
	int			saved_errno;	/* errno at entry */

	void	   *stacktracearray[30];
	size_t		stacktracesize;
	bool		printstack;		/* force output stack trace */

	/* context containing associated non-constant strings */
	struct MemoryContextData *assoc_context;
} ErrorData;

实际打印日志到服务端的函数是elog.c中的send_message_to_server_log(edata)中的write_message_to_server_log(),该函数的传参和部分内容如下:

write_message_to_server_log(edata->elevel,
                            edata->sqlerrcode,
                            edata->message,
                            edata->detail_log != NULL ? edata->detail_log : edata->detail,
                            edata->hint,
                            debug_query_string,
                            edata->cursorpos,
                            edata->internalpos,
                            edata->internalquery,
                            edata->context,
                            edata->funcname,
                            edata->show_funcname,
                            edata->filename,
                            edata->lineno,
                            edata->stacktracesize,
                            edata->omit_location,
                            edata->stacktracearray,
                            edata->printstack)

.......................

    /* error_message */
    append_string_to_pipe_chunk(&buffer, message);

.......................

    /* debug_query_string */
    append_string_to_pipe_chunk(&buffer, query_text);

.......................

    gp_write_pipe_chunk((char *) &buffer, buffer.hdr.len + PIPE_HEADER_SIZE);

.......................

其中的edata->message保存的是错误SQL的报错信息,debug_query_string是全局变量,不属于edata数据结构,其值在postgres.c中已被赋为query_string(原始的SQL)。gp_write_pipe_chunk是将日志传递到管道(打印日志)的实际执行函数。

其中buffer的数据结构如下:

typedef struct 
{
	PipeProtoHeader hdr; 
	char		data[PIPE_MAX_PAYLOAD];
} PipeProtoChunk;

buffer.data中保存了SQL信息,通过gp_write_pipe_chunk输出到日志。

这里需要注意edata->message与debug_query_string的区别,目前来看,edata->message是SQL解析时的报错信息,debug_query_string是原始的SQL。可以通过下面的例子来看:
实际运行SQL:

postgres=# alter rolee t2 with password '123456';

Gdb调试中打印信息:

(gdb) p edata->message
$2 = 0x1e25658 "syntax error at or near \"rolee\""
(gdb) p debug_query_string
$3 = 0x1e45068 "alter rolee t2 with password '123456'\n;"
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值