From 6a25c6e1d1036db1162f3137bfc8213ecd7446a4 Mon Sep 17 00:00:00 2001 From: Bruce Momjian <bruce@momjian.us> Date: Wed, 7 Apr 2004 05:05:50 +0000 Subject: [PATCH] > >>1. change the type of "log_statement" option from boolean to string, > >>with allowed values of "all, mod, ddl, none" with default "none". OK, here is a patch that implements #1. Here is sample output: test=> set client_min_messages = 'log'; SET test=> set log_statement = 'mod'; SET test=> select 1; ?column? ---------- 1 (1 row) test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> update test set x=1; LOG: statement: update test set x=1; ERROR: relation "test" does not exist test=> copy test from '/tmp/x'; LOG: statement: copy test from '/tmp/x'; ERROR: relation "test" does not exist test=> copy test to '/tmp/x'; ERROR: relation "test" does not exist test=> prepare xx as select 1; PREPARE test=> prepare xx as update x set y=1; LOG: statement: prepare xx as update x set y=1; ERROR: relation "x" does not exist test=> explain analyze select 1;; QUERY PLAN ------------------------------------------------------------------------------------ Result (cost=0.00..0.01 rows=1 width=0) (actual time=0.006..0.007 rows=1 loops=1) Total runtime: 0.046 ms (2 rows) test=> explain analyze update test set x=1; LOG: statement: explain analyze update test set x=1; ERROR: relation "test" does not exist test=> explain update test set x=1; ERROR: relation "test" does not exist It checks PREPARE and EXECUTE ANALYZE too. The log_statement values are 'none', 'mod', 'ddl', and 'all'. For 'all', it prints before the query is parsed, and for ddl/mod, it does it right after parsing using the node tag (or command tag for CREATE/ALTER/DROP), so any non-parse errors will print after the log line. --- doc/src/sgml/runtime.sgml | 23 ++-- src/backend/tcop/postgres.c | 56 ++++++++- src/backend/utils/misc/guc.c | 112 ++++++++++++------ src/backend/utils/misc/postgresql.conf.sample | 2 +- src/include/tcop/tcopprot.h | 15 ++- src/include/utils/guc.h | 3 +- 6 files changed, 161 insertions(+), 50 deletions(-) diff --git a/doc/src/sgml/runtime.sgml b/doc/src/sgml/runtime.sgml index f23772b8da7..bf9caac2aee 100644 --- a/doc/src/sgml/runtime.sgml +++ b/doc/src/sgml/runtime.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.257 2004/04/05 03:02:03 momjian Exp $ +$PostgreSQL: pgsql/doc/src/sgml/runtime.sgml,v 1.258 2004/04/07 05:05:49 momjian Exp $ --> <Chapter Id="runtime"> @@ -2121,12 +2121,21 @@ SET ENABLE_SEQSCAN TO OFF; </varlistentry> <varlistentry id="guc-log-statement" xreflabel="log_statement"> - <term><varname>log_statement</varname> (<type>boolean</type>)</term> - <listitem> - <para> - Causes each SQL statement to be logged. The default is - off. Only superusers can disable this option if it has been - enabled by an administrator. + <term><varname>log_statement</varname> (<type>string</type>)</term> + <listitem> + <para> + Controls which SQL statement are logged. Valid values are + <literal>all</>, <literal>ddl</>, <literal>mod</>, and + <literal>none</>. <literal>ddl</> logs all data definition + commands like <literal>CREATE</>, <literal>ALTER</>, and + <literal>DROP</> commands. <literal>mod</> logs all + <literal>ddl</> statements, plus <literal>INSERT</>, + <literal>UPDATE</>, <literal>DELETE</>, <literal>TRUNCATE</>, + and <literal>COPY FROM</>. <literal>PREPARE</> and + <literal>EXPLAIN ANALYZE</> statements are also considered for + appropriate commands. The default is <literal>none</>. Only + superusers can reduce the detail of this option if it has been + set by an administrator. </para> <note> diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index ff0ac6aa64a..6cece54b30b 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.397 2004/03/24 22:40:29 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/postgres.c,v 1.398 2004/04/07 05:05:49 momjian Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -87,6 +87,8 @@ bool InError = false; /* flag for logging end of session */ bool Log_disconnections = false; +LogStmtLevel log_statement = LOGSTMT_NONE; + /* * Flags for expensive function optimization -- JMH 3/9/92 */ @@ -471,9 +473,10 @@ pg_parse_and_rewrite(const char *query_string, /* string to execute */ List * pg_parse_query(const char *query_string) { - List *raw_parsetree_list; + List *raw_parsetree_list, + *parsetree_item; - if (log_statement) + if (log_statement == LOGSTMT_ALL) ereport(LOG, (errmsg("statement: %s", query_string))); @@ -482,6 +485,51 @@ pg_parse_query(const char *query_string) raw_parsetree_list = raw_parser(query_string); + /* do log_statement tests for mod and ddl */ + if (log_statement == LOGSTMT_MOD || + log_statement == LOGSTMT_DDL) + { + foreach(parsetree_item, raw_parsetree_list) + { + Node *parsetree = (Node *) lfirst(parsetree_item); + const char *commandTag; + + if (IsA(parsetree, ExplainStmt) && + ((ExplainStmt *)parsetree)->analyze) + parsetree = (Node *)(((ExplainStmt *)parsetree)->query); + + if (IsA(parsetree, PrepareStmt)) + parsetree = (Node *)(((PrepareStmt *)parsetree)->query); + + if (IsA(parsetree, SelectStmt)) + continue; /* optimization for frequent command */ + + if (log_statement == LOGSTMT_MOD && + (IsA(parsetree, InsertStmt) || + IsA(parsetree, UpdateStmt) || + IsA(parsetree, DeleteStmt) || + IsA(parsetree, TruncateStmt) || + (IsA(parsetree, CopyStmt) && + ((CopyStmt *)parsetree)->is_from))) /* COPY FROM */ + { + ereport(LOG, + (errmsg("statement: %s", query_string))); + break; + } + commandTag = CreateCommandTag(parsetree); + if (strncmp(commandTag, "CREATE ", strlen("CREATE ")) == 0 || + strncmp(commandTag, "ALTER ", strlen("ALTER ")) == 0 || + strncmp(commandTag, "DROP ", strlen("DROP ")) == 0 || + IsA(parsetree, GrantStmt) || /* GRANT or REVOKE */ + IsA(parsetree, CommentStmt)) + { + ereport(LOG, + (errmsg("statement: %s", query_string))); + break; + } + } + } + if (log_parser_stats) ShowUsage("PARSER STATISTICS"); @@ -2488,7 +2536,7 @@ PostgresMain(int argc, char *argv[], const char *username) SetConfigOption("log_disconnections", "true", debug_context, gucsource); } if (debug_flag >= 2) - SetConfigOption("log_statement", "true", debug_context, gucsource); + SetConfigOption("log_statement", "all", debug_context, gucsource); if (debug_flag >= 3) SetConfigOption("debug_print_parse", "true", debug_context, gucsource); if (debug_flag >= 4) diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index 215378749b6..adbbda25d48 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -10,7 +10,7 @@ * Written by Peter Eisentraut <peter_e@gmx.net>. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.197 2004/04/05 03:02:07 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.198 2004/04/07 05:05:50 momjian Exp $ * *-------------------------------------------------------------------- */ @@ -86,18 +86,22 @@ static const char *assign_facility(const char *facility, bool doit, GucSource source); #endif -static const char *assign_defaultxactisolevel(const char *newval, - bool doit, GucSource source); -static const char *assign_log_min_messages(const char *newval, - bool doit, GucSource source); +static const char *assign_defaultxactisolevel(const char *newval, bool doit, + GucSource source); +static const char *assign_log_min_messages(const char *newval, bool doit, + GucSource source); static const char *assign_client_min_messages(const char *newval, bool doit, GucSource source); static const char *assign_min_error_statement(const char *newval, bool doit, GucSource source); -static const char *assign_msglvl(int *var, const char *newval, - bool doit, GucSource source); +static const char *assign_msglvl(int *var, const char *newval, bool doit, + GucSource source); static const char *assign_log_error_verbosity(const char *newval, bool doit, GucSource source); +static const char *assign_log_statement(const char *newval, bool doit, + GucSource source); +static const char *assign_log_stmtlvl(int *var, const char *newval, + bool doit, GucSource source); static bool assign_phony_autocommit(bool newval, bool doit, GucSource source); @@ -107,7 +111,6 @@ static bool assign_phony_autocommit(bool newval, bool doit, GucSource source); #ifdef USE_ASSERT_CHECKING bool assert_enabled = true; #endif -bool log_statement = false; bool log_duration = false; bool Debug_print_plan = false; bool Debug_print_parse = false; @@ -145,6 +148,7 @@ int log_min_duration_statement = -1; static char *client_min_messages_str; static char *log_min_messages_str; static char *log_error_verbosity_str; +static char *log_statement_str; static char *log_min_error_statement_str; static char *log_destination_string; static bool phony_autocommit; @@ -527,14 +531,6 @@ static struct config_bool ConfigureNamesBool[] = &ExitOnAnyError, false, NULL, NULL }, - { - {"log_statement", PGC_USERLIMIT, LOGGING_WHAT, - gettext_noop("Logs each SQL statement."), - NULL - }, - &log_statement, - false, NULL, NULL - }, { {"log_duration", PGC_USERLIMIT, LOGGING_WHAT, gettext_noop("Logs the duration each completed SQL statement."), @@ -1442,6 +1438,14 @@ static struct config_string ConfigureNamesString[] = &log_error_verbosity_str, "default", assign_log_error_verbosity, NULL }, + { + {"log_statement", PGC_USERLIMIT, LOGGING_WHAT, + gettext_noop("Sets the type of statements logged."), + gettext_noop("Valid values are \"none\", \"mod\", \"ddl\", and \"all\".") + }, + &log_statement_str, + "none", assign_log_statement, NULL + }, { {"log_min_error_statement", PGC_USERLIMIT, LOGGING_WHEN, @@ -2007,14 +2011,11 @@ InitializeGUCOptions(void) struct config_string *conf = (struct config_string *) gconf; char *str; - /* - * Check to make sure we only have valid - * PGC_USERLIMITs - */ + /* Check to make sure we only have valid PGC_USERLIMITs */ Assert(conf->gen.context != PGC_USERLIMIT || conf->assign_hook == assign_log_min_messages || - conf->assign_hook == assign_client_min_messages || - conf->assign_hook == assign_min_error_statement); + conf->assign_hook == assign_min_error_statement || + conf->assign_hook == assign_log_statement); *conf->variable = NULL; conf->reset_val = NULL; conf->session_val = NULL; @@ -3025,15 +3026,23 @@ set_config_option(const char *name, const char *value, if (record->context == PGC_USERLIMIT && IsUnderPostmaster && !superuser()) { - int old_int_value, - new_int_value; - - /* all USERLIMIT strings are message levels */ - assign_msglvl(&new_int_value, newval, - true, source); - assign_msglvl(&old_int_value, conf->reset_val, - true, source); - if (new_int_value > old_int_value) + int var_value, reset_value, new_value; + const char * (*var_hook) (int *var, const char *newval, + bool doit, GucSource source); + + if (conf->assign_hook == assign_log_statement) + var_hook = assign_log_stmtlvl; + else + var_hook = assign_msglvl; + + (*var_hook) (&new_value, newval, true, source); + (*var_hook) (&reset_value, conf->reset_val, true, + source); + (*var_hook) (&var_value, *conf->variable, true, + source); + + /* Limit non-superuser changes */ + if (new_value > reset_value) { /* Limit non-superuser changes */ if (source > PGC_S_UNPRIVILEGED) @@ -3046,10 +3055,9 @@ set_config_option(const char *name, const char *value, return false; } } - /* Allow change if admin should override */ - assign_msglvl(&old_int_value, *conf->variable, - true, source); - if (new_int_value < old_int_value) + + /* Allow change if admin should override */ + if (new_value < var_value) { if (source < PGC_S_UNPRIVILEGED && record->source > PGC_S_UNPRIVILEGED) @@ -4652,6 +4660,40 @@ assign_log_error_verbosity(const char *newval, bool doit, GucSource source) return newval; /* OK */ } +static const char * +assign_log_statement(const char *newval, bool doit, GucSource source) +{ + return (assign_log_stmtlvl((int *)&log_statement, newval, doit, source)); +} + +static const char * +assign_log_stmtlvl(int *var, const char *newval, bool doit, GucSource source) +{ + if (strcasecmp(newval, "none") == 0) + { + if (doit) + (*var) = LOGSTMT_NONE; + } + else if (strcasecmp(newval, "mod") == 0) + { + if (doit) + (*var) = LOGSTMT_MOD; + } + else if (strcasecmp(newval, "ddl") == 0) + { + if (doit) + (*var) = LOGSTMT_DDL; + } + else if (strcasecmp(newval, "all") == 0) + { + if (doit) + (*var) = LOGSTMT_ALL; + } + else + return NULL; /* fail */ + return newval; /* OK */ +} + static bool assign_phony_autocommit(bool newval, bool doit, GucSource source) { diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample index 536e3de0c06..7983abbf7f0 100644 --- a/src/backend/utils/misc/postgresql.conf.sample +++ b/src/backend/utils/misc/postgresql.conf.sample @@ -191,7 +191,7 @@ # %s=session start timestamp # %x=stop here in non-session processes # %%='%' -#log_statement = false +#log_statement = 'none' # none, mod, ddl, all #log_hostname = false diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index b2520b210e6..046fe3e810e 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.63 2004/03/24 22:40:29 tgl Exp $ + * $PostgreSQL: pgsql/src/include/tcop/tcopprot.h,v 1.64 2004/04/07 05:05:50 momjian Exp $ * * OLD COMMENTS * This file was created so that other c files could get the two @@ -35,6 +35,19 @@ extern DLLIMPORT const char *debug_query_string; extern char *rendezvous_name; extern int max_stack_depth; +/* GUC-configurable parameters */ + +typedef enum +{ + /* Reverse order so GUC USERLIMIT is easier */ + LOGSTMT_ALL, /* log all statements */ + LOGSTMT_DDL, /* log data definition statements */ + LOGSTMT_MOD, /* log modification statements, plus DDL */ + LOGSTMT_NONE /* log no statements */ +} LogStmtLevel; + +extern LogStmtLevel log_statement; + #ifndef BOOTSTRAP_INCLUDE extern List *pg_parse_and_rewrite(const char *query_string, diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index 597f10069fd..ca1f87b1fe1 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -7,7 +7,7 @@ * Copyright (c) 2000-2003, PostgreSQL Global Development Group * Written by Peter Eisentraut <peter_e@gmx.net>. * - * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.44 2004/01/19 19:04:40 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/guc.h,v 1.45 2004/04/07 05:05:50 momjian Exp $ *-------------------------------------------------------------------- */ #ifndef GUC_H @@ -103,7 +103,6 @@ typedef enum } GucSource; /* GUC vars that are actually declared in guc.c, rather than elsewhere */ -extern bool log_statement; extern bool log_duration; extern bool Debug_print_plan; extern bool Debug_print_parse; -- GitLab