Oid         column_type;
    Oid         typiofunc;
    Oid         typioparam;
+   bool        typisvarlena;
    FmgrInfo    proc;
 } ColumnIOData;
 
    {
        ColumnIOData *column_info = &my_extra->columns[i];
        Oid         column_type = tupdesc->attrs[i]->atttypid;
+       Datum       attr;
        char       *value;
        char       *tmp;
        bool        nq;
         */
        if (column_info->column_type != column_type)
        {
-           bool        typIsVarlena;
-
            getTypeOutputInfo(column_type,
                              &column_info->typiofunc,
-                             &typIsVarlena);
+                             &column_info->typisvarlena);
            fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
                          fcinfo->flinfo->fn_mcxt);
            column_info->column_type = column_type;
        }
 
-       value = OutputFunctionCall(&column_info->proc, values[i]);
+       /*
+        * If we have a toasted datum, forcibly detoast it here to avoid
+        * memory leakage inside the type's output routine.
+        */
+       if (column_info->typisvarlena)
+           attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
+       else
+           attr = values[i];
+
+       value = OutputFunctionCall(&column_info->proc, attr);
 
        /* Detect whether we need double quotes for this value */
        nq = (value[0] == '\0');    /* force quotes for empty string */
 
        /* And emit the string */
        if (nq)
-           appendStringInfoChar(&buf, '"');
+           appendStringInfoCharMacro(&buf, '"');
        for (tmp = value; *tmp; tmp++)
        {
            char        ch = *tmp;
 
            if (ch == '"' || ch == '\\')
-               appendStringInfoChar(&buf, ch);
-           appendStringInfoChar(&buf, ch);
+               appendStringInfoCharMacro(&buf, ch);
+           appendStringInfoCharMacro(&buf, ch);
        }
        if (nq)
-           appendStringInfoChar(&buf, '"');
+           appendStringInfoCharMacro(&buf, '"');
+
+       pfree(value);
+
+       /* Clean up detoasted copy, if any */
+       if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
+           pfree(DatumGetPointer(attr));
    }
 
    appendStringInfoChar(&buf, ')');
    {
        ColumnIOData *column_info = &my_extra->columns[i];
        Oid         column_type = tupdesc->attrs[i]->atttypid;
+       Datum       attr;
        bytea      *outputbytes;
 
        /* Ignore dropped columns in datatype */
         */
        if (column_info->column_type != column_type)
        {
-           bool        typIsVarlena;
-
            getTypeBinaryOutputInfo(column_type,
                                    &column_info->typiofunc,
-                                   &typIsVarlena);
+                                   &column_info->typisvarlena);
            fmgr_info_cxt(column_info->typiofunc, &column_info->proc,
                          fcinfo->flinfo->fn_mcxt);
            column_info->column_type = column_type;
        }
 
-       outputbytes = SendFunctionCall(&column_info->proc, values[i]);
+       /*
+        * If we have a toasted datum, forcibly detoast it here to avoid
+        * memory leakage inside the type's output routine.
+        */
+       if (column_info->typisvarlena)
+           attr = PointerGetDatum(PG_DETOAST_DATUM(values[i]));
+       else
+           attr = values[i];
+
+       outputbytes = SendFunctionCall(&column_info->proc, attr);
 
        /* We assume the result will not have been toasted */
        pq_sendint(&buf, VARSIZE(outputbytes) - VARHDRSZ, 4);
        pq_sendbytes(&buf, VARDATA(outputbytes),
                     VARSIZE(outputbytes) - VARHDRSZ);
+
        pfree(outputbytes);
+
+       /* Clean up detoasted copy, if any */
+       if (DatumGetPointer(attr) != DatumGetPointer(values[i]))
+           pfree(DatumGetPointer(attr));
    }
 
    pfree(values);