typedef struct plperl_query_desc
{
char qname[24];
+ MemoryContext plan_cxt; /* context holding this struct */
SPIPlanPtr plan;
int nargs;
Oid *argtypes;
SV *
plperl_spi_prepare(char *query, int argc, SV **argv)
{
- plperl_query_desc *qdesc;
- plperl_query_entry *hash_entry;
- bool found;
- SPIPlanPtr plan;
- int i;
-
+ volatile SPIPlanPtr plan = NULL;
+ volatile MemoryContext plan_cxt = NULL;
+ plperl_query_desc *volatile qdesc = NULL;
+ plperl_query_entry *volatile hash_entry = NULL;
MemoryContext oldcontext = CurrentMemoryContext;
ResourceOwner oldowner = CurrentResourceOwner;
+ MemoryContext work_cxt;
+ bool found;
+ int i;
check_spi_usage_allowed();
BeginInternalSubTransaction(NULL);
MemoryContextSwitchTo(oldcontext);
- /************************************************************
- * Allocate the new querydesc structure
- ************************************************************/
- qdesc = (plperl_query_desc *) malloc(sizeof(plperl_query_desc));
- MemSet(qdesc, 0, sizeof(plperl_query_desc));
- snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
- qdesc->nargs = argc;
- qdesc->argtypes = (Oid *) malloc(argc * sizeof(Oid));
- qdesc->arginfuncs = (FmgrInfo *) malloc(argc * sizeof(FmgrInfo));
- qdesc->argtypioparams = (Oid *) malloc(argc * sizeof(Oid));
-
PG_TRY();
{
+ CHECK_FOR_INTERRUPTS();
+
+ /************************************************************
+ * Allocate the new querydesc structure
+ *
+ * The qdesc struct, as well as all its subsidiary data, lives in its
+ * plan_cxt. But note that the SPIPlan does not.
+ ************************************************************/
+ plan_cxt = AllocSetContextCreate(TopMemoryContext,
+ "PL/Perl spi_prepare query",
+ ALLOCSET_SMALL_MINSIZE,
+ ALLOCSET_SMALL_INITSIZE,
+ ALLOCSET_SMALL_MAXSIZE);
+ MemoryContextSwitchTo(plan_cxt);
+ qdesc = (plperl_query_desc *) palloc0(sizeof(plperl_query_desc));
+ snprintf(qdesc->qname, sizeof(qdesc->qname), "%p", qdesc);
+ qdesc->plan_cxt = plan_cxt;
+ qdesc->nargs = argc;
+ qdesc->argtypes = (Oid *) palloc(argc * sizeof(Oid));
+ qdesc->arginfuncs = (FmgrInfo *) palloc(argc * sizeof(FmgrInfo));
+ qdesc->argtypioparams = (Oid *) palloc(argc * sizeof(Oid));
+ MemoryContextSwitchTo(oldcontext);
+
+ /************************************************************
+ * Do the following work in a short-lived context so that we don't
+ * leak a lot of memory in the PL/Perl function's SPI Proc context.
+ ************************************************************/
+ work_cxt = AllocSetContextCreate(CurrentMemoryContext,
+ "PL/Perl spi_prepare workspace",
+ ALLOCSET_DEFAULT_MINSIZE,
+ ALLOCSET_DEFAULT_INITSIZE,
+ ALLOCSET_DEFAULT_MAXSIZE);
+ MemoryContextSwitchTo(work_cxt);
+
/************************************************************
* Resolve argument type names and then look them up by oid
* in the system cache, and remember the required information
getTypeInputInfo(typId, &typInput, &typIOParam);
qdesc->argtypes[i] = typId;
- perm_fmgr_info(typInput, &(qdesc->arginfuncs[i]));
+ fmgr_info_cxt(typInput, &(qdesc->arginfuncs[i]), plan_cxt);
qdesc->argtypioparams[i] = typIOParam;
}
elog(ERROR, "SPI_keepplan() failed");
qdesc->plan = plan;
+ /************************************************************
+ * Insert a hashtable entry for the plan.
+ ************************************************************/
+ hash_entry = hash_search(plperl_active_interp->query_hash,
+ qdesc->qname,
+ HASH_ENTER, &found);
+ hash_entry->query_data = qdesc;
+
+ /* Get rid of workspace */
+ MemoryContextDelete(work_cxt);
+
/* Commit the inner transaction, return to outer xact context */
ReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
{
ErrorData *edata;
- free(qdesc->argtypes);
- free(qdesc->arginfuncs);
- free(qdesc->argtypioparams);
- free(qdesc);
-
/* Save error info */
MemoryContextSwitchTo(oldcontext);
edata = CopyErrorData();
FlushErrorState();
+ /* Drop anything we managed to allocate */
+ if (hash_entry)
+ hash_search(plperl_active_interp->query_hash,
+ qdesc->qname,
+ HASH_REMOVE, NULL);
+ if (plan_cxt)
+ MemoryContextDelete(plan_cxt);
+ if (plan)
+ SPI_freeplan(plan);
+
/* Abort the inner transaction */
RollbackAndReleaseCurrentSubTransaction();
MemoryContextSwitchTo(oldcontext);
PG_END_TRY();
/************************************************************
- * Insert a hashtable entry for the plan and return
- * the key to the caller.
+ * Return the query's hash key to the caller.
************************************************************/
-
- hash_entry = hash_search(plperl_active_interp->query_hash, qdesc->qname,
- HASH_ENTER, &found);
- hash_entry->query_data = qdesc;
-
return cstr2sv(qdesc->qname);
}
/************************************************************
* Fetch the saved plan descriptor, see if it's o.k.
************************************************************/
-
hash_entry = hash_search(plperl_active_interp->query_hash, query,
HASH_FIND, NULL);
if (hash_entry == NULL)
elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
qdesc = hash_entry->query_data;
-
if (qdesc == NULL)
- elog(ERROR, "spi_exec_prepared: panic - plperl query_hash value vanished");
+ elog(ERROR, "spi_exec_prepared: plperl query_hash value vanished");
if (qdesc->nargs != argc)
elog(ERROR, "spi_exec_prepared: expected %d argument(s), %d passed",
hash_entry = hash_search(plperl_active_interp->query_hash, query,
HASH_FIND, NULL);
if (hash_entry == NULL)
- elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
+ elog(ERROR, "spi_query_prepared: Invalid prepared query passed");
qdesc = hash_entry->query_data;
-
if (qdesc == NULL)
- elog(ERROR, "spi_query_prepared: panic - plperl query_hash value vanished");
+ elog(ERROR, "spi_query_prepared: plperl query_hash value vanished");
if (qdesc->nargs != argc)
elog(ERROR, "spi_query_prepared: expected %d argument(s), %d passed",
hash_entry = hash_search(plperl_active_interp->query_hash, query,
HASH_FIND, NULL);
if (hash_entry == NULL)
- elog(ERROR, "spi_exec_prepared: Invalid prepared query passed");
+ elog(ERROR, "spi_freeplan: Invalid prepared query passed");
qdesc = hash_entry->query_data;
-
if (qdesc == NULL)
- elog(ERROR, "spi_exec_freeplan: panic - plperl query_hash value vanished");
+ elog(ERROR, "spi_freeplan: plperl query_hash value vanished");
+ plan = qdesc->plan;
/*
* free all memory before SPI_freeplan, so if it dies, nothing will be
hash_search(plperl_active_interp->query_hash, query,
HASH_REMOVE, NULL);
- plan = qdesc->plan;
- free(qdesc->argtypes);
- free(qdesc->arginfuncs);
- free(qdesc->argtypioparams);
- free(qdesc);
+ MemoryContextDelete(qdesc->plan_cxt);
SPI_freeplan(plan);
}