"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])

Reply via email to