On 2014-05-08 07:58:34 -0400, Robert Haas wrote: > On Wed, May 7, 2014 at 5:54 PM, Andres Freund <and...@2ndquadrant.com> wrote: > > Hm. Not sure what you're ACKing here ;). > > The idea of giving the unallocated memory a NULL key.
Ok. A new version of the patches implementing that are attached. Including a couple of small fixups and docs. The latter aren't extensive, but that doesn't seem to be warranted anyway. > > There's lots of allocations from shmem that cannot be associated with > > any index entry though. Not just ShmemIndex's own entry. Most > > prominently most of the memory used for SharedBufHash isn't actually > > associated with the "Shared Buffer Lookup Table" entry - imo a > > dynahash.c defficiency. > Hmm, I don't know what to do about that. Well, we have to live with it for now :) Greetings, Andres Freund -- Andres Freund http://www.2ndQuadrant.com/ PostgreSQL Development, 24x7 Support, Training & Services
>From c219c03a173fef962c1caba9f016d5d87448fd8f Mon Sep 17 00:00:00 2001 From: Andres Freund <and...@anarazel.de> Date: Tue, 6 May 2014 19:42:36 +0200 Subject: [PATCH 1/4] Associate names to created dynamic shared memory segments. At some later point we want to add a view show all allocated dynamic shared memory segments so admins can understand resource usage. To avoid breaking the API in 9.5 add the necessary name now. --- contrib/test_shm_mq/setup.c | 2 +- src/backend/storage/ipc/dsm.c | 60 ++++++++++++++++++++++++++----------------- src/include/storage/dsm.h | 2 +- 3 files changed, 39 insertions(+), 25 deletions(-) diff --git a/contrib/test_shm_mq/setup.c b/contrib/test_shm_mq/setup.c index 572cf88..897c47b 100644 --- a/contrib/test_shm_mq/setup.c +++ b/contrib/test_shm_mq/setup.c @@ -125,7 +125,7 @@ setup_dynamic_shared_memory(int64 queue_size, int nworkers, segsize = shm_toc_estimate(&e); /* Create the shared memory segment and establish a table of contents. */ - seg = dsm_create(shm_toc_estimate(&e)); + seg = dsm_create("test_shm_mq", shm_toc_estimate(&e)); toc = shm_toc_create(PG_TEST_SHM_MQ_MAGIC, dsm_segment_address(seg), segsize); diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c index a5c0084..c8fdf6e 100644 --- a/src/backend/storage/ipc/dsm.c +++ b/src/backend/storage/ipc/dsm.c @@ -80,8 +80,10 @@ struct dsm_segment /* Shared-memory state for a dynamic shared memory segment. */ typedef struct dsm_control_item { - dsm_handle handle; + dsm_handle handle; /* segment identifier */ uint32 refcnt; /* 2+ = active, 1 = moribund, 0 = gone */ + Size size; /* current size */ + char name[SHMEM_INDEX_KEYSIZE]; /* informational name */ } dsm_control_item; /* Layout of the dynamic shared memory control segment. */ @@ -454,14 +456,16 @@ dsm_set_control_handle(dsm_handle h) * Create a new dynamic shared memory segment. */ dsm_segment * -dsm_create(Size size) +dsm_create(const char *name, Size size) { dsm_segment *seg = dsm_create_descriptor(); - uint32 i; - uint32 nitems; + dsm_control_item *item; + uint32 slot; /* Unsafe in postmaster (and pointless in a stand-alone backend). */ Assert(IsUnderPostmaster); + Assert(name != NULL && strlen(name) > 0 && + strlen(name) < SHMEM_INDEX_KEYSIZE - 1); if (!dsm_init_done) dsm_backend_startup(); @@ -479,33 +483,39 @@ dsm_create(Size size) /* Lock the control segment so we can register the new segment. */ LWLockAcquire(DynamicSharedMemoryControlLock, LW_EXCLUSIVE); - /* Search the control segment for an unused slot. */ - nitems = dsm_control->nitems; - for (i = 0; i < nitems; ++i) + /* + * Search the control segment for an unused slot that's previously been + * used. If we don't find one initialize a new one if there's still space. + */ + for (slot = 0; slot < dsm_control->nitems; ++slot) { - if (dsm_control->item[i].refcnt == 0) - { - dsm_control->item[i].handle = seg->handle; - /* refcnt of 1 triggers destruction, so start at 2 */ - dsm_control->item[i].refcnt = 2; - seg->control_slot = i; - LWLockRelease(DynamicSharedMemoryControlLock); - return seg; - } + if (dsm_control->item[slot].refcnt == 0) + break; } - /* Verify that we can support an additional mapping. */ - if (nitems >= dsm_control->maxitems) + /* Verify that we can support the mapping. */ + if (slot >= dsm_control->maxitems) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_RESOURCES), errmsg("too many dynamic shared memory segments"))); - /* Enter the handle into a new array slot. */ - dsm_control->item[nitems].handle = seg->handle; + item = &dsm_control->item[slot]; + item->handle = seg->handle; /* refcnt of 1 triggers destruction, so start at 2 */ - dsm_control->item[nitems].refcnt = 2; - seg->control_slot = nitems; - dsm_control->nitems++; + item->refcnt = 2; + item->size = size; + strncpy(item->name, name, SHMEM_INDEX_KEYSIZE); + item->name[SHMEM_INDEX_KEYSIZE - 1] = 0; + + seg->control_slot = slot; + + /* + * Increase number of initilized slots if we didn't reuse a previously + * used one. + */ + if (slot >= dsm_control->nitems) + dsm_control->nitems++; + LWLockRelease(DynamicSharedMemoryControlLock); return seg; @@ -658,6 +668,10 @@ dsm_resize(dsm_segment *seg, Size size) Assert(seg->control_slot != INVALID_CONTROL_SLOT); dsm_impl_op(DSM_OP_RESIZE, seg->handle, size, &seg->impl_private, &seg->mapped_address, &seg->mapped_size, ERROR); + + /* persist the changed size */ + dsm_control->item[seg->control_slot].size = size; + return seg->mapped_address; } diff --git a/src/include/storage/dsm.h b/src/include/storage/dsm.h index 1d0110d..3dbe53b 100644 --- a/src/include/storage/dsm.h +++ b/src/include/storage/dsm.h @@ -29,7 +29,7 @@ extern void dsm_set_control_handle(dsm_handle h); #endif /* Functions that create, update, or remove mappings. */ -extern dsm_segment *dsm_create(Size size); +extern dsm_segment *dsm_create(const char *name, Size size); extern dsm_segment *dsm_attach(dsm_handle h); extern void *dsm_resize(dsm_segment *seg, Size size); extern void *dsm_remap(dsm_segment *seg); -- 1.8.3.251.g1462b67
>From b5d301c847062cbede97a85d8f569ade9a896396 Mon Sep 17 00:00:00 2001 From: Andres Freund <and...@anarazel.de> Date: Sun, 4 May 2014 13:37:20 +0200 Subject: [PATCH 2/4] Add views to see shared memory allocations. Add the pg_shmem_allocations and pg_dynamic_shmem_allocations views. These are useful to see what memory is being used for. --- doc/src/sgml/catalogs.sgml | 132 +++++++++++++++++++++++++++++++++++ doc/src/sgml/xfunc.sgml | 2 +- src/backend/catalog/system_views.sql | 6 ++ src/backend/storage/ipc/dsm.c | 80 +++++++++++++++++++++ src/backend/storage/ipc/shmem.c | 113 ++++++++++++++++++++++++++++++ src/include/catalog/pg_proc.h | 4 ++ src/include/utils/builtins.h | 5 ++ src/test/regress/expected/rules.out | 9 +++ 8 files changed, 350 insertions(+), 1 deletion(-) diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index b4a06e4..f106dd6 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -7549,6 +7549,70 @@ </sect1> + <sect1 id="view-pg-dynamic-shmem-allocations"> + <title><structname>pg_dynamic_shmem_allocations</structname></title> + + <indexterm zone="view-pg-dynamic-shmem-allocations"> + <primary>pg_dynamic_shmem_allocations</primary> + </indexterm> + + <para> + The <structname>pg_dynamic_shmem_allocations</structname> view shows + information about the currently existing dynamic shared memory segments.. + </para> + + <para> + Note that this view does not include shared memory allocated at server + startup. That is shown in + the <link linkend="view-pg-shmem-allocations"><structname>pg_shmem_allocations</structname></link> + view. + </para> + + <table> + <title><structname>pg_dynamic_shmem_allocations</structname> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>handle</structfield></entry> + <entry><type>int64</type></entry> + <entry>The identifier used to refer to this specific segment.</entry> + </row> + + <row> + <entry><structfield>name</structfield></entry> + <entry><type>text</type></entry> + <entry>Informational name for this segment.</entry> + </row> + + <row> + <entry><structfield>size</structfield></entry> + <entry><type>bigint</type></entry> + <entry>Size of the segment.</entry> + </row> + + <row> + <entry><structfield>refcnt</structfield></entry> + <entry><type>bigint</type></entry> + <entry>Number of backends attached to this segment.</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + The <structname>pg_shmem_allocations</structname> view is read only. + </para> + </sect1> + <sect1 id="view-pg-group"> <title><structname>pg_group</structname></title> @@ -8873,6 +8937,74 @@ SELECT * FROM pg_locks pl LEFT JOIN pg_prepared_xacts ppx </sect1> + <sect1 id="view-pg-shmem-allocations"> + <title><structname>pg_shmem_allocations</structname></title> + + <indexterm zone="view-pg-shmem-allocations"> + <primary>pg_shmem_allocations</primary> + </indexterm> + + <para> + The <structname>pg_shmem_allocations</structname> view shows what the + server's shared memory is being used for. That includes memory allocated by + <productname>postgres</> itself and memory used by extensions using the + mechanisms detailed in <xref linkend="xfunc-shared-addin">. + </para> + + <para> + Note that this view does not include memory allocated using the dynamic + shared memory infrastructure. That is shown in + the <link linkend="view-pg-dynamic-shmem-allocations"><structname>pg_dynamic_shmem_allocations</structname></link> + view. + </para> + + <table> + <title><structname>pg_shmem_allocations</structname> Columns</title> + + <tgroup cols="3"> + <thead> + <row> + <entry>Name</entry> + <entry>Type</entry> + <entry>Description</entry> + </row> + </thead> + + <tbody> + <row> + <entry><structfield>name</structfield></entry> + <entry><type>text</type></entry> + <entry>The name of the shared memory allocation. NULL for unused memory + and <anonymous> for anonymous allocations.</entry> + </row> + + <row> + <entry><structfield>off</structfield></entry> + <entry><type>bigint</type></entry> + <entry>The offset at which the allocation starts. NULL for anonymous + allocations.</entry> + </row> + + <row> + <entry><structfield>size</structfield></entry> + <entry><type>bigint</type></entry> + <entry>Size of the allocation</entry> + </row> + </tbody> + </tgroup> + </table> + + <para> + Anonymous allocations are allocations that have been made + with <literal>ShmemAlloc()</> and not <literal>ShmemInitStruct()</literal>. + </para> + + <para> + The <structname>pg_shmem_allocations</structname> view is read only. + </para> + + </sect1> + <sect1 id="view-pg-stats"> <title><structname>pg_stats</structname></title> diff --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 941b101..adf0d9d 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -3275,7 +3275,7 @@ CREATE FUNCTION make_array(anyelement) RETURNS anyarray </para> </sect2> - <sect2> + <sect2 id="xfunc-shared-addin"> <title>Shared Memory and LWLocks</title> <para> diff --git a/src/backend/catalog/system_views.sql b/src/backend/catalog/system_views.sql index 42a4c00..4041ec3 100644 --- a/src/backend/catalog/system_views.sql +++ b/src/backend/catalog/system_views.sql @@ -387,6 +387,12 @@ CREATE VIEW pg_timezone_abbrevs AS CREATE VIEW pg_timezone_names AS SELECT * FROM pg_timezone_names(); +CREATE VIEW pg_shmem_allocations AS + SELECT * FROM pg_get_shmem_allocations(); + +CREATE VIEW pg_dynamic_shmem_allocations AS + SELECT * FROM pg_get_dynamic_shmem_allocations(); + -- Statistics views CREATE VIEW pg_stat_all_tables AS diff --git a/src/backend/storage/ipc/dsm.c b/src/backend/storage/ipc/dsm.c index c8fdf6e..a564645 100644 --- a/src/backend/storage/ipc/dsm.c +++ b/src/backend/storage/ipc/dsm.c @@ -34,12 +34,15 @@ #endif #include <sys/stat.h> +#include "fmgr.h" +#include "funcapi.h" #include "lib/ilist.h" #include "miscadmin.h" #include "storage/dsm.h" #include "storage/ipc.h" #include "storage/lwlock.h" #include "storage/pg_shmem.h" +#include "utils/builtins.h" #include "utils/guc.h" #include "utils/memutils.h" #include "utils/resowner_private.h" @@ -1022,3 +1025,80 @@ dsm_control_bytes_needed(uint32 nitems) return offsetof(dsm_control_header, item) +sizeof(dsm_control_item) * (uint64) nitems; } + +/* SQL SRF showing allocated shared memory */ +Datum +pg_get_dynamic_shmem_allocations(PG_FUNCTION_ARGS) +{ +#define PG_GET_SHMEM_SIZES_COLS 4 + + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + int i; + + /* 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); + + LWLockAcquire(DynamicSharedMemoryControlLock, LW_SHARED); + + for (i = 0; i < dsm_control->nitems; ++i) + { + dsm_control_item *item; + Datum values[PG_GET_SHMEM_SIZES_COLS]; + bool nulls[PG_GET_SHMEM_SIZES_COLS]; + + /* don't look at unused or about to be destroyed items */ + if (dsm_control->item[i].refcnt < 2) + continue; + + item = &dsm_control->item[i]; + + /* handle */ + values[0] = Int64GetDatum(item->handle); + nulls[0] = false; + + /* name */ + values[1] = CStringGetTextDatum(item->name); + nulls[1] = false; + + /* size */ + values[2] = Int64GetDatum(item->size); + nulls[2] = false; + + /* refcnt */ + values[3] = Int64GetDatum(item->refcnt - 2); + nulls[3] = false; + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + LWLockRelease(DynamicSharedMemoryControlLock); + + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} diff --git a/src/backend/storage/ipc/shmem.c b/src/backend/storage/ipc/shmem.c index 2ea2216..9c47959 100644 --- a/src/backend/storage/ipc/shmem.c +++ b/src/backend/storage/ipc/shmem.c @@ -66,11 +66,14 @@ #include "postgres.h" #include "access/transam.h" +#include "fmgr.h" +#include "funcapi.h" #include "miscadmin.h" #include "storage/lwlock.h" #include "storage/pg_shmem.h" #include "storage/shmem.h" #include "storage/spin.h" +#include "utils/builtins.h" /* shared memory global variables */ @@ -459,3 +462,113 @@ mul_size(Size s1, Size s2) errmsg("requested shared memory size overflows size_t"))); return result; } + +/* SQL SRF showing allocated shared memory */ +Datum +pg_get_shmem_allocations(PG_FUNCTION_ARGS) +{ +#define PG_GET_SHMEM_SIZES_COLS 3 + ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo; + TupleDesc tupdesc; + Tuplestorestate *tupstore; + MemoryContext per_query_ctx; + MemoryContext oldcontext; + HASH_SEQ_STATUS hstat; + ShmemIndexEnt *ent; + Size named_allocated = 0; + + /* 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); + + LWLockAcquire(ShmemIndexLock, LW_SHARED); + + hash_seq_init(&hstat, ShmemIndex); + + /* output all allocated entries */ + while ((ent = (ShmemIndexEnt *) hash_seq_search(&hstat)) != NULL) + { + Datum values[PG_GET_SHMEM_SIZES_COLS]; + bool nulls[PG_GET_SHMEM_SIZES_COLS]; + + /* key */ + values[0] = CStringGetTextDatum(ent->key); + nulls[0] = false; + + /* off */ + values[1] = Int64GetDatum((char *) ent->location - (char *) ShmemSegHdr); + nulls[1] = false; + + /* size */ + values[2] = Int64GetDatum(ent->size); + nulls[2] = false; + named_allocated += ent->size; + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* output shared memory allocated but not counted via the shmem index */ + { + Datum values[PG_GET_SHMEM_SIZES_COLS]; + bool nulls[PG_GET_SHMEM_SIZES_COLS]; + + /* key */ + values[0] = CStringGetTextDatum("<anonymous>"); + nulls[0] = false; + + /* off */ + nulls[1] = true; + + /* size */ + values[2] = Int64GetDatum(ShmemSegHdr->freeoffset - named_allocated); + nulls[2] = false; + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + /* output as-of-yet unused shared memory */ + { + Datum values[PG_GET_SHMEM_SIZES_COLS]; + bool nulls[PG_GET_SHMEM_SIZES_COLS]; + + /* key, show unallocated as NULL */ + nulls[0] = true; + + /* off */ + values[1] = Int64GetDatum(ShmemSegHdr->freeoffset); + nulls[1] = false; + + /* size */ + values[2] = Int64GetDatum(ShmemSegHdr->totalsize - ShmemSegHdr->freeoffset); + nulls[2] = false; + + tuplestore_putvalues(tupstore, tupdesc, values, nulls); + } + + LWLockRelease(ShmemIndexLock); + + tuplestore_donestoring(tupstore); + + return (Datum) 0; +} diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index e601ccd..16502b0 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -3899,6 +3899,10 @@ DATA(insert OID = 3035 ( pg_listening_channels PGNSP PGUID 12 1 10 0 0 f f f f DESCR("get the channels that the current backend listens to"); DATA(insert OID = 3036 ( pg_notify PGNSP PGUID 12 1 0 0 0 f f f f f f v 2 0 2278 "25 25" _null_ _null_ _null_ _null_ pg_notify _null_ _null_ _null_ )); DESCR("send a notification event"); +DATA(insert OID = 86 ( pg_get_shmem_allocations PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{25,20,20}" "{o,o,o}" "{key, off, size}" _null_ pg_get_shmem_allocations _null_ _null_ _null_ )); +DESCR("show shared memory allocations"); +DATA(insert OID = 87 ( pg_get_dynamic_shmem_allocations PGNSP PGUID 12 1 10 0 0 f f f f f t s 0 0 2249 "" "{20,25,20,20}" "{o,o,o,o}" "{handle, name, size, refcnt}" _null_ pg_get_dynamic_shmem_allocations _null_ _null_ _null_ )); +DESCR("show dynamic shared memory allocations"); /* non-persistent series generator */ DATA(insert OID = 1066 ( generate_series PGNSP PGUID 12 1 1000 0 0 f f f f t t i 3 0 23 "23 23 23" _null_ _null_ _null_ _null_ generate_series_step_int4 _null_ _null_ _null_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index bbb5d39..b1e37cf 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -1209,4 +1209,9 @@ extern Datum pg_prepared_statement(PG_FUNCTION_ARGS); /* utils/mmgr/portalmem.c */ extern Datum pg_cursor(PG_FUNCTION_ARGS); +/* backend/storage/ipc/shmem.c */ +extern Datum pg_get_shmem_allocations(PG_FUNCTION_ARGS); +/* backend/storage/ipc/dsm.c */ +extern Datum pg_get_dynamic_shmem_allocations(PG_FUNCTION_ARGS); + #endif /* BUILTINS_H */ diff --git a/src/test/regress/expected/rules.out b/src/test/regress/expected/rules.out index 87870cf..97aec3f 100644 --- a/src/test/regress/expected/rules.out +++ b/src/test/regress/expected/rules.out @@ -1308,6 +1308,11 @@ pg_cursors| SELECT c.name, c.is_scrollable, c.creation_time FROM pg_cursor() c(name, statement, is_holdable, is_binary, is_scrollable, creation_time); +pg_dynamic_shmem_allocations| SELECT pg_get_dynamic_shmem_allocations.handle, + pg_get_dynamic_shmem_allocations.name, + pg_get_dynamic_shmem_allocations.size, + pg_get_dynamic_shmem_allocations.refcnt + FROM pg_get_dynamic_shmem_allocations() pg_get_dynamic_shmem_allocations(handle, name, size, refcnt); pg_group| SELECT pg_authid.rolname AS groname, pg_authid.oid AS grosysid, ARRAY( SELECT pg_auth_members.member @@ -1591,6 +1596,10 @@ pg_shadow| SELECT pg_authid.rolname AS usename, FROM (pg_authid LEFT JOIN pg_db_role_setting s ON (((pg_authid.oid = s.setrole) AND (s.setdatabase = (0)::oid)))) WHERE pg_authid.rolcanlogin; +pg_shmem_allocations| SELECT pg_get_shmem_allocations.key, + pg_get_shmem_allocations.off, + pg_get_shmem_allocations.size + FROM pg_get_shmem_allocations() pg_get_shmem_allocations(key, off, size); pg_stat_activity| SELECT s.datid, d.datname, s.pid, -- 1.8.3.251.g1462b67
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers