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;"