> > > According to my colleagues it would be very nice to have this feature. > > > For instance, if you are trying to optimize PostgreSQL for application > > > that uses COPY and you don't have access to or something like this. > > > It could also be useful in some other cases. > > > > This use-case doesn't really make much sense to me. Can you explain it > > in more detail? Is the goal here to replicate all of the statements > > that are changing data in the database? > > The idea is to record application workload in real environment and write > a benchmark based on this record. Then using this benchmark we could try > different OS/DBMS configuration (or maybe hardware), find an extremum, > then change configuration in production environment. > > It's not always possible to change an application or even database (e.g. > to use triggers) for this purpose. For instance, if DBMS is provided as > a service. > > Currently PostgreSQL allows to record all workload _except_ COPY > queries. Considering how easily it could be done I think it's wrong. > Basically the only real question here is how it should look like in > postgresql.conf.
OK, how about introducing a new boolean parameter named log_copy? Corresponding patch is attached. -- Best regards, Aleksander Alekseev
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml index 8c25b45..84a7542 100644 --- a/doc/src/sgml/config.sgml +++ b/doc/src/sgml/config.sgml @@ -5205,6 +5205,20 @@ FROM pg_stat_activity; </listitem> </varlistentry> + <varlistentry id="guc-log-copy" xreflabel="log_copy"> + <term><varname>log_copy</varname> (<type>boolean</type>) + <indexterm> + <primary><varname>log_copy</> configuration parameter</primary> + </indexterm> + </term> + <listitem> + <para> + Controls whether file content is logged during execution of + COPY queries. The default is <literal>off</>. + </para> + </listitem> + </varlistentry> + <varlistentry id="guc-log-replication-commands" xreflabel="log_replication_commands"> <term><varname>log_replication_commands</varname> (<type>boolean</type>) <indexterm> diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c index 5947e72..1863e27 100644 --- a/src/backend/commands/copy.c +++ b/src/backend/commands/copy.c @@ -331,6 +331,38 @@ static bool CopyGetInt16(CopyState cstate, int16 *val); /* + * Logs file content during COPY ... FROM / COPY ... TO execution if + * log_copy = 'on'. + */ +static void +CopyLogStatement(const char* str, bool flush) +{ + static StringInfo logString = NULL; + + if(log_copy == false) + return; + + if(logString == NULL) + { + MemoryContext oldctx = MemoryContextSwitchTo(TopMemoryContext); + logString = makeStringInfo(); + MemoryContextSwitchTo(oldctx); + } + + appendStringInfoString(logString, str); + + if(flush) + { + ereport(LOG, + (errmsg("statement: %s", logString->data), + errhidestmt(true), + errhidecontext(true))); + + resetStringInfo(logString); + } +} + +/* * Send copy start/stop messages for frontend copies. These have changed * in past protocol redesigns. */ @@ -2045,14 +2077,20 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) if (!cstate->binary) { if (need_delim) + { CopySendChar(cstate, cstate->delim[0]); + CopyLogStatement(cstate->delim, false); + } need_delim = true; } if (isnull) { if (!cstate->binary) + { CopySendString(cstate, cstate->null_print_client); + CopyLogStatement(cstate->null_print_client, false); + } else CopySendInt32(cstate, -1); } @@ -2062,6 +2100,9 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) { string = OutputFunctionCall(&out_functions[attnum - 1], value); + + CopyLogStatement(string, false); + if (cstate->csv_mode) CopyAttributeOutCSV(cstate, string, cstate->force_quote_flags[attnum - 1], @@ -2083,6 +2124,7 @@ CopyOneRowTo(CopyState cstate, Oid tupleOid, Datum *values, bool *nulls) } CopySendEndOfRow(cstate); + CopyLogStatement("", true); MemoryContextSwitchTo(oldcontext); } @@ -2914,6 +2956,8 @@ NextCopyFromRawFields(CopyState cstate, char ***fields, int *nfields) if (done && cstate->line_buf.len == 0) return false; + CopyLogStatement(cstate->line_buf.data, true); + /* Parse the line into de-escaped field values */ if (cstate->csv_mode) fldct = CopyReadAttributesCSV(cstate); diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c index bc9d33f..0f035ac 100644 --- a/src/backend/utils/misc/guc.c +++ b/src/backend/utils/misc/guc.c @@ -415,6 +415,8 @@ bool log_planner_stats = false; bool log_executor_stats = false; bool log_statement_stats = false; /* this is sort of all three * above together */ +bool log_copy = false; + bool log_btree_build_stats = false; char *event_source; @@ -1161,6 +1163,15 @@ static struct config_bool ConfigureNamesBool[] = false, check_log_stats, NULL, NULL }, + { + {"log_copy", PGC_SUSET, STATS_MONITORING, + gettext_noop("Writes file content during COPY queries to the server log."), + NULL + }, + &log_copy, + false, + NULL, NULL, NULL + }, #ifdef BTREE_BUILD_STATS { {"log_btree_build_stats", PGC_SUSET, DEVELOPER_OPTIONS, diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h index e1de1a5..4f25331 100644 --- a/src/include/utils/guc.h +++ b/src/include/utils/guc.h @@ -240,6 +240,7 @@ extern bool log_parser_stats; extern bool log_planner_stats; extern bool log_executor_stats; extern bool log_statement_stats; +extern bool log_copy; extern bool log_btree_build_stats; extern PGDLLIMPORT bool check_function_bodies;
signature.asc
Description: PGP signature