]> granicus.if.org Git - postgresql/commitdiff
Fix memory leak in printtup.c.
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Mar 2019 21:54:24 +0000 (17:54 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 18 Mar 2019 21:54:41 +0000 (17:54 -0400)
Commit f2dec34e1 changed things so that printtup's output stringinfo
buffer was allocated outside the per-row temporary context, not inside
it.  This creates a need to free that buffer explicitly when the temp
context is freed, but that was overlooked.  In most cases, this is all
happening inside a portal or executor context that will go away shortly
anyhow, but that's not always true.  Notably, the stringinfo ends up
getting leaked when JDBC uses row-at-a-time fetches.  For a query
that returns wide rows, that adds up after awhile.

Per bug #15700 from Matthias Otterbach.  Back-patch to v11 where the
faulty code was added.

Discussion: https://postgr.es/m/15700-8c408321a87d56bb@postgresql.org

src/backend/access/common/printtup.c

index e4ee5c9d3450cf3afe24a8c71812f24ec6f5a4fb..44add482d3ec1ec6b9ab924217f1146db30b7426 100644 (file)
@@ -61,12 +61,12 @@ typedef struct
 typedef struct
 {
        DestReceiver pub;                       /* publicly-known function pointers */
-       StringInfoData buf;                     /* output buffer */
        Portal          portal;                 /* the Portal we are printing from */
        bool            sendDescrip;    /* send RowDescription at startup? */
        TupleDesc       attrinfo;               /* The attr info we are set up for */
        int                     nattrs;
        PrinttupAttrInfo *myinfo;       /* Cached info about each attr */
+       StringInfoData buf;                     /* output buffer (*not* in tmpcontext) */
        MemoryContext tmpcontext;       /* Memory context for per-row workspace */
 } DR_printtup;
 
@@ -94,6 +94,7 @@ printtup_create_DR(CommandDest dest)
        self->attrinfo = NULL;
        self->nattrs = 0;
        self->myinfo = NULL;
+       self->buf.data = NULL;
        self->tmpcontext = NULL;
 
        return (DestReceiver *) self;
@@ -132,7 +133,10 @@ printtup_startup(DestReceiver *self, int operation, TupleDesc typeinfo)
        DR_printtup *myState = (DR_printtup *) self;
        Portal          portal = myState->portal;
 
-       /* create buffer to be used for all messages */
+       /*
+        * Create I/O buffer to be used for all messages.  This cannot be inside
+        * tmpcontext, since we want to re-use it across rows.
+        */
        initStringInfo(&myState->buf);
 
        /*
@@ -544,6 +548,10 @@ printtup_shutdown(DestReceiver *self)
 
        myState->attrinfo = NULL;
 
+       if (myState->buf.data)
+               pfree(myState->buf.data);
+       myState->buf.data = NULL;
+
        if (myState->tmpcontext)
                MemoryContextDelete(myState->tmpcontext);
        myState->tmpcontext = NULL;