* pltcl.c - PostgreSQL support for Tcl as
* procedural language (PL)
*
- * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.132 2010/02/26 02:01:37 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.133 2010/05/13 18:29:12 tgl Exp $
*
**********************************************************************/
* Global data
**********************************************************************/
static bool pltcl_pm_init_done = false;
-static bool pltcl_be_init_done = false;
+static bool pltcl_be_norm_init_done = false;
+static bool pltcl_be_safe_init_done = false;
static Tcl_Interp *pltcl_hold_interp = NULL;
static Tcl_Interp *pltcl_norm_interp = NULL;
static Tcl_Interp *pltcl_safe_interp = NULL;
Datum pltclu_call_handler(PG_FUNCTION_ARGS);
void _PG_init(void);
-static void pltcl_init_all(void);
static void pltcl_init_interp(Tcl_Interp *interp);
+static Tcl_Interp *pltcl_fetch_interp(bool pltrusted);
static void pltcl_init_load_unknown(Tcl_Interp *interp);
static Datum pltcl_func_handler(PG_FUNCTION_ARGS);
pltcl_pm_init_done = true;
}
-/**********************************************************************
- * pltcl_init_all() - Initialize all
- *
- * This does initialization that can't be done in the postmaster, and
- * hence is not safe to do at library load time.
- **********************************************************************/
-static void
-pltcl_init_all(void)
-{
- /************************************************************
- * Try to load the unknown procedure from pltcl_modules
- ************************************************************/
- if (!pltcl_be_init_done)
- {
- if (SPI_connect() != SPI_OK_CONNECT)
- elog(ERROR, "SPI_connect failed");
- pltcl_init_load_unknown(pltcl_norm_interp);
- pltcl_init_load_unknown(pltcl_safe_interp);
- if (SPI_finish() != SPI_OK_FINISH)
- elog(ERROR, "SPI_finish failed");
- pltcl_be_init_done = true;
- }
-}
-
-
/**********************************************************************
* pltcl_init_interp() - initialize a Tcl interpreter
+ *
+ * The work done here must be safe to do in the postmaster process,
+ * in case the pltcl library is preloaded in the postmaster. Note
+ * that this is applied separately to the "normal" and "safe" interpreters.
**********************************************************************/
static void
pltcl_init_interp(Tcl_Interp *interp)
pltcl_SPI_lastoid, NULL, NULL);
}
+/**********************************************************************
+ * pltcl_fetch_interp() - fetch the Tcl interpreter to use for a function
+ *
+ * This also takes care of any on-first-use initialization required.
+ * The initialization work done here can't be done in the postmaster, and
+ * hence is not safe to do at library load time, because it may invoke
+ * arbitrary user-defined code.
+ * Note: we assume caller has already connected to SPI.
+ **********************************************************************/
+static Tcl_Interp *
+pltcl_fetch_interp(bool pltrusted)
+{
+ Tcl_Interp *interp;
+
+ /* On first use, we try to load the unknown procedure from pltcl_modules */
+ if (pltrusted)
+ {
+ interp = pltcl_safe_interp;
+ if (!pltcl_be_safe_init_done)
+ {
+ pltcl_init_load_unknown(interp);
+ pltcl_be_safe_init_done = true;
+ }
+ }
+ else
+ {
+ interp = pltcl_norm_interp;
+ if (!pltcl_be_norm_init_done)
+ {
+ pltcl_init_load_unknown(interp);
+ pltcl_be_norm_init_done = true;
+ }
+ }
+
+ return interp;
+}
/**********************************************************************
* pltcl_init_load_unknown() - Load the unknown procedure from
static void
pltcl_init_load_unknown(Tcl_Interp *interp)
{
+ Relation pmrel;
+ char *pmrelname,
+ *nspname;
+ char *buf;
+ int buflen;
int spi_rc;
int tcl_rc;
Tcl_DString unknown_src;
/************************************************************
* Check if table pltcl_modules exists
+ *
+ * We allow the table to be found anywhere in the search_path.
+ * This is for backwards compatibility. To ensure that the table
+ * is trustworthy, we require it to be owned by a superuser.
************************************************************/
- spi_rc = SPI_execute("select 1 from pg_catalog.pg_class "
- "where relname = 'pltcl_modules'",
- false, 1);
- SPI_freetuptable(SPI_tuptable);
- if (spi_rc != SPI_OK_SELECT)
- elog(ERROR, "select from pg_class failed");
- if (SPI_processed == 0)
+ pmrel = try_relation_openrv(makeRangeVar(NULL, "pltcl_modules", -1),
+ AccessShareLock);
+ if (pmrel == NULL)
+ return;
+ /* must be table or view, else ignore */
+ if (!(pmrel->rd_rel->relkind == RELKIND_RELATION ||
+ pmrel->rd_rel->relkind == RELKIND_VIEW))
+ {
+ relation_close(pmrel, AccessShareLock);
+ return;
+ }
+ /* must be owned by superuser, else ignore */
+ if (!superuser_arg(pmrel->rd_rel->relowner))
+ {
+ relation_close(pmrel, AccessShareLock);
return;
+ }
+ /* get fully qualified table name for use in select command */
+ nspname = get_namespace_name(RelationGetNamespace(pmrel));
+ if (!nspname)
+ elog(ERROR, "cache lookup failed for namespace %u",
+ RelationGetNamespace(pmrel));
+ pmrelname = quote_qualified_identifier(nspname,
+ RelationGetRelationName(pmrel));
/************************************************************
- * Read all the row's from it where modname = 'unknown' in
- * the order of modseq
+ * Read all the rows from it where modname = 'unknown',
+ * in the order of modseq
************************************************************/
- Tcl_DStringInit(&unknown_src);
+ buflen = strlen(pmrelname) + 100;
+ buf = (char *) palloc(buflen);
+ snprintf(buf, buflen,
+ "select modsrc from %s where modname = 'unknown' order by modseq",
+ pmrelname);
- spi_rc = SPI_execute("select modseq, modsrc from pltcl_modules "
- "where modname = 'unknown' "
- "order by modseq",
- false, 0);
+ spi_rc = SPI_execute(buf, false, 0);
if (spi_rc != SPI_OK_SELECT)
elog(ERROR, "select from pltcl_modules failed");
+ pfree(buf);
+
/************************************************************
* If there's nothing, module unknown doesn't exist
************************************************************/
if (SPI_processed == 0)
{
- Tcl_DStringFree(&unknown_src);
SPI_freetuptable(SPI_tuptable);
elog(WARNING, "module \"unknown\" not found in pltcl_modules");
+ relation_close(pmrel, AccessShareLock);
return;
}
/************************************************************
- * There is a module named unknown. Resemble the
+ * There is a module named unknown. Reassemble the
* source from the modsrc attributes and evaluate
* it in the Tcl interpreter
************************************************************/
fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc");
+ Tcl_DStringInit(&unknown_src);
+
for (i = 0; i < SPI_processed; i++)
{
part = SPI_getvalue(SPI_tuptable->vals[i],
}
}
tcl_rc = Tcl_GlobalEval(interp, Tcl_DStringValue(&unknown_src));
+
Tcl_DStringFree(&unknown_src);
SPI_freetuptable(SPI_tuptable);
+
+ if (tcl_rc != TCL_OK)
+ {
+ UTF_BEGIN;
+ elog(ERROR, "could not load module \"unknown\": %s",
+ UTF_U2E(Tcl_GetStringResult(interp)));
+ UTF_END;
+ }
+
+ relation_close(pmrel, AccessShareLock);
}
FunctionCallInfo save_fcinfo;
pltcl_proc_desc *save_prodesc;
- /*
- * Initialize interpreters if first time through
- */
- pltcl_init_all();
-
/*
* Ensure that static pointers are saved/restored properly
*/
pltcl_current_prodesc = prodesc;
- if (prodesc->lanpltrusted)
- interp = pltcl_safe_interp;
- else
- interp = pltcl_norm_interp;
+ interp = pltcl_fetch_interp(prodesc->lanpltrusted);
/************************************************************
* Create the tcl command to call the internal
pltcl_current_prodesc = prodesc;
- if (prodesc->lanpltrusted)
- interp = pltcl_safe_interp;
- else
- interp = pltcl_norm_interp;
+ interp = pltcl_fetch_interp(prodesc->lanpltrusted);
tupdesc = trigdata->tg_relation->rd_att;
prodesc->lanpltrusted = langStruct->lanpltrusted;
ReleaseSysCache(langTup);
- if (prodesc->lanpltrusted)
- interp = pltcl_safe_interp;
- else
- interp = pltcl_norm_interp;
+ interp = pltcl_fetch_interp(prodesc->lanpltrusted);
/************************************************************
* Get the required information for input conversion of the