"Nigel J. Andrews" <[EMAIL PROTECTED]> writes:
> Yes, I do get the similar results.
>
> A quick investigation shows that the SPI_freetuptable at the end of
> pltcl_SPI_exec is trying to free a tuptable of value 0x82ebe64
> (which looks sensible to me) but which has a memory context of
> 0x7f7f7f7f (the unallocated marker).
Attached is a patch against CVS HEAD which fixes this, I believe. The
problem appears to be the newly added free of the tuptable at the end
of pltcl_SPI_exec(). I've added a comment to that effect:
/*
* Do *NOT* free the tuptable here. That's because if the loop
* body executed any SQL statements, it will have already free'd
* the tuptable itself, so freeing it twice is not wise. We could
* get around this by making a copy of SPI_tuptable->vals and
* feeding that to pltcl_set_tuple_values above, but that would
* still leak memory (the palloc'ed copy would only be free'd on
* context reset).
*/
At least, I *think* that's the problem -- I've only been looking at
the code for about 20 minutes, so I may be wrong. In any case, this
makes both memleak() and memleak(1) work on my machine. Let me know if
it works for you, and/or if someone knows of a better solution.
I also added some SPI_freetuptable() calls in some places where Nigel
didn't, and added some paranoia when dealing with statically sized
buffers (snprintf() rather than sprintf(), and so on). I also didn't
include Nigel's changes to some apparently unrelated PL/Python stuff
-- this patch includes only the PL/Tcl changes.
Cheers,
Neil
--
Neil Conway <[EMAIL PROTECTED]> || PGP Key ID: DB3C29FC
Index: src/pl/tcl/pltcl.c
===================================================================
RCS file: /var/lib/cvs/pgsql-server/src/pl/tcl/pltcl.c,v
retrieving revision 1.62
diff -c -r1.62 pltcl.c
*** src/pl/tcl/pltcl.c 21 Sep 2002 18:39:26 -0000 1.62
--- src/pl/tcl/pltcl.c 25 Sep 2002 04:54:57 -0000
***************
*** 39,50 ****
#include <tcl.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <stdarg.h>
#include <unistd.h>
#include <fcntl.h>
- #include <string.h>
#include <setjmp.h>
#include "access/heapam.h"
--- 39,46 ----
***************
*** 308,313 ****
--- 304,310 ----
************************************************************/
spi_rc = SPI_exec("select 1 from pg_class "
"where relname = 'pltcl_modules'", 1);
+ SPI_freetuptable(SPI_tuptable);
if (spi_rc != SPI_OK_SELECT)
elog(ERROR, "pltcl_init_load_unknown(): select from pg_class failed");
if (SPI_processed == 0)
***************
*** 334,339 ****
--- 331,337 ----
if (SPI_processed == 0)
{
Tcl_DStringFree(&unknown_src);
+ SPI_freetuptable(SPI_tuptable);
elog(WARNING, "pltcl: Module unknown not found in pltcl_modules");
return;
}
***************
*** 359,364 ****
--- 357,363 ----
}
tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src));
Tcl_DStringFree(&unknown_src);
+ SPI_freetuptable(SPI_tuptable);
}
***************
*** 955,963 ****
* Build our internal proc name from the functions Oid
************************************************************/
if (!is_trigger)
! sprintf(internal_proname, "__PLTcl_proc_%u", fn_oid);
else
! sprintf(internal_proname, "__PLTcl_proc_%u_trigger", fn_oid);
/************************************************************
* Lookup the internal proc name in the hashtable
--- 954,964 ----
* Build our internal proc name from the functions Oid
************************************************************/
if (!is_trigger)
! snprintf(internal_proname, sizeof(internal_proname),
! "__PLTcl_proc_%u", fn_oid);
else
! snprintf(internal_proname, sizeof(internal_proname),
! "__PLTcl_proc_%u_trigger", fn_oid);
/************************************************************
* Lookup the internal proc name in the hashtable
***************
*** 1127,1133 ****
prodesc->arg_is_rel[i] = 1;
if (i > 0)
strcat(proc_internal_args, " ");
! sprintf(buf, "__PLTcl_Tup_%d", i + 1);
strcat(proc_internal_args, buf);
ReleaseSysCache(typeTup);
continue;
--- 1128,1134 ----
prodesc->arg_is_rel[i] = 1;
if (i > 0)
strcat(proc_internal_args, " ");
! snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
strcat(proc_internal_args, buf);
ReleaseSysCache(typeTup);
continue;
***************
*** 1140,1146 ****
if (i > 0)
strcat(proc_internal_args, " ");
! sprintf(buf, "%d", i + 1);
strcat(proc_internal_args, buf);
ReleaseSysCache(typeTup);
--- 1141,1147 ----
if (i > 0)
strcat(proc_internal_args, " ");
! snprintf(buf, sizeof(buf), "%d", i + 1);
strcat(proc_internal_args, buf);
ReleaseSysCache(typeTup);
***************
*** 1177,1183 ****
{
if (!prodesc->arg_is_rel[i])
continue;
! sprintf(buf, "array set %d $__PLTcl_Tup_%d\n", i + 1, i + 1);
Tcl_DStringAppend(&proc_internal_body, buf, -1);
}
}
--- 1178,1185 ----
{
if (!prodesc->arg_is_rel[i])
continue;
! snprintf(buf, sizeof(buf), "array set %d $__PLTcl_Tup_%d\n",
! i + 1, i + 1);
Tcl_DStringAppend(&proc_internal_body, buf, -1);
}
}
***************
*** 1557,1570 ****
{
case SPI_OK_UTILITY:
Tcl_SetResult(interp, "0", TCL_VOLATILE);
return TCL_OK;
case SPI_OK_SELINTO:
case SPI_OK_INSERT:
case SPI_OK_DELETE:
case SPI_OK_UPDATE:
! sprintf(buf, "%d", SPI_processed);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
case SPI_OK_SELECT:
--- 1559,1574 ----
{
case SPI_OK_UTILITY:
Tcl_SetResult(interp, "0", TCL_VOLATILE);
+ SPI_freetuptable(SPI_tuptable);
return TCL_OK;
case SPI_OK_SELINTO:
case SPI_OK_INSERT:
case SPI_OK_DELETE:
case SPI_OK_UPDATE:
! snprintf(buf, sizeof(buf), "%d", SPI_processed);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ SPI_freetuptable(SPI_tuptable);
return TCL_OK;
case SPI_OK_SELECT:
***************
*** 1607,1613 ****
return TCL_ERROR;
default:
! sprintf(buf, "%d", spi_rc);
Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ",
"unknown RC ", buf, NULL);
return TCL_ERROR;
--- 1611,1617 ----
return TCL_ERROR;
default:
! snprintf(buf, sizeof(buf), "%d", spi_rc);
Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ",
"unknown RC ", buf, NULL);
return TCL_ERROR;
***************
*** 1645,1652 ****
{
if (ntuples > 0)
pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
! sprintf(buf, "%d", ntuples);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
return TCL_OK;
}
--- 1649,1657 ----
{
if (ntuples > 0)
pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
! snprintf(buf, sizeof(buf), "%d", ntuples);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ SPI_freetuptable(SPI_tuptable);
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
return TCL_OK;
}
***************
*** 1668,1678 ****
--- 1673,1685 ----
continue;
if (loop_rc == TCL_RETURN)
{
+ SPI_freetuptable(SPI_tuptable);
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
return TCL_RETURN;
}
if (loop_rc == TCL_BREAK)
break;
+ SPI_freetuptable(SPI_tuptable);
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
return TCL_ERROR;
}
***************
*** 1681,1688 ****
* Finally return the number of tuples
************************************************************/
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
! sprintf(buf, "%d", ntuples);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
--- 1688,1705 ----
* Finally return the number of tuples
************************************************************/
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
! snprintf(buf, sizeof(buf), "%d", ntuples);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
+
+ /*
+ * Do *NOT* free the tuptable here. That's because if the loop body
+ * executed any SQL statements, it will have already free'd the
+ * tuptable itself, so freeing it twice is not wise. We could get
+ * around this by making a copy of SPI_tuptable->vals and feeding
+ * that to pltcl_set_tuple_values above, but that would still leak
+ * memory (the palloc'ed copy would only be free'd on context
+ * reset).
+ */
return TCL_OK;
}
***************
*** 1690,1696 ****
/**********************************************************************
* pltcl_SPI_prepare() - Builtin support for prepared plans
* The Tcl command SPI_prepare
! * allways saves the plan using
* SPI_saveplan and returns a key for
* access. There is no chance to prepare
* and not save the plan currently.
--- 1707,1713 ----
/**********************************************************************
* pltcl_SPI_prepare() - Builtin support for prepared plans
* The Tcl command SPI_prepare
! * always saves the plan using
* SPI_saveplan and returns a key for
* access. There is no chance to prepare
* and not save the plan currently.
***************
*** 1736,1742 ****
* Allocate the new querydesc structure
************************************************************/
qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc));
! sprintf(qdesc->qname, "%lx", (long) qdesc);
qdesc->nargs = nargs;
qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid));
qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
--- 1753,1759 ----
* Allocate the new querydesc structure
************************************************************/
qdesc = (pltcl_query_desc *) malloc(sizeof(pltcl_query_desc));
! snprintf(qdesc->qname, sizeof(qdesc->qname), "%lx", (long) qdesc);
qdesc->nargs = nargs;
qdesc->argtypes = (Oid *) malloc(nargs * sizeof(Oid));
qdesc->arginfuncs = (FmgrInfo *) malloc(nargs * sizeof(FmgrInfo));
***************
*** 1815,1821 ****
break;
default:
! sprintf(buf, "unknown RC %d", SPI_result);
reason = buf;
break;
--- 1832,1838 ----
break;
default:
! snprintf(buf, sizeof(buf), "unknown RC %d", SPI_result);
reason = buf;
break;
***************
*** 2116,2129 ****
{
case SPI_OK_UTILITY:
Tcl_SetResult(interp, "0", TCL_VOLATILE);
return TCL_OK;
case SPI_OK_SELINTO:
case SPI_OK_INSERT:
case SPI_OK_DELETE:
case SPI_OK_UPDATE:
! sprintf(buf, "%d", SPI_processed);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
case SPI_OK_SELECT:
--- 2133,2148 ----
{
case SPI_OK_UTILITY:
Tcl_SetResult(interp, "0", TCL_VOLATILE);
+ SPI_freetuptable(SPI_tuptable);
return TCL_OK;
case SPI_OK_SELINTO:
case SPI_OK_INSERT:
case SPI_OK_DELETE:
case SPI_OK_UPDATE:
! snprintf(buf, sizeof(buf), "%d", SPI_processed);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ SPI_freetuptable(SPI_tuptable);
return TCL_OK;
case SPI_OK_SELECT:
***************
*** 2166,2172 ****
return TCL_ERROR;
default:
! sprintf(buf, "%d", spi_rc);
Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ",
"unknown RC ", buf, NULL);
return TCL_ERROR;
--- 2185,2191 ----
return TCL_ERROR;
default:
! snprintf(buf, sizeof(buf), "%d", spi_rc);
Tcl_AppendResult(interp, "pltcl: SPI_exec() failed - ",
"unknown RC ", buf, NULL);
return TCL_ERROR;
***************
*** 2208,2215 ****
if (ntuples > 0)
pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
! sprintf(buf, "%d", ntuples);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
--- 2227,2235 ----
if (ntuples > 0)
pltcl_set_tuple_values(interp, arrayname, 0, tuples[0], tupdesc);
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
! snprintf(buf, sizeof(buf), "%d", ntuples);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
+ SPI_freetuptable(SPI_tuptable);
return TCL_OK;
}
***************
*** 2229,2239 ****
--- 2249,2261 ----
continue;
if (loop_rc == TCL_RETURN)
{
+ SPI_freetuptable(SPI_tuptable);
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
return TCL_RETURN;
}
if (loop_rc == TCL_BREAK)
break;
+ SPI_freetuptable(SPI_tuptable);
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
return TCL_ERROR;
}
***************
*** 2242,2249 ****
* Finally return the number of tuples
************************************************************/
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
! sprintf(buf, "%d", ntuples);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
--- 2264,2281 ----
* Finally return the number of tuples
************************************************************/
memcpy(&Warn_restart, &save_restart, sizeof(Warn_restart));
! snprintf(buf, sizeof(buf), "%d", ntuples);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
+
+ /*
+ * Do *NOT* free the tuptable here. That's because if the loop body
+ * executed any SQL statements, it will have already free'd the
+ * tuptable itself, so freeing it twice is not wise. We could get
+ * around this by making a copy of SPI_tuptable->vals and feeding
+ * that to pltcl_set_tuple_values above, but that would still leak
+ * memory (the palloc'ed copy would only be free'd on context
+ * reset).
+ */
return TCL_OK;
}
***************
*** 2258,2264 ****
{
char buf[64];
! sprintf(buf, "%u", SPI_lastoid);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
--- 2290,2296 ----
{
char buf[64];
! snprintf(buf, sizeof(buf), "%u", SPI_lastoid);
Tcl_SetResult(interp, buf, TCL_VOLATILE);
return TCL_OK;
}
***************
*** 2300,2306 ****
{
arrptr = &arrayname;
nameptr = &attname;
! sprintf(buf, "%d", tupno);
Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
}
--- 2332,2338 ----
{
arrptr = &arrayname;
nameptr = &attname;
! snprintf(buf, sizeof(buf), "%d", tupno);
Tcl_SetVar2(interp, arrayname, ".tupno", buf, 0);
}
---------------------------(end of broadcast)---------------------------
TIP 2: you can get off all lists at once with the unregister command
(send "unregister YourEmailAddressHere" to [EMAIL PROTECTED])