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