*** a/doc/src/sgml/ref/psql-ref.sgml
--- b/doc/src/sgml/ref/psql-ref.sgml
***************
*** 404,409 **** PostgreSQL documentation
--- 404,415 ----
done during a very early stage of start-up, so variables reserved
for internal purposes might get overwritten later.
+
+ When name of variable has a "status." prefix, then content of this
+ variable is synchronized with server custom variable and it is
+ accessable with SET and SHOW
+ commands.
+
***************
*** 2591,2596 **** bar
--- 2597,2615 ----
+ Variables that has prefix "status" are synchronised between server and
+ client side.
+
+ testdb=> \set status.myvar foo bar
+ testdb=> SHOW status.myvar;
+ status.myvar
+ --------------
+ foobar
+ (1 row)
+
+
+
+
If you call \set without a second argument, the
variable is set, with an empty string as value. To unset (i.e., delete)
a variable, use the command \unset. To show the
*** a/src/backend/utils/misc/guc.c
--- b/src/backend/utils/misc/guc.c
***************
*** 3721,3726 **** add_placeholder_variable(const char *name, int elevel)
--- 3721,3735 ----
gen->vartype = PGC_STRING;
/*
+ * Any change of custom status variable is forwarded back to client.
+ * It is implemented via GUC_REPORT flag.
+ */
+ if (strncmp(name, "status.", 7) == 0)
+ {
+ gen->flags |= GUC_REPORT;
+ }
+
+ /*
* The char* is allocated at the end of the struct since we have no
* 'static' place to point to. Note that the current value, as well as
* the boot and reset values, start out NULL.
*** a/src/bin/psql/command.c
--- b/src/bin/psql/command.c
***************
*** 1092,1098 **** exec_command(const char *cmd,
free(opt);
}
! if (!SetVariable(pset.vars, opt0, newval))
{
psql_error("\\%s: error while setting variable\n", cmd);
success = false;
--- 1092,1098 ----
free(opt);
}
! if (!SetVar(pset.vars, opt0, newval))
{
psql_error("\\%s: error while setting variable\n", cmd);
success = false;
***************
*** 1618,1623 **** do_connect(char *dbname, char *user, char *host, char *port)
--- 1618,1625 ----
* connection-dependent variables.
*/
PQsetNoticeProcessor(n_conn, NoticeProcessor, NULL);
+ PQsetStatusVariableReceiver(n_conn, StatusVariableReceiver);
+
pset.db = n_conn;
SyncVariables();
connection_warnings(false); /* Must be after SyncVariables */
***************
*** 1762,1767 **** checkWin32Codepage(void)
--- 1764,1771 ----
void
SyncVariables(void)
{
+ const char **varnames;
+
/* get stuff from connection */
pset.encoding = PQclientEncoding(pset.db);
pset.popt.topt.encoding = pset.encoding;
***************
*** 1775,1780 **** SyncVariables(void)
--- 1779,1802 ----
/* send stuff to it, too */
PQsetErrorVerbosity(pset.db, pset.verbosity);
+
+ varnames = GetVarnames(pset.vars);
+
+ /* sync status variables */
+ if (varnames != NULL)
+ {
+ int i;
+
+ for (i = 0; varnames[i]; i++)
+ {
+ if (is_custom_status_variable(varnames[i]))
+ {
+ ForwardStatusVar(varnames[i], GetVariable(pset.vars, varnames[i]));
+ }
+ }
+ }
+
+ free(varnames);
}
/*
*** a/src/bin/psql/common.c
--- b/src/bin/psql/common.c
***************
*** 183,189 **** NoticeProcessor(void *arg, const char *message)
psql_error("%s", message);
}
!
/*
* Code to support query cancellation
--- 183,199 ----
psql_error("%s", message);
}
! /*
! * for custom status variables
! */
! void
! StatusVariableReceiver(const char *varname, const char *value)
! {
! if (!SetVariable(pset.vars, varname, value))
! {
! psql_error("cannot set custom status variable \"%s\"\n", varname);
! }
! }
/*
* Code to support query cancellation
***************
*** 1668,1670 **** expand_tilde(char **filename)
--- 1678,1740 ----
return *filename;
}
+
+ /*
+ * forward change of some status variable to server
+ */
+ bool
+ ForwardStatusVar(const char *name, const char *value)
+ {
+ char *escaped_value;
+ PQExpBufferData query_buffer;
+ PGresult *res;
+
+ escaped_value = PQescapeLiteral(pset.db, value, strlen(value));
+ if (escaped_value == NULL)
+ {
+ const char *error = PQerrorMessage(pset.db);
+
+ psql_error("%s", error);
+
+ return false;
+ }
+
+ initPQExpBuffer(&query_buffer);
+
+ appendPQExpBuffer(&query_buffer, "SET %s = %s",
+ name,
+ escaped_value);
+ PQfreemem(escaped_value);
+
+ res = PSQLexec(query_buffer.data, false);
+
+ termPQExpBuffer(&query_buffer);
+
+ if (res != NULL)
+ {
+ PQclear(res);
+
+ return true;
+ }
+ else
+ return false;
+ }
+
+ /*
+ * switch handling of variables between status variables (server) and
+ * other variables (local).
+ */
+ bool
+ SetVar(VariableSpace space, const char *name, const char *value)
+ {
+ if (is_custom_status_variable(name) && pset.db)
+ return ForwardStatusVar(name, value);
+
+ if (!SetVariable(pset.vars, name, value))
+ {
+ psql_error("error while setting variable\n");
+ return false;
+ }
+
+ return true;
+ }
*** a/src/bin/psql/common.h
--- b/src/bin/psql/common.h
***************
*** 12,17 ****
--- 12,19 ----
#include
#include "libpq-fe.h"
+ #include "variables.h"
+
#ifdef USE_ASSERT_CHECKING
#include
#define psql_assert(p) assert(p)
***************
*** 39,44 **** psql_error(const char *fmt,...)
--- 41,47 ----
__attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
extern void NoticeProcessor(void *arg, const char *message);
+ extern void StatusVariableReceiver(const char *varname, const char *value);
extern volatile bool sigint_interrupt_enabled;
***************
*** 63,66 **** extern const char *session_username(void);
--- 66,75 ----
extern char *expand_tilde(char **filename);
+ extern bool SetVar(VariableSpace space, const char *name, const char *value);
+ extern const char *GetVar(VariableSpace space, const char *name, bool *is_error);
+
+ extern bool ForwardStatusVar(const char *name, const char *value);
+
+
#endif /* COMMON_H */
*** a/src/bin/psql/psqlscan.l
--- b/src/bin/psql/psqlscan.l
***************
*** 389,395 **** realfail2 ({integer}|{decimal})[Ee][-+]
param \${integer}
/* psql-specific: characters allowed in variable names */
! variable_char [A-Za-z\200-\377_0-9]
other .
--- 389,395 ----
param \${integer}
/* psql-specific: characters allowed in variable names */
! variable_char [A-Za-z\200-\377_0-9.]
other .
*** a/src/bin/psql/startup.c
--- b/src/bin/psql/startup.c
***************
*** 250,255 **** main(int argc, char *argv[])
--- 250,256 ----
}
PQsetNoticeProcessor(pset.db, NoticeProcessor, NULL);
+ PQsetStatusVariableReceiver(pset.db, StatusVariableReceiver);
SyncVariables();
***************
*** 524,530 **** parse_psql_options(int argc, char *argv[], struct adhoc_opts * options)
else
{
*equal_loc = '\0';
! if (!SetVariable(pset.vars, value, equal_loc + 1))
{
fprintf(stderr, _("%s: could not set variable \"%s\"\n"),
pset.progname, value);
--- 525,531 ----
else
{
*equal_loc = '\0';
! if (!SetVar(pset.vars, value, equal_loc + 1))
{
fprintf(stderr, _("%s: could not set variable \"%s\"\n"),
pset.progname, value);
*** a/src/bin/psql/variables.c
--- b/src/bin/psql/variables.c
***************
*** 30,36 **** valid_variable_name(const char *name)
while (*ptr)
{
if (IS_HIGHBIT_SET(*ptr) ||
! strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz"
"_0123456789", *ptr) != NULL)
ptr++;
else
--- 30,36 ----
while (*ptr)
{
if (IS_HIGHBIT_SET(*ptr) ||
! strchr("ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "."
"_0123456789", *ptr) != NULL)
ptr++;
else
***************
*** 41,46 **** valid_variable_name(const char *name)
--- 41,55 ----
}
/*
+ * returns true, when variable has prefix for custom status variables
+ */
+ bool
+ is_custom_status_variable(const char *varname)
+ {
+ return strncmp(varname, "status.", 7) == 0;
+ }
+
+ /*
* A "variable space" is represented by an otherwise-unused struct _variable
* that serves as list header.
*/
***************
*** 296,298 **** DeleteVariable(VariableSpace space, const char *name)
--- 305,342 ----
return true;
}
+
+
+ const char **
+ GetVarnames(VariableSpace space)
+ {
+ int count = 0;
+ int i;
+
+ struct _variable *current,
+ *previous;
+ char **result;
+
+ if (!space)
+ return NULL;
+
+ for (previous = space, current = space->next;
+ current;
+ previous = current, current = current->next)
+ {
+ count++;
+ }
+
+ result = pg_malloc(sizeof(char *) * (count + 1));
+
+ for (previous = space, current = space->next, i = 0;
+ current;
+ previous = current, current = current->next, i++)
+ {
+ result[i] = current->name;
+ }
+
+ result[i] = NULL;
+
+ return result;
+ }
*** a/src/bin/psql/variables.h
--- b/src/bin/psql/variables.h
***************
*** 52,56 **** bool SetVariable(VariableSpace space, const char *name, const char *value);
--- 52,59 ----
bool SetVariableAssignHook(VariableSpace space, const char *name, VariableAssignHook hook);
bool SetVariableBool(VariableSpace space, const char *name);
bool DeleteVariable(VariableSpace space, const char *name);
+ bool is_custom_status_variable(const char *varname);
+ const char **GetVarnames(VariableSpace space);
+
#endif /* VARIABLES_H */
*** a/src/interfaces/libpq/exports.txt
--- b/src/interfaces/libpq/exports.txt
***************
*** 161,163 **** PQping 158
--- 161,164 ----
PQpingParams 159
PQlibVersion 160
PQsetSingleRowMode 161
+ PQsetStatusVariableReceiver 162
*** a/src/interfaces/libpq/fe-connect.c
--- b/src/interfaces/libpq/fe-connect.c
***************
*** 325,330 **** static PQconninfoOption *conninfo_find(PQconninfoOption *connOptions,
--- 325,331 ----
const char *keyword);
static void defaultNoticeReceiver(void *arg, const PGresult *res);
static void defaultNoticeProcessor(void *arg, const char *message);
+ static void defaultStatusVariableReceiver(const char *varname, const char *value);
static int parseServiceInfo(PQconninfoOption *options,
PQExpBuffer errorMessage);
static int parseServiceFile(const char *serviceFile,
***************
*** 2712,2717 **** makeEmptyPGconn(void)
--- 2713,2719 ----
/* install default notice hooks */
conn->noticeHooks.noticeRec = defaultNoticeReceiver;
conn->noticeHooks.noticeProc = defaultNoticeProcessor;
+ conn->statusVariableReceiverProc = defaultStatusVariableReceiver;
conn->status = CONNECTION_BAD;
conn->asyncStatus = PGASYNC_IDLE;
***************
*** 5411,5416 **** defaultNoticeProcessor(void *arg, const char *message)
--- 5413,5446 ----
fprintf(stderr, "%s", message);
}
+ PQstatusVariableReceiver
+ PQsetStatusVariableReceiver(PGconn *conn, PQstatusVariableReceiver proc)
+ {
+ PQstatusVariableReceiver old;
+
+ if (conn == NULL)
+ return NULL;
+
+ old = conn->statusVariableReceiverProc;
+ if (proc)
+ {
+ conn->statusVariableReceiverProc = proc;
+ }
+ return old;
+ }
+
+ /*
+ * The default custom status variable just prints the
+ * name and value on stderr. Applications can override this if they
+ * want use own processing of custom status variables.
+ */
+ static void
+ defaultStatusVariableReceiver(const char *varname, const char *value)
+
+ {
+ fprintf(stderr, "received %s=\"%s\"\n", varname, value);
+ }
+
/*
* returns a pointer to the next token or NULL if the current
* token doesn't match
*** a/src/interfaces/libpq/fe-exec.c
--- b/src/interfaces/libpq/fe-exec.c
***************
*** 910,915 **** pqSaveParameterStatus(PGconn *conn, const char *name, const char *value)
--- 910,920 ----
pgParameterStatus *pstatus;
pgParameterStatus *prev;
+ /* forward custom status variables to own callback processor */
+ if (strncmp(name, "status.", 7) == 0)
+ (*conn->statusVariableReceiverProc) (name, value);
+
+
if (conn->Pfdebug)
fprintf(conn->Pfdebug, "pqSaveParameterStatus: '%s' = '%s'\n",
name, value);
*** a/src/interfaces/libpq/libpq-fe.h
--- b/src/interfaces/libpq/libpq-fe.h
***************
*** 155,160 **** typedef struct pgNotify
--- 155,163 ----
typedef void (*PQnoticeReceiver) (void *arg, const PGresult *res);
typedef void (*PQnoticeProcessor) (void *arg, const char *message);
+ /* Function types for custom status variables handling callbasks */
+ typedef void (*PQstatusVariableReceiver) (const char *varname, const char *value);
+
/* Print options for PQprint() */
typedef char pqbool;
***************
*** 335,340 **** extern PQnoticeProcessor PQsetNoticeProcessor(PGconn *conn,
--- 338,347 ----
PQnoticeProcessor proc,
void *arg);
+ /* Override custom status variable handling routine */
+ extern PQstatusVariableReceiver PQsetStatusVariableReceiver(PGconn *conn,
+ PQstatusVariableReceiver proc);
+
/*
* Used to set callback that prevents concurrent access to
* non-thread safe functions that libpq needs.
*** a/src/interfaces/libpq/libpq-int.h
--- b/src/interfaces/libpq/libpq-int.h
***************
*** 458,463 **** struct pg_conn
--- 458,466 ----
/* Buffer for receiving various parts of messages */
PQExpBufferData workBuffer; /* expansible string */
+
+ /* callback for processing of custom status variables */
+ PQstatusVariableReceiver statusVariableReceiverProc;
};
/* PGcancel stores all data necessary to cancel a connection. A copy of this