88 *
99 *
1010 * IDENTIFICATION
11- * $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.49 2008/03/25 22:42:44 tgl Exp $
11+ * $PostgreSQL: pgsql/src/backend/utils/adt/pgstatfuncs.c,v 1.50 2008/05/07 14:41:55 mha Exp $
1212 *
1313 *-------------------------------------------------------------------------
1414 */
1717#include "funcapi.h"
1818#include "miscadmin.h"
1919#include "pgstat.h"
20+ #include "catalog/pg_type.h"
21+ #include "access/heapam.h"
2022#include "utils/builtins.h"
2123#include "utils/inet.h"
2224#include "libpq/ip.h"
@@ -39,6 +41,7 @@ extern Datum pg_stat_get_last_analyze_time(PG_FUNCTION_ARGS);
3941extern Datum pg_stat_get_last_autoanalyze_time (PG_FUNCTION_ARGS );
4042
4143extern Datum pg_stat_get_backend_idset (PG_FUNCTION_ARGS );
44+ extern Datum pg_stat_get_activity (PG_FUNCTION_ARGS );
4245extern Datum pg_backend_pid (PG_FUNCTION_ARGS );
4346extern Datum pg_stat_get_backend_pid (PG_FUNCTION_ARGS );
4447extern Datum pg_stat_get_backend_dbid (PG_FUNCTION_ARGS );
@@ -363,6 +366,225 @@ pg_stat_get_backend_idset(PG_FUNCTION_ARGS)
363366 }
364367}
365368
369+ Datum
370+ pg_stat_get_activity (PG_FUNCTION_ARGS )
371+ {
372+ FuncCallContext * funcctx ;
373+
374+ if (SRF_IS_FIRSTCALL ())
375+ {
376+ MemoryContext oldcontext ;
377+ TupleDesc tupdesc ;
378+
379+ funcctx = SRF_FIRSTCALL_INIT ();
380+
381+ oldcontext = MemoryContextSwitchTo (funcctx -> multi_call_memory_ctx );
382+
383+ tupdesc = CreateTemplateTupleDesc (10 , false);
384+ TupleDescInitEntry (tupdesc , (AttrNumber ) 1 , "datid" , OIDOID , -1 , 0 );
385+ TupleDescInitEntry (tupdesc , (AttrNumber ) 2 , "procpid" , INT4OID , -1 , 0 );
386+ TupleDescInitEntry (tupdesc , (AttrNumber ) 3 , "usesysid" , OIDOID , -1 , 0 );
387+ TupleDescInitEntry (tupdesc , (AttrNumber ) 4 , "current_query" , TEXTOID , -1 , 0 );
388+ TupleDescInitEntry (tupdesc , (AttrNumber ) 5 , "waiting" , BOOLOID , -1 , 0 );
389+ TupleDescInitEntry (tupdesc , (AttrNumber ) 6 , "act_start" , TIMESTAMPTZOID , -1 , 0 );
390+ TupleDescInitEntry (tupdesc , (AttrNumber ) 7 , "query_start" , TIMESTAMPTZOID , -1 , 0 );
391+ TupleDescInitEntry (tupdesc , (AttrNumber ) 8 , "backend_start" , TIMESTAMPTZOID , -1 , 0 );
392+ TupleDescInitEntry (tupdesc , (AttrNumber ) 9 , "client_addr" , INETOID , -1 , 0 );
393+ TupleDescInitEntry (tupdesc , (AttrNumber ) 10 , "client_port" , INT4OID , -1 , 0 );
394+
395+ funcctx -> tuple_desc = BlessTupleDesc (tupdesc );
396+
397+ funcctx -> user_fctx = palloc0 (sizeof (int ));
398+ if (PG_ARGISNULL (0 ))
399+ {
400+ /* Get all backends */
401+ funcctx -> max_calls = pgstat_fetch_stat_numbackends ();
402+ }
403+ else
404+ {
405+ /*
406+ * Get one backend - locate by pid.
407+ *
408+ * We lookup the backend early, so we can return zero rows if it doesn't
409+ * exist, instead of returning a single row full of NULLs.
410+ */
411+ int pid = PG_GETARG_INT32 (0 );
412+ int i ;
413+ int n = pgstat_fetch_stat_numbackends ();
414+
415+ for (i = 1 ; i <= n ; i ++ )
416+ {
417+ PgBackendStatus * be = pgstat_fetch_stat_beentry (i );
418+ if (be )
419+ {
420+ if (be -> st_procpid == pid )
421+ {
422+ * (int * )(funcctx -> user_fctx ) = i ;
423+ break ;
424+ }
425+ }
426+ }
427+
428+ if (* (int * )(funcctx -> user_fctx ) == 0 )
429+ /* Pid not found, return zero rows */
430+ funcctx -> max_calls = 0 ;
431+ else
432+ funcctx -> max_calls = 1 ;
433+ }
434+
435+ MemoryContextSwitchTo (oldcontext );
436+ }
437+
438+ /* stuff done on every call of the function */
439+ funcctx = SRF_PERCALL_SETUP ();
440+
441+ if (funcctx -> call_cntr < funcctx -> max_calls )
442+ {
443+ /* for each row */
444+ Datum values [10 ];
445+ bool nulls [10 ];
446+ HeapTuple tuple ;
447+ PgBackendStatus * beentry ;
448+ SockAddr zero_clientaddr ;
449+
450+ MemSet (values , 0 , sizeof (values ));
451+ MemSet (nulls , 0 , sizeof (nulls ));
452+
453+ if (* (int * )(funcctx -> user_fctx ) > 0 )
454+ /* Get specific pid slot */
455+ beentry = pgstat_fetch_stat_beentry (* (int * )(funcctx -> user_fctx ));
456+ else
457+ /* Get the next one in the list */
458+ beentry = pgstat_fetch_stat_beentry (funcctx -> call_cntr + 1 ); /* 1-based index */
459+ if (!beentry )
460+ {
461+ int i ;
462+
463+ for (i = 0 ; i < sizeof (nulls )/sizeof (nulls [0 ]); i ++ )
464+ nulls [i ] = true;
465+
466+ nulls [3 ] = false;
467+ values [3 ] = CStringGetTextDatum ("<backend information not available>" );
468+
469+ tuple = heap_form_tuple (funcctx -> tuple_desc , values , nulls );
470+ SRF_RETURN_NEXT (funcctx , HeapTupleGetDatum (tuple ));
471+ }
472+
473+ /* Values available to all callers */
474+ values [0 ] = ObjectIdGetDatum (beentry -> st_databaseid );
475+ values [1 ] = Int32GetDatum (beentry -> st_procpid );
476+ values [2 ] = ObjectIdGetDatum (beentry -> st_userid );
477+
478+ /* Values only available to same user or superuser */
479+ if (superuser () || beentry -> st_userid == GetUserId ())
480+ {
481+ if (* (beentry -> st_activity ) == '\0' )
482+ {
483+ values [3 ] = CStringGetTextDatum ("<command string not enabled>" );
484+ }
485+ else
486+ {
487+ values [3 ] = CStringGetTextDatum (beentry -> st_activity );
488+ }
489+
490+ values [4 ] = BoolGetDatum (beentry -> st_waiting );
491+
492+ if (beentry -> st_xact_start_timestamp != 0 )
493+ values [5 ] = TimestampTzGetDatum (beentry -> st_xact_start_timestamp );
494+ else
495+ nulls [5 ] = true;
496+
497+ if (beentry -> st_activity_start_timestamp != 0 )
498+ values [6 ] = TimestampTzGetDatum (beentry -> st_activity_start_timestamp );
499+ else
500+ nulls [6 ] = true;
501+
502+ if (beentry -> st_proc_start_timestamp != 0 )
503+ values [7 ] = TimestampTzGetDatum (beentry -> st_proc_start_timestamp );
504+ else
505+ nulls [7 ] = true;
506+
507+ /* A zeroed client addr means we don't know */
508+ memset (& zero_clientaddr , 0 , sizeof (zero_clientaddr ));
509+ if (memcmp (& (beentry -> st_clientaddr ), & zero_clientaddr ,
510+ sizeof (zero_clientaddr ) == 0 ))
511+ {
512+ nulls [8 ] = true;
513+ nulls [9 ] = true;
514+ }
515+ else
516+ {
517+ if (beentry -> st_clientaddr .addr .ss_family == AF_INET
518+ #ifdef HAVE_IPV6
519+ || beentry -> st_clientaddr .addr .ss_family == AF_INET6
520+ #endif
521+ )
522+ {
523+ char remote_host [NI_MAXHOST ];
524+ char remote_port [NI_MAXSERV ];
525+ int ret ;
526+
527+ remote_host [0 ] = '\0' ;
528+ remote_port [0 ] = '\0' ;
529+ ret = pg_getnameinfo_all (& beentry -> st_clientaddr .addr ,
530+ beentry -> st_clientaddr .salen ,
531+ remote_host , sizeof (remote_host ),
532+ remote_port , sizeof (remote_port ),
533+ NI_NUMERICHOST | NI_NUMERICSERV );
534+ if (ret )
535+ {
536+ nulls [8 ] = true;
537+ nulls [9 ] = true;
538+ }
539+ else
540+ {
541+ clean_ipv6_addr (beentry -> st_clientaddr .addr .ss_family , remote_host );
542+ values [8 ] = DirectFunctionCall1 (inet_in ,
543+ CStringGetDatum (remote_host ));
544+ values [9 ] = Int32GetDatum (atoi (remote_port ));
545+ }
546+ }
547+ else if (beentry -> st_clientaddr .addr .ss_family == AF_UNIX )
548+ {
549+ /*
550+ * Unix sockets always reports NULL for host and -1 for port, so it's
551+ * possible to tell the difference to connections we have no
552+ * permissions to view, or with errors.
553+ */
554+ nulls [8 ] = true;
555+ values [9 ] = DatumGetInt32 (-1 );
556+ }
557+ else
558+ {
559+ /* Unknown address type, should never happen */
560+ nulls [8 ] = true;
561+ nulls [9 ] = true;
562+ }
563+ }
564+ }
565+ else
566+ {
567+ /* No permissions to view data about this session */
568+ values [3 ] = CStringGetTextDatum ("<insufficient privilege>" );
569+ nulls [4 ] = true;
570+ nulls [5 ] = true;
571+ nulls [6 ] = true;
572+ nulls [7 ] = true;
573+ nulls [8 ] = true;
574+ nulls [9 ] = true;
575+ }
576+
577+ tuple = heap_form_tuple (funcctx -> tuple_desc , values , nulls );
578+
579+ SRF_RETURN_NEXT (funcctx , HeapTupleGetDatum (tuple ));
580+ }
581+ else
582+ {
583+ /* nothing left */
584+ SRF_RETURN_DONE (funcctx );
585+ }
586+ }
587+
366588
367589Datum
368590pg_backend_pid (PG_FUNCTION_ARGS )
0 commit comments