>
> Well, I didn't attach the updated patch (doing that now).
>
This time for real. Sorry, it's Monday :-p
diff --git a/src/backend/storage/ipc/ipci.c b/src/backend/storage/ipc/ipci.c
index 32ac58f..2e3beaf 100644
--- a/src/backend/storage/ipc/ipci.c
+++ b/src/backend/storage/ipc/ipci.c
@@ -43,6 +43,7 @@
#include "storage/procsignal.h"
#include "storage/sinvaladt.h"
#include "storage/spin.h"
+#include "utils/cmdstatus.h"
shmem_startup_hook_type shmem_startup_hook = NULL;
@@ -139,6 +140,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
size = add_size(size, BTreeShmemSize());
size = add_size(size, SyncScanShmemSize());
size = add_size(size, AsyncShmemSize());
+ size = add_size(size, CmdStatusShmemSize());
#ifdef EXEC_BACKEND
size = add_size(size, ShmemBackendArraySize());
#endif
@@ -243,6 +245,7 @@ CreateSharedMemoryAndSemaphores(bool makePrivate, int port)
ReplicationOriginShmemInit();
WalSndShmemInit();
WalRcvShmemInit();
+ CmdStatusShmemInit();
/*
* Set up other modules that need some shared memory space
diff --git a/src/backend/storage/ipc/procsignal.c b/src/backend/storage/ipc/procsignal.c
index 0abde43..e637be1 100644
--- a/src/backend/storage/ipc/procsignal.c
+++ b/src/backend/storage/ipc/procsignal.c
@@ -26,6 +26,7 @@
#include "storage/shmem.h"
#include "storage/sinval.h"
#include "tcop/tcopprot.h"
+#include "utils/cmdstatus.h"
/*
@@ -296,6 +297,9 @@ procsignal_sigusr1_handler(SIGNAL_ARGS)
if (CheckProcSignal(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN))
RecoveryConflictInterrupt(PROCSIG_RECOVERY_CONFLICT_BUFFERPIN);
+ if (CheckProcSignal(PROCSIG_CMD_STATUS_INFO))
+ HandleCmdStatusInfoInterrupt();
+
if (set_latch_on_sigusr1)
SetLatch(MyLatch);
diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c
index d917af3..1a5b03c 100644
--- a/src/backend/tcop/postgres.c
+++ b/src/backend/tcop/postgres.c
@@ -67,6 +67,7 @@
#include "tcop/pquery.h"
#include "tcop/tcopprot.h"
#include "tcop/utility.h"
+#include "utils/cmdstatus.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/ps_status.h"
@@ -2991,6 +2992,9 @@ ProcessInterrupts(void)
if (ParallelMessagePending)
HandleParallelMessages();
+
+ if (CmdStatusInfoPending)
+ ProcessCmdStatusInfoRequest();
}
diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile
index 3ed0b44..2c8687c 100644
--- a/src/backend/utils/adt/Makefile
+++ b/src/backend/utils/adt/Makefile
@@ -18,7 +18,7 @@ endif
# keep this list arranged alphabetically or it gets to be a mess
OBJS = acl.o arrayfuncs.o array_expanded.o array_selfuncs.o \
array_typanalyze.o array_userfuncs.o arrayutils.o ascii.o \
- bool.o cash.o char.o date.o datetime.o datum.o dbsize.o domains.o \
+ bool.o cash.o char.o cmdstatus.o date.o datetime.o datum.o dbsize.o domains.o \
encode.o enum.o expandeddatum.o \
float.o format_type.o formatting.o genfile.o \
geo_ops.o geo_selfuncs.o inet_cidr_ntop.o inet_net_pton.o int.o \
diff --git a/src/backend/utils/adt/cmdstatus.c b/src/backend/utils/adt/cmdstatus.c
new file mode 100644
index 0000000..5cc6d51
--- /dev/null
+++ b/src/backend/utils/adt/cmdstatus.c
@@ -0,0 +1,691 @@
+/*-------------------------------------------------------------------------
+ *
+ * cmdstatus.c
+ * Definitions for pg_cmdstatus function.
+ *
+ * Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ *
+ * IDENTIFICATION
+ * src/backend/utils/adt/cmdstatus.c
+ *
+ *-------------------------------------------------------------------------
+ */
+
+#include "postgres.h"
+
+#include "funcapi.h"
+#include "miscadmin.h"
+
+#include "access/htup_details.h"
+#include "commands/explain.h"
+#include "lib/stringinfo.h"
+#include "storage/dsm.h"
+#include "storage/ipc.h"
+#include "storage/latch.h"
+#include "storage/proc.h"
+#include "storage/procarray.h"
+#include "storage/procsignal.h"
+#include "storage/shm_mq.h"
+#include "storage/shm_toc.h"
+#include "tcop/dest.h"
+#include "tcop/pquery.h"
+#include "utils/builtins.h"
+#include "utils/cmdstatus.h"
+
+
+typedef enum {
+ CMD_STATUS_REQUEST_EXPLAIN = 1,
+ CMD_STATUS_REQUEST_QUERY_TEXT = 2,
+ CMD_STATUS_REQUEST_PROGRESS_TAG = 3,
+ CMD_STATUS_REQUEST_EXPLAIN_BACKTRACE = 4
+} CmdStatusInfoRequestType;
+
+#define CMD_STATUS_MAX_REQUEST CMD_STATUS_REQUEST_EXPLAIN_BACKTRACE
+
+typedef enum {
+ CMD_STATUS_RESULT_FAILURE = -1,
+ CMD_STATUS_RESULT_SUCCESS = 0,
+ CMD_STATUS_RESULT_BACKEND_IDLE,
+ CMD_STATUS_RESULT_NO_DATA
+} CmdStatusInfoResultCode;
+
+/*
+ * Each process that wants to be able to query other processes for their
+ * status registers itself in a slot in the shared memory by setting the
+ * slot's process ID. The slots array is indexed by backend ID, so any slot
+ * can be assigned at max to one backend at any given time.
+ *
+ * In order to actually query some backend for its status, the interested
+ * process will initialize its *own* slot, create a DSM segment and initialize
+ * a shared memory queue in it, then send the SIGUSR1 with the reason
+ * PROCSIG_CMD_STATUS_INFO to the process being queried. Then the interested
+ * process will wait for the response to be delivered via the shared memory
+ * queue.
+ */
+typedef struct
+{
+ pid_t css_pid; /* the ID of process asking for the status */
+ pid_t sender_css_pid; /* the ID of process that sends back the result */
+ dsm_handle dsm_seg_handle;
+ CmdStatusInfoRequestType request_type;
+ CmdStatusInfoResultCode result_code;
+ bool is_valid;
+ bool is_processed;
+} CmdStatusSlot;
+
+/* TODO: do we really want the aux processes to be able to query other backends? */
+#define NumCmdStatusSlots (MaxBackends + NUM_AUXPROCTYPES)
+
+#define PG_CMD_STATUS_INFO_MAGIC 0x79fb2449
+#define CMD_STATUS_BUFFER_SIZE 1024
+
+/*
+ * These structs are allocated on the program stack as local variables in the
+ * ExecutorRun hook. The top of stack is current_query_stack, see below.
+ *
+ * Capturing the execution stack allows us to inspect the inner-most running
+ * query as well as showing the complete backtrace.
+ */
+typedef struct CmdInfoStack {
+ QueryDesc *query_desc;
+ struct CmdInfoStack *parent;
+} CmdInfoStack;
+
+/* The array of slots pre-allocated on shared memory. */
+static CmdStatusSlot *CmdStatusSlots = NULL;
+
+static volatile CmdStatusSlot *MyCmdStatusSlot = NULL;
+
+static CmdInfoStack *current_query_stack = NULL;
+static int query_stack_size = 0; /* XXX not really used */
+
+static ExecutorRun_hook_type prev_ExecutorRun = NULL;
+
+static void cmdstatus_ExecutorRun(QueryDesc *queryDesc,
+ ScanDirection direction, long count);
+
+static void CleanupCmdStatusSlot(int status, Datum arg);
+static int SendCmdStatusInfoSignal(pid_t target_pid, BackendId target_backend_id,
+ dsm_handle dsm_seg_handle,
+ CmdStatusInfoRequestType request_type);
+static void InvalidateCmdStatusSlot(void);
+static StringInfo ProcessCmdStatusSlot(volatile CmdStatusSlot *slot);
+
+static StringInfo explain_query(QueryDesc *queryDesc);
+static void report_result_code(CmdStatusInfoResultCode result, pid_t target_pid);
+static void RestoreResourceOwner(ResourceOwner prevResourceOwner);
+
+
+Size
+CmdStatusShmemSize(void)
+{
+ return NumCmdStatusSlots * sizeof(CmdStatusSlot);
+}
+
+void
+CmdStatusShmemInit(void)
+{
+ Size size = CmdStatusShmemSize();
+ bool found;
+
+ CmdStatusSlots = (CmdStatusSlot *)
+ ShmemInitStruct("CmdStatusSlots", size, &found);
+
+ if (!found)
+ MemSet(CmdStatusSlots, 0, size);
+}
+
+static void
+CleanupCmdStatusSlot(int status, Datum arg)
+{
+ MemSet(MyCmdStatusSlot, 0, sizeof(CmdStatusSlot));
+ MyCmdStatusSlot = NULL;
+}
+
+/*
+ * CmdStatusInit
+ * Register the current process in the CmdStatusSlots array
+ *
+ * The passed index should be MyBackendId.
+ */
+void
+CmdStatusInit(int css_idx)
+{
+ volatile CmdStatusSlot *slot;
+
+ slot = &CmdStatusSlots[css_idx - 1];
+
+ if (slot->css_pid != 0)
+ elog(LOG, "process %d taking over CmdStatus slot %d, but it's not empty",
+ MyProcPid, css_idx);
+
+ slot->css_pid = MyProcPid;
+ slot->is_valid = false;
+
+ MyCmdStatusSlot = slot;
+
+ on_shmem_exit(CleanupCmdStatusSlot, (Datum) 0);
+
+ /* also install executor hooks */
+ prev_ExecutorRun = ExecutorRun_hook;
+ ExecutorRun_hook = cmdstatus_ExecutorRun;
+}
+
+/* Prepare the slot on receiving side and signal the target process. */
+static int
+SendCmdStatusInfoSignal(pid_t target_pid, BackendId target_backend_id,
+ dsm_handle dsm_seg_handle,
+ CmdStatusInfoRequestType request_type)
+{
+ MyCmdStatusSlot->sender_css_pid = target_pid;
+ MyCmdStatusSlot->dsm_seg_handle = dsm_seg_handle;
+ MyCmdStatusSlot->request_type = request_type;
+ MyCmdStatusSlot->is_processed = false;
+ MyCmdStatusSlot->is_valid = true; /* this should be set the latest */
+
+ return SendProcSignal(target_pid, PROCSIG_CMD_STATUS_INFO, target_backend_id);
+}
+
+/* Clean up the slot for next use by this process. */
+static void
+InvalidateCmdStatusSlot(void)
+{
+ MyCmdStatusSlot->is_valid = false;
+ MyCmdStatusSlot->is_processed = false;
+ MyCmdStatusSlot->sender_css_pid = 0;
+ MyCmdStatusSlot->dsm_seg_handle = 0;
+ MyCmdStatusSlot->request_type = 0;
+}
+
+/*
+ * The executor hook.
+ *
+ * Accumulates the query descriptors on the program stack and takes care of
+ * popping the current frame when leaving the function ab-/normally.
+ */
+static void
+cmdstatus_ExecutorRun(QueryDesc *queryDesc, ScanDirection direction, long count)
+{
+ CmdInfoStack current;
+
+ current.query_desc = queryDesc;
+ current.parent = current_query_stack;
+
+ current_query_stack = ¤t;
+ query_stack_size++;
+
+ PG_TRY();
+ {
+ if (prev_ExecutorRun)
+ prev_ExecutorRun(queryDesc, direction, count);
+ else
+ standard_ExecutorRun(queryDesc, direction, count);
+
+ Assert(current_query_stack == ¤t);
+ Assert(query_stack_size > 0);
+
+ query_stack_size--;
+ current_query_stack = current.parent;
+ }
+ PG_CATCH();
+ {
+ Assert(current_query_stack == ¤t);
+ Assert(query_stack_size > 0);
+
+ query_stack_size--;
+ current_query_stack = current.parent;
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+}
+
+/* Produce an explain plan of a single query descriptor. */
+static StringInfo
+explain_query(QueryDesc *queryDesc)
+{
+ StringInfo str;
+ ExplainState *es;
+
+ es = NewExplainState();
+ es->analyze = false;
+ es->verbose = false;
+ es->buffers = false;
+ es->format = EXPLAIN_FORMAT_TEXT;
+
+ ExplainBeginOutput(es);
+ /* XXX: appendStringInfo(es->str, "#%d ", depth); ? */
+ ExplainQueryText(es, queryDesc);
+ ExplainPrintPlan(es, queryDesc);
+ ExplainEndOutput(es);
+
+ str = es->str;
+
+ pfree(es);
+ return str;
+}
+
+
+/* signal handler for PROCSIG_CMD_STATUS_INFO */
+void
+HandleCmdStatusInfoInterrupt(void)
+{
+ CmdStatusInfoPending = true;
+ InterruptPending = true;
+
+ SetLatch(MyLatch);
+}
+
+/* Produce a response and set the result code for a single slot. */
+static StringInfo
+ProcessCmdStatusSlot(volatile CmdStatusSlot *slot)
+{
+ StringInfo result = NULL;
+
+ /* Show some optimism, overwrite with error code later if needed. */
+ slot->result_code = CMD_STATUS_RESULT_SUCCESS;
+
+ if (ActivePortal)
+ {
+ switch (slot->request_type)
+ {
+ case CMD_STATUS_REQUEST_EXPLAIN:
+ if (ActivePortal->queryDesc != NULL)
+ result = explain_query(ActivePortal->queryDesc);
+ else if (current_query_stack != NULL)
+ result = explain_query(current_query_stack->query_desc);
+ else
+ slot->result_code = CMD_STATUS_RESULT_NO_DATA;
+ break;
+
+ case CMD_STATUS_REQUEST_EXPLAIN_BACKTRACE:
+ {
+ StringInfo str;
+ CmdInfoStack *query;
+
+ result = makeStringInfo();
+
+ if (current_query_stack != NULL)
+ {
+ for (query = current_query_stack;
+ query != NULL;
+ query = query->parent)
+ {
+ str = explain_query(query->query_desc);
+ appendBinaryStringInfo(result, str->data, str->len);
+
+ pfree(str->data);
+ pfree(str);
+ }
+ }
+ else
+ slot->result_code = CMD_STATUS_RESULT_BACKEND_IDLE;
+ break;
+ }
+
+ case CMD_STATUS_REQUEST_QUERY_TEXT:
+ result = makeStringInfo();
+ appendStringInfoString(result, ActivePortal->sourceText);
+ break;
+
+ case CMD_STATUS_REQUEST_PROGRESS_TAG:
+ if (ActivePortal->commandTag != NULL)
+ {
+ result = makeStringInfo();
+
+ if (ActivePortal->queryDesc != NULL &&
+ ActivePortal->queryDesc->estate != NULL)
+ {
+ appendStringInfo(result, "%s %u",
+ ActivePortal->commandTag,
+ ActivePortal->queryDesc->estate->es_processed);
+ }
+ else
+ {
+ /* no progress available, at least show the command tag */
+ appendStringInfoString(result, ActivePortal->commandTag);
+ }
+ }
+ else
+ slot->result_code = CMD_STATUS_RESULT_NO_DATA;
+ break;
+ }
+ }
+ else
+ slot->result_code = CMD_STATUS_RESULT_BACKEND_IDLE;
+
+ return result;
+}
+
+static void
+RestoreResourceOwner(ResourceOwner prevResourceOwner)
+{
+ if (prevResourceOwner != CurrentResourceOwner)
+ {
+ ResourceOwner res_owner = CurrentResourceOwner;
+
+ CurrentResourceOwner = prevResourceOwner;
+ ResourceOwnerDelete(res_owner);
+ }
+}
+
+/*
+ * Process all command status requests from other backends by scanning the
+ * whole array of slots looking for the requests targeted at us, which were
+ * not invalidated or already processed by us during earlier rounds.
+ */
+void
+ProcessCmdStatusInfoRequest(void)
+{
+ /*
+ * We might have been signaled again by another process while handling a
+ * request, so re-check after going through all the slots.
+ */
+ while (CmdStatusInfoPending)
+ {
+ int i;
+
+ CmdStatusInfoPending = false;
+
+ for (i = 0; i < NumCmdStatusSlots; i++)
+ {
+ volatile CmdStatusSlot *slot = &CmdStatusSlots[i];
+
+ if (slot->sender_css_pid == MyProcPid &&
+ slot->is_valid && !slot->is_processed)
+ {
+ ResourceOwner prevResourceOwner = CurrentResourceOwner;
+ dsm_segment *seg = NULL;
+ shm_toc *toc = NULL;
+ shm_mq_handle *output;
+ shm_mq *mq;
+
+ /* Ensure valid resource owner for access to dsm. */
+ if (CurrentResourceOwner == NULL)
+ CurrentResourceOwner = ResourceOwnerCreate(NULL, "ProcessCmdStatusInfoRequest");
+
+ seg = dsm_attach(slot->dsm_seg_handle);
+ if (seg == NULL)
+ {
+ elog(LOG, "unable to map dynamic memory segment for command status");
+
+ slot->is_processed = true;
+ slot->is_valid = false;
+
+ RestoreResourceOwner(prevResourceOwner);
+ continue;
+ }
+
+ toc = shm_toc_attach(PG_CMD_STATUS_INFO_MAGIC, dsm_segment_address(seg));
+ if (toc == NULL)
+ {
+ elog(LOG, "bad magic in dynamic memory segment for command status");
+
+ slot->is_processed = true;
+ slot->is_valid = false;
+
+ dsm_detach(seg);
+
+ RestoreResourceOwner(prevResourceOwner);
+ continue;
+ }
+
+ mq = shm_toc_lookup(toc, 0);
+ output = shm_mq_attach(mq, seg, NULL);
+
+ PG_TRY();
+ {
+ StringInfo payload;
+ int res;
+
+ payload = ProcessCmdStatusSlot(slot);
+ if (payload != NULL)
+ {
+ /*
+ * It's important to set the processed flag and result
+ * code *before* sending the message through the
+ * queue. Otherwise it may result that the receiving
+ * backend will wake up the queue and re-use its slot
+ * to send another status request (it doesn't matter
+ * if that would be a request to the same backend or a
+ * different one).
+ *
+ * The message queue serves as a barrier here: it
+ * should be generally safe to assume that before we
+ * send anything to the queue or detach from it, the
+ * receiving backend is waiting.
+ */
+ slot->is_processed = true;
+
+ /*
+ * Send the data, including null terminator.
+ *
+ * This blocks until the receiving side completes
+ * reading or detaches from the queue.
+ */
+ res = shm_mq_send(output, payload->len + 1, payload->data, false);
+ if (res != SHM_MQ_SUCCESS)
+ {
+ elog(LOG, "cannot send command status to backend with PID %d",
+ slot->css_pid);
+ slot->is_valid = false;
+ }
+ pfree(payload->data);
+ pfree(payload);
+ }
+ else
+ {
+ slot->is_valid = false;
+
+ /*
+ * If there was no payload to send to the receiver, we
+ * just detach from the queue and the other side
+ * should wake up.
+ */
+ }
+
+ shm_mq_detach(mq);
+ dsm_detach(seg);
+
+ RestoreResourceOwner(prevResourceOwner);
+ }
+ PG_CATCH();
+ {
+ slot->is_processed = true;
+ slot->is_valid = false;
+
+ /*
+ * Detaching from the queue also wakes up the receiver, be
+ * sure to update the slot status before detaching.
+ */
+ shm_mq_detach(mq);
+ dsm_detach(seg);
+
+ RestoreResourceOwner(prevResourceOwner);
+
+ PG_RE_THROW(); /* XXX is it better to log/suppress the error here? */
+ }
+ PG_END_TRY();
+ }
+ }
+ }
+}
+
+static void
+report_result_code(CmdStatusInfoResultCode result, pid_t target_pid)
+{
+ switch (result)
+ {
+ case CMD_STATUS_RESULT_BACKEND_IDLE:
+ elog(INFO, "no command is currently running in backend with PID %d",
+ target_pid);
+ break;
+
+ case CMD_STATUS_RESULT_NO_DATA:
+ elog(WARNING, "no suitable data found for the query in backend with PID %d",
+ target_pid);
+ break;
+
+ default:
+ elog(ERROR, "general command status request failure");
+ break;
+ }
+}
+
+/*
+ * Try to get status of a command running in another process.
+ *
+ * FUNCTION pg_cmdstatus(pid, request_type)
+ * RETURNS SETOF text
+ */
+Datum
+pg_cmdstatus(PG_FUNCTION_ARGS)
+{
+ ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+ Tuplestorestate *tupstore;
+ TupleDesc tupdesc;
+ MemoryContext per_query_ctx;
+ MemoryContext oldcontext;
+ pid_t target_pid = (pid_t) PG_GETARG_INT32(0);
+ int request_type = PG_GETARG_INT32(1);
+ PGPROC *proc;
+ shm_toc_estimator estimator;
+ Size segsize;
+ dsm_segment *seg = NULL;
+ shm_toc *toc = NULL;
+ shm_mq *mq = NULL;
+ shm_mq_handle *input = NULL;
+
+ /* check to see if caller supports us returning a tuplestore */
+ if (rsinfo == NULL || !IsA(rsinfo, ReturnSetInfo))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("set-valued function called in context that cannot accept a set")));
+
+ if (!(rsinfo->allowedModes & SFRM_Materialize))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("materialize mode required, but it is not allowed in this context")));
+
+ if (request_type < 1 || request_type > CMD_STATUS_MAX_REQUEST)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("unknown command status request")));
+
+ /* need to build tuplestore in query context */
+ per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+ oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+ tupdesc = CreateTupleDescCopy(rsinfo->expectedDesc);
+ tupstore = tuplestore_begin_heap(false, false, work_mem);
+ MemoryContextSwitchTo(oldcontext);
+
+ if (target_pid == MyProcPid)
+ ereport(ERROR,
+ (errmsg("backend cannot query command status of itself")));
+
+ /* verify access to target_pid */
+ proc = BackendPidGetProc(target_pid);
+
+ if (proc == NULL)
+ ereport(ERROR,
+ (errmsg("PID %d is not a PostgreSQL server process", target_pid)));
+
+ if (!(superuser() || proc->roleId == GetUserId()))
+ ereport(ERROR,
+ (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
+ (errmsg("must be superuser or have the same role to explain queries running in other server processes"))));
+
+ /* prepare shared dsm segment */
+ shm_toc_initialize_estimator(&estimator);
+ shm_toc_estimate_keys(&estimator, 1);
+ shm_toc_estimate_chunk(&estimator, CMD_STATUS_BUFFER_SIZE);
+ segsize = shm_toc_estimate(&estimator);
+
+ seg = dsm_create(segsize, 0);
+
+ toc = shm_toc_create(PG_CMD_STATUS_INFO_MAGIC, dsm_segment_address(seg),
+ segsize);
+
+ /* prepare basic structures */
+ mq = shm_mq_create(shm_toc_allocate(toc, CMD_STATUS_BUFFER_SIZE),
+ CMD_STATUS_BUFFER_SIZE);
+
+ shm_mq_set_receiver(mq, MyProc);
+ shm_mq_set_sender(mq, proc);
+ shm_toc_insert(toc, 0, mq);
+
+ PG_TRY();
+ {
+ shm_mq_result res;
+ char *data = NULL;
+ Size length;
+
+ input = shm_mq_attach(mq, seg, NULL);
+
+ if (SendCmdStatusInfoSignal(target_pid, proc->backendId,
+ dsm_segment_handle(seg), request_type))
+ {
+ elog(ERROR, "could not signal backend with PID %d", target_pid);
+ }
+
+ /*
+ * This blocks until the sender process writes a message to the queue
+ * or detaches from it. In either case we wake up and process the
+ * result.
+ */
+ res = shm_mq_receive(input, &length, (void **) &data, false);
+ if (res == SHM_MQ_SUCCESS)
+ {
+ const char *p = data;
+
+ /* break into tuples on newline */
+ while (*p)
+ {
+ const char *q = strchr(p, '\n');
+ Size len = q ? q - p : strlen(p);
+ Datum value;
+ HeapTuple tuple;
+ bool isnull = false;
+
+ value = PointerGetDatum(cstring_to_text_with_len(p, len));
+
+ tuple = heap_form_tuple(tupdesc, &value, &isnull);
+ tuplestore_puttuple(tupstore, tuple);
+
+ if (!q)
+ break;
+
+ p += len + 1;
+ }
+ }
+ else
+ {
+ report_result_code(MyCmdStatusSlot->result_code, target_pid);
+ }
+
+ /* clean up and return the tuplestore */
+ tuplestore_donestoring(tupstore);
+
+ InvalidateCmdStatusSlot();
+
+ shm_mq_detach(mq);
+ dsm_detach(seg);
+ }
+ PG_CATCH();
+ {
+ InvalidateCmdStatusSlot();
+
+ shm_mq_detach(mq);
+ dsm_detach(seg);
+
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ rsinfo->returnMode = SFRM_Materialize;
+ rsinfo->setResult = tupstore;
+ rsinfo->setDesc = tupdesc;
+
+ return (Datum) 0;
+}
diff --git a/src/backend/utils/init/globals.c b/src/backend/utils/init/globals.c
index 23e594e..2269853 100644
--- a/src/backend/utils/init/globals.c
+++ b/src/backend/utils/init/globals.c
@@ -29,6 +29,7 @@ ProtocolVersion FrontendProtocol;
volatile bool InterruptPending = false;
volatile bool QueryCancelPending = false;
volatile bool ProcDiePending = false;
+volatile bool CmdStatusInfoPending = false;
volatile bool ClientConnectionLost = false;
volatile uint32 InterruptHoldoffCount = 0;
volatile uint32 QueryCancelHoldoffCount = 0;
diff --git a/src/backend/utils/init/postinit.c b/src/backend/utils/init/postinit.c
index 7b19714..7e7ba77 100644
--- a/src/backend/utils/init/postinit.c
+++ b/src/backend/utils/init/postinit.c
@@ -50,6 +50,7 @@
#include "storage/smgr.h"
#include "tcop/tcopprot.h"
#include "utils/acl.h"
+#include "utils/cmdstatus.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/memutils.h"
@@ -588,6 +589,9 @@ InitPostgres(const char *in_dbname, Oid dboid, const char *username,
/* Now that we have a BackendId, we can participate in ProcSignal */
ProcSignalInit(MyBackendId);
+ /* Init CmdStatus slot */
+ CmdStatusInit(MyBackendId);
+
/*
* Also set up timeout handlers needed for backend operation. We need
* these in every case except bootstrap.
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index ddf7c67..d083aaf 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -3130,6 +3130,8 @@ DESCR("get OID of current session's temp schema, if any");
DATA(insert OID = 2855 ( pg_is_other_temp_schema PGNSP PGUID 12 1 0 0 0 f f f f t f s 1 0 16 "26" _null_ _null_ _null_ _null_ _null_ pg_is_other_temp_schema _null_ _null_ _null_ ));
DESCR("is schema another session's temp schema?");
+DATA(insert OID = 4099 ( pg_cmdstatus PGNSP PGUID 12 1 100 0 0 f f f f f t s 2 0 25 "23 23" _null_ _null_ _null_ _null_ _null_ pg_cmdstatus _null_ _null_ _null_ ));
+DESCR("returns information about another process");
DATA(insert OID = 2171 ( pg_cancel_backend PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ _null_ pg_cancel_backend _null_ _null_ _null_ ));
DESCR("cancel a server process' current query");
DATA(insert OID = 2096 ( pg_terminate_backend PGNSP PGUID 12 1 0 0 0 f f f f t f v 1 0 16 "23" _null_ _null_ _null_ _null_ _null_ pg_terminate_backend _null_ _null_ _null_ ));
diff --git a/src/include/miscadmin.h b/src/include/miscadmin.h
index e0cc69f..5f03f63 100644
--- a/src/include/miscadmin.h
+++ b/src/include/miscadmin.h
@@ -80,6 +80,7 @@
extern PGDLLIMPORT volatile bool InterruptPending;
extern PGDLLIMPORT volatile bool QueryCancelPending;
extern PGDLLIMPORT volatile bool ProcDiePending;
+extern PGDLLIMPORT volatile bool CmdStatusInfoPending;
extern volatile bool ClientConnectionLost;
diff --git a/src/include/storage/procsignal.h b/src/include/storage/procsignal.h
index af1a0cd..cd856e5 100644
--- a/src/include/storage/procsignal.h
+++ b/src/include/storage/procsignal.h
@@ -41,6 +41,8 @@ typedef enum
PROCSIG_RECOVERY_CONFLICT_BUFFERPIN,
PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK,
+ PROCSIG_CMD_STATUS_INFO,
+
NUM_PROCSIGNALS /* Must be last! */
} ProcSignalReason;
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index fc1679e..605612e 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -1243,6 +1243,9 @@ extern Datum pg_identify_object_as_address(PG_FUNCTION_ARGS);
/* catalog/objectaddress.c */
extern Datum pg_get_object_address(PG_FUNCTION_ARGS);
+/* utils/adt/cmdstatus.c */
+extern Datum pg_cmdstatus(PG_FUNCTION_ARGS);
+
/* commands/constraint.c */
extern Datum unique_key_recheck(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/cmdstatus.h b/src/include/utils/cmdstatus.h
new file mode 100644
index 0000000..85627a57
--- /dev/null
+++ b/src/include/utils/cmdstatus.h
@@ -0,0 +1,23 @@
+/*-------------------------------------------------------------------------
+ *
+ * cmdstatus.h
+ * Declarations for command status interrupt handling.
+ *
+ *
+ * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ *
+ * src/include/utils/cmdstatus.h
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef CMDSTATUS_H
+#define CMDSTATUS_H
+
+extern Size CmdStatusShmemSize(void);
+extern void CmdStatusShmemInit(void);
+extern void CmdStatusInit(int css_idx);
+
+extern void HandleCmdStatusInfoInterrupt(void);
+extern void ProcessCmdStatusInfoRequest(void);
+
+#endif /* CMDSTATUS_H */
--
Sent via pgsql-hackers mailing list ([email protected])
To make changes to your subscription:
http://www.postgresql.org/mailpref/pgsql-hackers