Skip to content

Commit 2970afa

Browse files
committed
Add PQresultMemorySize function to report allocated size of a PGresult.
This number can be useful for application memory management, and the overhead to track it seems pretty trivial. Lars Kanis, reviewed by Pavel Stehule, some mods by me Discussion: https://postgr.es/m/fa16a288-9685-14f2-97c8-b8ac84365a4f@greiz-reinsdorf.de
1 parent e7a2217 commit 2970afa

File tree

5 files changed

+70
-6
lines changed

5 files changed

+70
-6
lines changed

doc/src/sgml/libpq.sgml

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6387,6 +6387,32 @@ void *PQresultAlloc(PGresult *res, size_t nBytes);
63876387
</listitem>
63886388
</varlistentry>
63896389

6390+
<varlistentry id="libpq-pqresultmemorysize">
6391+
<term>
6392+
<function>PQresultMemorySize</function>
6393+
<indexterm>
6394+
<primary>PQresultMemorySize</primary>
6395+
</indexterm>
6396+
</term>
6397+
6398+
<listitem>
6399+
<para>
6400+
Retrieves the number of bytes allocated for
6401+
a <structname>PGresult</structname> object.
6402+
<synopsis>
6403+
size_t PQresultMemorySize(const PGresult *res);
6404+
</synopsis>
6405+
</para>
6406+
6407+
<para>
6408+
This value is the sum of all <function>malloc</function> requests
6409+
associated with the <structname>PGresult</structname> object, that is,
6410+
all the space that will be freed by <function>PQclear</function>.
6411+
This information can be useful for managing memory consumption.
6412+
</para>
6413+
</listitem>
6414+
</varlistentry>
6415+
63906416
<varlistentry id="libpq-pqlibversion">
63916417
<term>
63926418
<function>PQlibVersion</function>
@@ -6960,6 +6986,14 @@ void *PQinstanceData(const PGconn *conn, PGEventProc proc);
69606986
int PQresultSetInstanceData(PGresult *res, PGEventProc proc, void *data);
69616987
</synopsis>
69626988
</para>
6989+
6990+
<para>
6991+
Beware that any storage represented by <parameter>data</parameter>
6992+
will not be accounted for by <function>PQresultMemorySize</function>,
6993+
unless it is allocated using <function>PQresultAlloc</function>.
6994+
(Doing so is recommendable because it eliminates the need to free
6995+
such storage explicitly when the result is destroyed.)
6996+
</para>
69636997
</listitem>
69646998
</varlistentry>
69656999

src/interfaces/libpq/exports.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,4 @@ PQsslAttribute 169
172172
PQsetErrorContextVisibility 170
173173
PQresultVerboseErrorMessage 171
174174
PQencryptPasswordConn 172
175+
PQresultMemorySize 173

src/interfaces/libpq/fe-exec.c

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ static int static_client_encoding = PG_SQL_ASCII;
5151
static bool static_std_strings = false;
5252

5353

54-
static PGEvent *dupEvents(PGEvent *events, int count);
54+
static PGEvent *dupEvents(PGEvent *events, int count, size_t *memSize);
5555
static bool pqAddTuple(PGresult *res, PGresAttValue *tup,
5656
const char **errmsgp);
5757
static bool PQsendQueryStart(PGconn *conn);
@@ -166,6 +166,7 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
166166
result->curBlock = NULL;
167167
result->curOffset = 0;
168168
result->spaceLeft = 0;
169+
result->memorySize = sizeof(PGresult);
169170

