Hello,
I'd like to propose a patch to log bind parameter values not only when
logging duration,
but also on error (timeout in particular) or in whatever situation the
statement normally gets logged.
This mostly could be useful when the request originator doesn't log them
either, so it's hard
to reproduce the problem.
Unfortunately, when enabled, the feature comes with some memory and CPU
overhead,
as we cannot convert certain values to text when in aborted transaction.
We potentially could do the trick with built-in types, but it would need
cautious work with composite types,
and also require more computation on the logging stage, which is a risk
of cascading errors.
Custom types still wouldn't be loggable, even as passed by client, which
would be not great.
So I decided to cache textual representations on bind stage,
which is especially easy if the client uses text protocol.
Best,
Alexey
diff --git a/doc/src/sgml/config.sgml b/doc/src/sgml/config.sgml
index 4a7121a..7b38aed 100644
--- a/doc/src/sgml/config.sgml
+++ b/doc/src/sgml/config.sgml
@@ -6265,6 +6265,23 @@ log_line_prefix = '%m [%p] %q%u@%d/%a '
</listitem>
</varlistentry>
+ <varlistentry id="guc-log-parameters" xreflabel="log_parameters">
+ <term><varname>log_parameters</varname> (<type>boolean</type>)
+ <indexterm>
+ <primary><varname>log_parameters</varname> configuration parameter</primary>
+ </indexterm>
+ </term>
+ <listitem>
+ <para>
+ Controls whether the statement is logged with bind parameter values.
+ It adds some overhead, as postgres will cache textual
+ representations of parameter values in memory for all statements,
+ even if they eventually do not get logged. The default is
+ <literal>off</literal>. Only superusers can change this setting.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="guc-log-statement" xreflabel="log_statement">
<term><varname>log_statement</varname> (<type>enum</type>)
<indexterm>
diff --git a/src/backend/commands/portalcmds.c b/src/backend/commands/portalcmds.c
index 5684997..ba431ce 100644
--- a/src/backend/commands/portalcmds.c
+++ b/src/backend/commands/portalcmds.c
@@ -94,7 +94,7 @@ PerformCursorOpen(DeclareCursorStmt *cstmt, ParamListInfo params,
/*
* Create a portal and copy the plan and queryString into its memory.
*/
- portal = CreatePortal(cstmt->portalname, false, false);
+ portal = CreatePortal(cstmt->portalname, false, false, false);
oldContext = MemoryContextSwitchTo(portal->portalContext);
diff --git a/src/backend/commands/prepare.c b/src/backend/commands/prepare.c
index 6036b73..363c096 100644
--- a/src/backend/commands/prepare.c
+++ b/src/backend/commands/prepare.c
@@ -404,6 +404,7 @@ EvaluateParams(PreparedStatement *pstmt, List *params,
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = num_params;
+ paramLI->hasTextValues = false;
i = 0;
foreach(l, exprstates)
diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c
index fc7c605..6d047cd 100644
--- a/src/backend/executor/functions.c
+++ b/src/backend/executor/functions.c
@@ -921,6 +921,7 @@ postquel_sub_params(SQLFunctionCachePtr fcache,
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nargs;
+ paramLI->hasTextValues = false;
fcache->paramLI = paramLI;
}
else
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index ad72667..532a365 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -1302,7 +1302,7 @@ SPI_cursor_open_internal(const char *name, SPIPlanPtr plan,
else
{
/* In this path, error if portal of same name already exists */
- portal = CreatePortal(name, false, false);
+ portal = CreatePortal(name, false, false, false);
}
/* Copy the plan's query string into the portal */
@@ -2399,6 +2399,7 @@ _SPI_convert_params(int nargs, Oid *argtypes,
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nargs;
+ paramLI->hasTextValues = false;
for (i = 0; i < nargs; i++)
{
diff --git a/src/backend/nodes/params.c b/src/backend/nodes/params.c
index 79197b1..3c3b152 100644
--- a/src/backend/nodes/params.c
+++ b/src/backend/nodes/params.c
@@ -31,6 +31,8 @@
* set of parameter values. If dynamic parameter hooks are present, we
* intentionally do not copy them into the result. Rather, we forcibly
* instantiate all available parameter values and copy the datum values.
+ *
+ * We don't copy textual representations here.
*/
ParamListInfo
copyParamList(ParamListInfo from)
@@ -53,6 +55,7 @@ copyParamList(ParamListInfo from)
retval->parserSetup = NULL;
retval->parserSetupArg = NULL;
retval->numParams = from->numParams;
+ retval->hasTextValues = false;
for (i = 0; i < from->numParams; i++)
{
@@ -229,6 +232,7 @@ RestoreParamList(char **start_address)
paramLI->parserSetup = NULL;
paramLI->parserSetupArg = NULL;
paramLI->numParams = nparams;
+ paramLI->hasTextValues = false;
for (i = 0; i < nparams; i++)
{
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index 5ab7d3c..2ad56a2 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -183,7 +183,7 @@ static void forbidden_in_wal_sender(char firstchar);
static List *pg_rewrite_query(Query *query);
static bool check_log_statement(List *stmt_list);
static int errdetail_execute(List *raw_parsetree_list);
-static int errdetail_params(ParamListInfo params);
+static int errdetail_params();
static int errdetail_abort(void);
static int errdetail_recovery_conflict(void);
static void start_xact_command(void);
@@ -1154,7 +1154,7 @@ exec_simple_query(const char *query_string)
* Create unnamed portal to run the query or queries in. If there
* already is one, silently drop it.
*/
- portal = CreatePortal("", true, true);
+ portal = CreatePortal("", true, true, true);
/* Don't display the portal in pg_cursors */
portal->visible = false;
@@ -1690,9 +1690,9 @@ exec_bind_message(StringInfo input_message)
* if the unnamed portal is specified.
*/
if (portal_name[0] == '\0')
- portal = CreatePortal(portal_name, true, true);
+ portal = CreatePortal(portal_name, true, true, true);
else
- portal = CreatePortal(portal_name, false, false);
+ portal = CreatePortal(portal_name, false, false, true);
/*
* Prepare to copy stuff into the portal's memory context. We do all this
@@ -1731,6 +1731,9 @@ exec_bind_message(StringInfo input_message)
*/
if (numParams > 0)
{
+ /* GUC value can change, so we remember its state to be consistent */
+ bool need_text_values = log_parameters;
+
params = (ParamListInfo) palloc(offsetof(ParamListInfoData, params) +
numParams * sizeof(ParamExternData));
/* we have static list of params, so no hooks needed */
@@ -1741,6 +1744,8 @@ exec_bind_message(StringInfo input_message)
params->parserSetup = NULL;
params->parserSetupArg = NULL;
params->numParams = numParams;
+ /* mark as not having text values before we have populated them all */
+ params->hasTextValues = false;
for (int paramno = 0; paramno < numParams; paramno++)
{
@@ -1807,9 +1812,31 @@ exec_bind_message(StringInfo input_message)
pval = OidInputFunctionCall(typinput, pstring, typioparam, -1);
- /* Free result of encoding conversion, if any */
- if (pstring && pstring != pbuf.data)
- pfree(pstring);
+ if (pstring)
+ {
+ if (need_text_values)
+ {
+ if (pstring == pbuf.data)
+ {
+ /*
+ * Copy textual representation to portal context.
+ */
+ params->params[paramno].textValue =
+ pstrdup(pstring);
+ }
+ else
+ {
+ /* Reuse the result of encoding conversion for it */
+ params->params[paramno].textValue = pstring;
+ }
+ }
+ else
+ {
+ /* Free result of encoding conversion */
+ if (pstring != pbuf.data)
+ pfree(pstring);
+ }
+ }
}
else if (pformat == 1) /* binary mode */
{
@@ -1835,6 +1862,22 @@ exec_bind_message(StringInfo input_message)
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
errmsg("incorrect binary data format in bind parameter %d",
paramno + 1)));
+
+ /*
+ * Compute textual representation for further logging. We waste
+ * some time and memory here, maybe one day we could skip
+ * certain types like built-in primitives, which are safe to get
+ * it calculated later in an aborted xact.
+ */
+ if (!isNull && need_text_values)
+ {
+ Oid typoutput;
+ bool typisvarlena;
+
+ getTypeOutputInfo(ptype, &typoutput, &typisvarlena);
+ params->params[paramno].textValue =
+ OidOutputFunctionCall(typoutput, pval);
+ }
}
else
{
@@ -1859,10 +1902,22 @@ exec_bind_message(StringInfo input_message)
params->params[paramno].pflags = PARAM_FLAG_CONST;
params->params[paramno].ptype = ptype;
}
+
+ /*
+ * now we can safely set it, as we have textValue populated
+ * for all non-null parameters
+ */
+ params->hasTextValues = need_text_values;
}
else
params = NULL;
+ /*
+ * Set portal parameters early for them to get logged if an error happens
+ * on planning stage
+ */
+ portal->portalParams = params;
+
/* Done storing stuff in portal's context */
MemoryContextSwitchTo(oldContext);
@@ -1936,13 +1991,14 @@ exec_bind_message(StringInfo input_message)
*portal_name ? portal_name : "",
psrc->query_string),
errhidestmt(true),
- errdetail_params(params)));
+ errdetail_params()));
break;
}
if (save_log_statement_stats)
ShowUsage("BIND MESSAGE STATISTICS");
+ PortalClearCurrentTop(portal);
debug_query_string = NULL;
}
@@ -1961,7 +2017,6 @@ exec_execute_message(const char *portal_name, long max_rows)
char completionTag[COMPLETION_TAG_BUFSIZE];
const char *sourceText;
const char *prepStmtName;
- ParamListInfo portalParams;
bool save_log_statement_stats = log_statement_stats;
bool is_xact_command;
bool execute_is_fetch;
@@ -1978,6 +2033,7 @@ exec_execute_message(const char *portal_name, long max_rows)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_CURSOR),
errmsg("portal \"%s\" does not exist", portal_name)));
+ PortalSetCurrentTop(portal);
/*
* If the original query was a null string, just return
@@ -2005,12 +2061,6 @@ exec_execute_message(const char *portal_name, long max_rows)
prepStmtName = pstrdup(portal->prepStmtName);
else
prepStmtName = "<unnamed>";
-
- /*
- * An xact command shouldn't have any parameters, which is a good
- * thing because they wouldn't be around after finish_xact_command.
- */
- portalParams = NULL;
}
else
{
@@ -2019,7 +2069,6 @@ exec_execute_message(const char *portal_name, long max_rows)
prepStmtName = portal->prepStmtName;
else
prepStmtName = "<unnamed>";
- portalParams = portal->portalParams;
}
/*
@@ -2071,7 +2120,7 @@ exec_execute_message(const char *portal_name, long max_rows)
*portal_name ? portal_name : "",
sourceText),
errhidestmt(true),
- errdetail_params(portalParams)));
+ errdetail_params()));
was_logged = true;
}
@@ -2160,7 +2209,7 @@ exec_execute_message(const char *portal_name, long max_rows)
*portal_name ? portal_name : "",
sourceText),
errhidestmt(true),
- errdetail_params(portalParams)));
+ errdetail_params()));
break;
}
@@ -2304,63 +2353,20 @@ errdetail_execute(List *raw_parsetree_list)
* Add an errdetail() line showing bind-parameter data, if available.
*/
static int
-errdetail_params(ParamListInfo params)
+errdetail_params()
{
- /* We mustn't call user-defined I/O functions when in an aborted xact */
- if (params && params->numParams > 0 && !IsAbortedTransactionBlockState())
- {
- StringInfoData param_str;
- MemoryContext oldcontext;
-
- /* This code doesn't support dynamic param lists */
- Assert(params->paramFetch == NULL);
-
- /* Make sure any trash is generated in MessageContext */
- oldcontext = MemoryContextSwitchTo(MessageContext);
-
- initStringInfo(¶m_str);
-
- for (int paramno = 0; paramno < params->numParams; paramno++)
- {
- ParamExternData *prm = ¶ms->params[paramno];
- Oid typoutput;
- bool typisvarlena;
- char *pstring;
- char *p;
-
- appendStringInfo(¶m_str, "%s$%d = ",
- paramno > 0 ? ", " : "",
- paramno + 1);
-
- if (prm->isnull || !OidIsValid(prm->ptype))
- {
- appendStringInfoString(¶m_str, "NULL");
- continue;
- }
-
- getTypeOutputInfo(prm->ptype, &typoutput, &typisvarlena);
+ /* Make sure any trash is generated in MessageContext */
+ MemoryContext oldcontext = MemoryContextSwitchTo(MessageContext);
+ char *params_message = GetCurrentPortalBindParameters();
- pstring = OidOutputFunctionCall(typoutput, prm->value);
-
- appendStringInfoCharMacro(¶m_str, '\'');
- for (p = pstring; *p; p++)
- {
- if (*p == '\'') /* double single quotes */
- appendStringInfoCharMacro(¶m_str, *p);
- appendStringInfoCharMacro(¶m_str, *p);
- }
- appendStringInfoCharMacro(¶m_str, '\'');
-
- pfree(pstring);
- }
-
- errdetail("parameters: %s", param_str.data);
-
- pfree(param_str.data);
-
- MemoryContextSwitchTo(oldcontext);
+ if (params_message)
+ {
+ errdetail("parameters: %s", params_message);
+ pfree(params_message);
}
+ MemoryContextSwitchTo(oldcontext);
+
return 0;
}
diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c
index ca02aac..f7984c9 100644
--- a/src/backend/utils/error/elog.c
+++ b/src/backend/utils/error/elog.c
@@ -76,6 +76,7 @@
#include "tcop/tcopprot.h"
#include "utils/guc.h"
#include "utils/memutils.h"
+#include "utils/portal.h"
#include "utils/ps_status.h"
@@ -344,6 +345,7 @@ errstart(int elevel, const char *filename, int lineno,
{
error_context_stack = NULL;
debug_query_string = NULL;
+ current_top_portal = NULL;
}
}
if (++errordata_stack_depth >= ERRORDATA_STACK_SIZE)
@@ -2788,6 +2790,16 @@ write_csvlog(ErrorData *edata)
if (print_stmt && edata->cursorpos > 0)
appendStringInfo(&buf, "%d", edata->cursorpos);
appendStringInfoChar(&buf, ',');
+ if (print_stmt)
+ {
+ char *param_values = GetCurrentPortalBindParameters();
+ if (param_values != NULL)
+ {
+ appendCSVLiteral(&buf, param_values);
+ pfree(param_values);
+ }
+ }
+ appendStringInfoChar(&buf, ',');
/* file error location */
if (Log_error_verbosity >= PGERROR_VERBOSE)
@@ -2944,6 +2956,17 @@ send_message_to_server_log(ErrorData *edata)
appendStringInfoString(&buf, _("STATEMENT: "));
append_with_tabs(&buf, debug_query_string);
appendStringInfoChar(&buf, '\n');
+
+ if (log_parameters)
+ {
+ char *param_values = GetCurrentPortalBindParameters();
+ if (param_values != NULL)
+ {
+ log_line_prefix(&buf, edata);
+ appendStringInfo(&buf, _("PARAMETERS: %s\n"), param_values);
+ pfree(param_values);
+ }
+ }
}
#ifdef HAVE_SYSLOG
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6fe1939..097a17b 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -464,6 +464,7 @@ extern const struct config_enum_entry dynamic_shared_memory_options[];
* GUC option variables that are exported from this module
*/
bool log_duration = false;
+bool log_parameters = false;
bool Debug_print_plan = false;
bool Debug_print_parse = false;
bool Debug_print_rewritten = false;
@@ -1235,6 +1236,15 @@ static struct config_bool ConfigureNamesBool[] =
NULL, NULL, NULL
},
{
+ {"log_parameters", PGC_SUSET, LOGGING_WHAT,
+ gettext_noop("Logs bind parameters of the logged statements where possible."),
+ NULL
+ },
+ &log_parameters,
+ false,
+ NULL, NULL, NULL
+ },
+ {
{"debug_print_parse", PGC_USERSET, LOGGING_WHAT,
gettext_noop("Logs each query's parse tree."),
NULL
diff --git a/src/backend/utils/misc/postgresql.conf.sample b/src/backend/utils/misc/postgresql.conf.sample
index 1fa02d2..7ce88a2 100644
--- a/src/backend/utils/misc/postgresql.conf.sample
+++ b/src/backend/utils/misc/postgresql.conf.sample
@@ -527,6 +527,7 @@
# e.g. '<%u%%%d> '
#log_lock_waits = off # log lock waits >= deadlock_timeout
#log_statement = 'none' # none, ddl, mod, all
+#log_parameters = off # log statements with bind parameters
#log_replication_commands = off
#log_temp_files = -1 # log temporary files equal or larger
# than the specified size in kilobytes;
diff --git a/src/backend/utils/mmgr/portalmem.c b/src/backend/utils/mmgr/portalmem.c
index 2b014c8..2f1bcb2 100644
--- a/src/backend/utils/mmgr/portalmem.c
+++ b/src/backend/utils/mmgr/portalmem.c
@@ -24,6 +24,7 @@
#include "miscadmin.h"
#include "storage/ipc.h"
#include "utils/builtins.h"
+#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/snapmgr.h"
#include "utils/timestamp.h"
@@ -53,6 +54,7 @@ typedef struct portalhashent
static HTAB *PortalHashTable = NULL;
+
#define PortalHashTableLookup(NAME, PORTAL) \
do { \
PortalHashEnt *hentry; \
@@ -88,6 +90,8 @@ do { \
elog(WARNING, "trying to delete portal name that does not exist"); \
} while(0)
+Portal current_top_portal = NULL;
+
static MemoryContext TopPortalContext = NULL;
@@ -163,6 +167,94 @@ PortalGetPrimaryStmt(Portal portal)
}
/*
+ * GetCurrentPortalBindParameters
+ * Get the string containing parameters data, is used for logging.
+ *
+ * Can return NULL if there are no parameters in the current portal
+ * or no current portal, or the text representation of the parameters is not
+ * available. If returning a non-NULL value, it allocates memory
+ * for the returned string in the current context, and it's the caller's
+ * responsibility to pfree() it if needed.
+ */
+char *
+GetCurrentPortalBindParameters()
+{
+ ParamListInfo params;
+ StringInfoData param_str;
+
+ /* Fail if no current portal */
+ if (!PortalIsValid(current_top_portal))
+ return NULL;
+
+ params = current_top_portal->portalParams;
+
+ /* No parameters to format */
+ if (!params || params->numParams == 0)
+ return NULL;
+
+ /*
+ * We either need textual representation of parameters pre-calcualted,
+ * or call potentially user-defined I/O functions to convert internal
+ * representation into text, which cannot be done in an aborted xact
+ */
+ if (!params->hasTextValues && IsAbortedTransactionBlockState())
+ return NULL;
+
+ initStringInfo(¶m_str);
+
+ /* This code doesn't support dynamic param lists */
+ Assert(params->paramFetch == NULL);
+
+ for (int paramno = 0; paramno < params->numParams; paramno++)
+ {
+ ParamExternData *prm = ¶ms->params[paramno];
+ char *pstring;
+ char *p;
+
+ appendStringInfo(¶m_str, "%s$%d = ",
+ paramno > 0 ? ", " : "",
+ paramno + 1);
+
+ if (prm->isnull)
+ {
+ appendStringInfoString(¶m_str, "NULL");
+ continue;
+ }
+
+ if (params->hasTextValues)
+ pstring = prm->textValue;
+ else
+ {
+ Oid typoutput;
+ bool typisvarlena;
+
+ if (OidIsValid(prm->ptype))
+ {
+ appendStringInfoString(¶m_str, "UNKNOWN TYPE");
+ continue;
+ }
+
+ getTypeOutputInfo(prm->ptype, &typoutput, &typisvarlena);
+ pstring = OidOutputFunctionCall(typoutput, prm->value);
+ }
+
+ appendStringInfoCharMacro(¶m_str, '\'');
+ for (p = pstring; *p; p++)
+ {
+ if (*p == '\'') /* double single quotes */
+ appendStringInfoCharMacro(¶m_str, *p);
+ appendStringInfoCharMacro(¶m_str, *p);
+ }
+ appendStringInfoCharMacro(¶m_str, '\'');
+
+ if (!params->hasTextValues)
+ pfree(pstring);
+ }
+
+ return param_str.data;
+}
+
+/*
* CreatePortal
* Returns a new portal given a name.
*
@@ -170,9 +262,11 @@ PortalGetPrimaryStmt(Portal portal)
* same name (if false, an error is raised).
*
* dupSilent: if true, don't even emit a WARNING.
+ *
+ * markCurrent: mark as current top portal
*/
Portal
-CreatePortal(const char *name, bool allowDup, bool dupSilent)
+CreatePortal(const char *name, bool allowDup, bool dupSilent, bool markCurrent)
{
Portal portal;
@@ -223,6 +317,9 @@ CreatePortal(const char *name, bool allowDup, bool dupSilent)
/* reuse portal->name copy */
MemoryContextSetIdentifier(portal->portalContext, portal->name);
+ if (markCurrent)
+ PortalSetCurrentTop(portal);
+
return portal;
}
@@ -246,7 +343,7 @@ CreateNewPortal(void)
break;
}
- return CreatePortal(portalname, false, false);
+ return CreatePortal(portalname, false, false, false);
}
/*
@@ -458,6 +555,28 @@ MarkPortalFailed(Portal portal)
}
/*
+ * PortalSetCurrentTop
+ * mark a portal as the current one.
+ */
+void
+PortalSetCurrentTop(Portal portal)
+{
+ Assert(current_top_portal == NULL);
+ current_top_portal = portal;
+}
+
+/*
+ * PortalClearCurrentTop
+ * mark a portal as no longer the current one.
+ */
+void
+PortalClearCurrentTop(Portal portal)
+{
+ Assert(current_top_portal == portal);
+ current_top_portal = NULL;
+}
+
+/*
* PortalDrop
* Destroy the portal.
*/
@@ -508,6 +627,9 @@ PortalDrop(Portal portal, bool isTopCommit)
*/
PortalHashTableDelete(portal);
+ if (portal == current_top_portal)
+ current_top_portal = NULL;
+
/* drop cached plan reference, if any */
PortalReleaseCachedPlan(portal);
diff --git a/src/include/nodes/params.h b/src/include/nodes/params.h
index 04b03c7..c441240 100644
--- a/src/include/nodes/params.h
+++ b/src/include/nodes/params.h
@@ -93,6 +93,7 @@ typedef struct ParamExternData
bool isnull; /* is it NULL? */
uint16 pflags; /* flag bits, see above */
Oid ptype; /* parameter's datatype, or 0 */
+ char *textValue; /* textual representation for debug purposes */
} ParamExternData;
typedef struct ParamListInfoData *ParamListInfo;
@@ -116,6 +117,8 @@ typedef struct ParamListInfoData
ParserSetupHook parserSetup; /* parser setup hook */
void *parserSetupArg;
int numParams; /* nominal/maximum # of Params represented */
+ bool hasTextValues; /* whether textValue for all non-null
+ params is populated */
/*
* params[] may be of length zero if paramFetch is supplied; otherwise it
diff --git a/src/include/utils/guc.h b/src/include/utils/guc.h
index 64457c7..2151807 100644
--- a/src/include/utils/guc.h
+++ b/src/include/utils/guc.h
@@ -232,6 +232,7 @@ typedef enum
/* GUC vars that are actually declared in guc.c, rather than elsewhere */
extern bool log_duration;
+extern bool log_parameters;
extern bool Debug_print_plan;
extern bool Debug_print_parse;
extern bool Debug_print_rewritten;
diff --git a/src/include/utils/portal.h b/src/include/utils/portal.h
index e4929b9..bcf74e7 100644
--- a/src/include/utils/portal.h
+++ b/src/include/utils/portal.h
@@ -200,6 +200,11 @@ typedef struct PortalData
*/
#define PortalIsValid(p) PointerIsValid(p)
+/*
+ * The top-level portal that the client is explicitly working with:
+ * creating, binding, executing, or all at one using simple protocol
+ */
+extern PGDLLIMPORT Portal current_top_portal;
/* Prototypes for functions in utils/mmgr/portalmem.c */
extern void EnablePortalManager(void);
@@ -215,13 +220,18 @@ extern void AtSubAbort_Portals(SubTransactionId mySubid,
ResourceOwner myXactOwner,
ResourceOwner parentXactOwner);
extern void AtSubCleanup_Portals(SubTransactionId mySubid);
-extern Portal CreatePortal(const char *name, bool allowDup, bool dupSilent);
+extern Portal CreatePortal(const char *name,
+ bool allowDup,
+ bool dupSilent,
+ bool markCurrent);
extern Portal CreateNewPortal(void);
extern void PinPortal(Portal portal);
extern void UnpinPortal(Portal portal);
extern void MarkPortalActive(Portal portal);
extern void MarkPortalDone(Portal portal);
extern void MarkPortalFailed(Portal portal);
+extern void PortalSetCurrentTop(Portal portal);
+extern void PortalClearCurrentTop(Portal portal);
extern void PortalDrop(Portal portal, bool isTopCommit);
extern Portal GetPortalByName(const char *name);
extern void PortalDefineQuery(Portal portal,
@@ -231,6 +241,7 @@ extern void PortalDefineQuery(Portal portal,
List *stmts,
CachedPlan *cplan);
extern PlannedStmt *PortalGetPrimaryStmt(Portal portal);
+extern char *GetCurrentPortalBindParameters();
extern void PortalCreateHoldStore(Portal portal);
extern void PortalHashTableDeleteAll(void);
extern bool ThereAreNoReadyPortals(void);