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