170171
if (conn)
171172
{
@@ -193,7 +194,8 @@ PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status)
193194
/* copy events last; result must be valid if we need to PQclear */
194195
if (conn->nEvents > 0)
195196
{
196-
result->events = dupEvents(conn->events, conn->nEvents);
197+
result->events = dupEvents(conn->events, conn->nEvents,
198+
&result->memorySize);
197199
if (!result->events)
198200
{
199201
PQclear(result);
@@ -344,7 +346,8 @@ PQcopyResult(const PGresult *src, int flags)
344346
/* Wants to copy PGEvents? */
345347
if ((flags & PG_COPYRES_EVENTS) && src->nEvents > 0)
346348
{
347-
dest->events = dupEvents(src->events, src->nEvents);
349+
dest->events = dupEvents(src->events, src->nEvents,
350+
&dest->memorySize);
348351
if (!dest->events)
349352
{
350353
PQclear(dest);
@@ -379,17 +382,20 @@ PQcopyResult(const PGresult *src, int flags)
379382
* Copy an array of PGEvents (with no extra space for more).
380383
* Does not duplicate the event instance data, sets this to NULL.
381384
* Also, the resultInitialized flags are all cleared.
385+
* The total space allocated is added to *memSize.
382386
*/
383387
static PGEvent *
384-
dupEvents(PGEvent *events, int count)
388+
dupEvents(PGEvent *events, int count, size_t *memSize)
385389
{
386390
PGEvent *newEvents;
391+
size_t msize;
387392
int i;
388393

389394
if (!events || count <= 0)
390395
return NULL;
391396

392-
newEvents = (PGEvent *) malloc(count * sizeof(PGEvent));
397+
msize = count * sizeof(PGEvent);
398+
newEvents = (PGEvent *) malloc(msize);
393399
if (!newEvents)
394400
return NULL;
395401

@@ -407,8 +413,10 @@ dupEvents(PGEvent *events, int count)
407413
free(newEvents);
408414
return NULL;
409415
}
416+
msize += strlen(events[i].name) + 1;
410417
}
411418

419+
*memSize += msize;
412420
return newEvents;
413421
}
414422

@@ -567,9 +575,12 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
567575
*/
568576
if (nBytes >= PGRESULT_SEP_ALLOC_THRESHOLD)
569577
{
570-
block = (PGresult_data *) malloc(nBytes + PGRESULT_BLOCK_OVERHEAD);
578+
size_t alloc_size = nBytes + PGRESULT_BLOCK_OVERHEAD;
579+
580+
block = (PGresult_data *) malloc(alloc_size);
571581
if (!block)
572582
return NULL;
583+
res->memorySize += alloc_size;
573584
space = block->space + PGRESULT_BLOCK_OVERHEAD;
574585
if (res->curBlock)
575586
{
@@ -594,6 +605,7 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
594605
block = (PGresult_data *) malloc(PGRESULT_DATA_BLOCKSIZE);
595606
if (!block)
596607
return NULL;
608+
res->memorySize += PGRESULT_DATA_BLOCKSIZE;
597609
block->next = res->curBlock;
598610
res->curBlock = block;
599611
if (isBinary)
@@ -615,6 +627,18 @@ pqResultAlloc(PGresult *res, size_t nBytes, bool isBinary)
615627
return space;
616628
}
617629

630+
/*
631+
* PQresultMemorySize -
632+
* Returns total space allocated for the PGresult.
633+
*/
634+
size_t
635+
PQresultMemorySize(const PGresult *res)
636+
{
637+
if (!res)
638+
return 0;
639+
return res->memorySize;
640+
}
641+
618642
/*
619643
* pqResultStrdup -
620644
* Like strdup, but the space is subsidiary PGresult space.
@@ -927,6 +951,8 @@ pqAddTuple(PGresult *res, PGresAttValue *tup, const char **errmsgp)
927951
realloc(res->tuples, newSize * sizeof(PGresAttValue *));
928952
if (!newTuples)
929953
return false; /* malloc or realloc failed */
954+
res->memorySize +=
955+
(newSize - res->tupArrSize) * sizeof(PGresAttValue *);
930956
res->tupArrSize = newSize;
931957
res->tuples = newTuples;
932958
}

src/interfaces/libpq/libpq-fe.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -516,6 +516,7 @@ extern PGresult *PQmakeEmptyPGresult(PGconn *conn, ExecStatusType status);
516516
extern PGresult *PQcopyResult(const PGresult *src, int flags);
517517
extern int PQsetResultAttrs(PGresult *res, int numAttributes, PGresAttDesc *attDescs);
518518
extern void *PQresultAlloc(PGresult *res, size_t nBytes);
519+
extern size_t PQresultMemorySize(const PGresult *res);
519520
extern int PQsetvalue(PGresult *res, int tup_num, int field_num, char *value, int len);
520521

521522
/* Quoting strings before inclusion in queries. */

src/interfaces/libpq/libpq-int.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,8 @@ struct pg_result
208208
PGresult_data *curBlock; /* most recently allocated block */
209209
int curOffset; /* start offset of free space in block */
210210
int spaceLeft; /* number of free bytes remaining in block */
211+
212+
size_t memorySize; /* total space allocated for this PGresult */
211213
};
212214

213215
/* PGAsyncStatusType defines the state of the query-execution state machine */

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy