Modify pg_stat_get_activity to build a tuplestore
authorStephen Frost <sfrost@snowman.net>
Fri, 8 May 2015 23:25:30 +0000 (19:25 -0400)
committerStephen Frost <sfrost@snowman.net>
Fri, 8 May 2015 23:25:30 +0000 (19:25 -0400)
This updates pg_stat_get_activity() to build a tuplestore for its
results instead of using the old-style multiple-call method.  This
simplifies the function, though that wasn't the primary motivation for
the change, which is that we may turn it into a helper function which
can filter the results (or not) much more easily.

src/backend/utils/adt/pgstatfuncs.c

index bbe94c34a15976ea76bb54a9673edfd928c5333d..2b3778b03ad85df70d725fdae6c272429042dd0b 100644 (file)
@@ -524,137 +524,75 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS)
    }
 }
 
+/*
+ * Returns activity of PG backends.
+ */
 Datum
 pg_stat_get_activity(PG_FUNCTION_ARGS)
 {
-   FuncCallContext *funcctx;
-
-   if (SRF_IS_FIRSTCALL())
-   {
-       MemoryContext oldcontext;
-       TupleDesc   tupdesc;
-
-       funcctx = SRF_FIRSTCALL_INIT();
-
-       oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
-
-       tupdesc = CreateTemplateTupleDesc(22, false);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 1, "datid",
-                          OIDOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 2, "pid",
-                          INT4OID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 3, "usesysid",
-                          OIDOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 4, "application_name",
-                          TEXTOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 5, "state",
-                          TEXTOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 6, "query",
-                          TEXTOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 7, "waiting",
-                          BOOLOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 8, "act_start",
-                          TIMESTAMPTZOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 9, "query_start",
-                          TIMESTAMPTZOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 10, "backend_start",
-                          TIMESTAMPTZOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 11, "state_change",
-                          TIMESTAMPTZOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 12, "client_addr",
-                          INETOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 13, "client_hostname",
-                          TEXTOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 14, "client_port",
-                          INT4OID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 15, "backend_xid",
-                          XIDOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 16, "backend_xmin",
-                          XIDOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 17, "ssl",
-                          BOOLOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 18, "sslversion",
-                          TEXTOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 19, "sslcipher",
-                          TEXTOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 20, "sslbits",
-                          INT4OID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 21, "sslcompression",
-                          BOOLOID, -1, 0);
-       TupleDescInitEntry(tupdesc, (AttrNumber) 22, "sslclientdn",
-                          TEXTOID, -1, 0);
-
-       funcctx->tuple_desc = BlessTupleDesc(tupdesc);
-
-       funcctx->user_fctx = palloc0(sizeof(int));
-       if (PG_ARGISNULL(0))
-       {
-           /* Get all backends */
-           funcctx->max_calls = pgstat_fetch_stat_numbackends();
-       }
-       else
-       {
-           /*
-            * Get one backend - locate by pid.
-            *
-            * We lookup the backend early, so we can return zero rows if it
-            * doesn't exist, instead of returning a single row full of NULLs.
-            */
-           int         pid = PG_GETARG_INT32(0);
-           int         i;
-           int         n = pgstat_fetch_stat_numbackends();
-
-           for (i = 1; i <= n; i++)
-           {
-               PgBackendStatus *be = pgstat_fetch_stat_beentry(i);
-
-               if (be)
-               {
-                   if (be->st_procpid == pid)
-                   {
-                       *(int *) (funcctx->user_fctx) = i;
-                       break;
-                   }
-               }
-           }
-
-           if (*(int *) (funcctx->user_fctx) == 0)
-               /* Pid not found, return zero rows */
-               funcctx->max_calls = 0;
-           else
-               funcctx->max_calls = 1;
-       }
-
-       MemoryContextSwitchTo(oldcontext);
-   }
-
-   /* stuff done on every call of the function */
-   funcctx = SRF_PERCALL_SETUP();
-
-   if (funcctx->call_cntr < funcctx->max_calls)
+#define PG_STAT_GET_ACTIVITY_COLS  22
+   int                 num_backends = pgstat_fetch_stat_numbackends();
+   int                 curr_backend;
+   int                 pid = PG_ARGISNULL(0) ? -1 : PG_GETARG_INT32(0);
+   ReturnSetInfo      *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
+   TupleDesc           tupdesc;
+   Tuplestorestate    *tupstore;
+   MemoryContext       per_query_ctx;
+   MemoryContext       oldcontext;
+
+   /* 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")));
+
+   /* Build a tuple descriptor for our result type */
+   if (get_call_result_type(fcinfo, NULL, &tupdesc) != TYPEFUNC_COMPOSITE)
+       elog(ERROR, "return type must be a row type");
+
+   per_query_ctx = rsinfo->econtext->ecxt_per_query_memory;
+   oldcontext = MemoryContextSwitchTo(per_query_ctx);
+
+   tupstore = tuplestore_begin_heap(true, false, work_mem);
+   rsinfo->returnMode = SFRM_Materialize;
+   rsinfo->setResult = tupstore;
+   rsinfo->setDesc = tupdesc;
+
+   MemoryContextSwitchTo(oldcontext);
+
+   /* 1-based index */
+   for (curr_backend = 1; curr_backend <= num_backends; curr_backend++)
    {
        /* for each row */
-       Datum       values[22];
-       bool        nulls[22];
-       HeapTuple   tuple;
+       Datum       values[PG_STAT_GET_ACTIVITY_COLS];
+       bool        nulls[PG_STAT_GET_ACTIVITY_COLS];
        LocalPgBackendStatus *local_beentry;
        PgBackendStatus *beentry;
 
        MemSet(values, 0, sizeof(values));
        MemSet(nulls, 0, sizeof(nulls));
 
-       if (*(int *) (funcctx->user_fctx) > 0)
-       {
-           /* Get specific pid slot */
-           local_beentry = pgstat_fetch_stat_local_beentry(*(int *) (funcctx->user_fctx));
-           beentry = &local_beentry->backendStatus;
-       }
-       else
+       if (pid != -1)
        {
-           /* Get the next one in the list */
-           local_beentry = pgstat_fetch_stat_local_beentry(funcctx->call_cntr + 1);    /* 1-based index */
-           beentry = &local_beentry->backendStatus;
+           /* Skip any which are not the one we're looking for. */
+           PgBackendStatus *be = pgstat_fetch_stat_beentry(curr_backend);
+
+           if (!be || be->st_procpid != pid)
+               continue;
+
        }
+
+       /* Get the next one in the list */
+       local_beentry = pgstat_fetch_stat_local_beentry(curr_backend);
+       if (!local_beentry)
+           continue;
+
+       beentry = &local_beentry->backendStatus;
        if (!beentry)
        {
            int         i;
@@ -665,8 +603,8 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
            nulls[5] = false;
            values[5] = CStringGetTextDatum("<backend information not available>");
 
-           tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
-           SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
+           tuplestore_putvalues(tupstore, tupdesc, values, nulls);
+           continue;
        }
 
        /* Values available to all callers */
@@ -839,15 +777,17 @@ pg_stat_get_activity(PG_FUNCTION_ARGS)
            nulls[13] = true;
        }
 
-       tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
+       tuplestore_putvalues(tupstore, tupdesc, values, nulls);
 
-       SRF_RETURN_NEXT(funcctx, HeapTupleGetDatum(tuple));
-   }
-   else
-   {
-       /* nothing left */
-       SRF_RETURN_DONE(funcctx);
+       /* If only a single backend was requested, and we found it, break. */
+       if (pid != -1)
+           break;
    }
+
+   /* clean up and return the tuplestore */
+   tuplestore_donestoring(tupstore);
+
+   return (Datum) 0;
 }