*** pgsql/src/pl/tcl/pltcl.c 2010/01/25 01:58:48 1.94.4.3 --- pgsql/src/pl/tcl/pltcl.c 2010/05/13 18:29:45 1.94.4.4 *************** *** 31,37 **** * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.94.4.2 2008/06/17 00:53:13 tgl Exp $ * **********************************************************************/ --- 31,37 ---- * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION ! * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.94.4.3 2010/01/25 01:58:48 tgl Exp $ * **********************************************************************/ *************** *** 48,62 **** --- 48,65 ---- #endif #include "access/heapam.h" + #include "catalog/namespace.h" #include "catalog/pg_language.h" #include "catalog/pg_proc.h" #include "commands/trigger.h" #include "executor/spi.h" #include "fmgr.h" + #include "miscadmin.h" #include "nodes/makefuncs.h" #include "parser/parse_type.h" #include "tcop/tcopprot.h" #include "utils/builtins.h" + #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/typcache.h" *************** typedef struct pltcl_query_desc *** 142,148 **** * Global data **********************************************************************/ static bool pltcl_pm_init_done = false; ! static bool pltcl_be_init_done = false; static Tcl_Interp *pltcl_hold_interp = NULL; static Tcl_Interp *pltcl_norm_interp = NULL; static Tcl_Interp *pltcl_safe_interp = NULL; --- 145,152 ---- * Global data **********************************************************************/ static bool pltcl_pm_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; *************** static pltcl_proc_desc *pltcl_current_pr *** 157,165 **** /********************************************************************** * Forward declarations **********************************************************************/ - static void pltcl_init_all(void); - static void pltcl_init_interp(Tcl_Interp *interp); static void pltcl_init_load_unknown(Tcl_Interp *interp); Datum pltcl_call_handler(PG_FUNCTION_ARGS); --- 161,169 ---- /********************************************************************** * Forward declarations **********************************************************************/ + 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); Datum pltcl_call_handler(PG_FUNCTION_ARGS); *************** pltcl_init(void) *** 277,313 **** } /********************************************************************** - * pltcl_init_all() - Initialize all - **********************************************************************/ - static void - pltcl_init_all(void) - { - /************************************************************ - * Execute postmaster-startup safe initialization - ************************************************************/ - if (!pltcl_pm_init_done) - pltcl_init(); - - /************************************************************ - * Any other initialization that must be done each time a new - * backend starts: - * - 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 **********************************************************************/ static void pltcl_init_interp(Tcl_Interp *interp) --- 281,291 ---- } /********************************************************************** * 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_init_interp(Tcl_Interp *interp) *** 334,339 **** --- 312,353 ---- 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 *************** pltcl_init_interp(Tcl_Interp *interp) *** 342,347 **** --- 356,367 ---- static void pltcl_init_load_unknown(Tcl_Interp *interp) { + Oid relOid; + Relation pmrel; + char *pmrelname, + *nspname; + char *buf; + int buflen; int spi_rc; int tcl_rc; Tcl_DString unknown_src; *************** pltcl_init_load_unknown(Tcl_Interp *inte *** 351,397 **** /************************************************************ * Check if table pltcl_modules exists ************************************************************/ ! 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) return; ! /************************************************************ ! * Read all the row's from it where modname = 'unknown' in ! * the order of modseq ! ************************************************************/ ! Tcl_DStringInit(&unknown_src); ! spi_rc = SPI_execute("select modseq, modsrc from pltcl_modules " ! "where modname = 'unknown' " ! "order by modseq", ! false, 0); if (spi_rc != SPI_OK_SELECT) elog(ERROR, "select from pltcl_modules failed"); /************************************************************ * 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"); return; } /************************************************************ ! * There is a module named unknown. Resemble the * source from the modsrc attributes and evaluate * it in the Tcl interpreter ************************************************************/ fno = SPI_fnumber(SPI_tuptable->tupdesc, "modsrc"); for (i = 0; i < SPI_processed; i++) { part = SPI_getvalue(SPI_tuptable->vals[i], --- 371,457 ---- /************************************************************ * 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. + * + * this next bit of code is the same as try_relation_openrv(), + * which only exists in 8.4 and up. ************************************************************/ ! ! /* Check for shared-cache-inval messages */ ! AcceptInvalidationMessages(); ! ! /* Look up the appropriate relation using namespace search */ ! relOid = RangeVarGetRelid(makeRangeVar(NULL, "pltcl_modules"), true); ! ! /* Drop out on not-found */ ! if (!OidIsValid(relOid)) return; ! /* Let relation_open do the rest */ ! pmrel = relation_open(relOid, 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 rows from it where modname = 'unknown', ! * in the order of modseq ! ************************************************************/ ! 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(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) { SPI_freetuptable(SPI_tuptable); elog(WARNING, "module \"unknown\" not found in pltcl_modules"); + relation_close(pmrel, AccessShareLock); return; } /************************************************************ ! * 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], *************** pltcl_init_load_unknown(Tcl_Interp *inte *** 405,412 **** --- 465,483 ---- } } 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); } *************** pltcl_call_handler(PG_FUNCTION_ARGS) *** 428,436 **** pltcl_proc_desc *save_prodesc; /* ! * Initialize interpreters if first time through */ ! pltcl_init_all(); /* * Ensure that static pointers are saved/restored properly --- 499,508 ---- pltcl_proc_desc *save_prodesc; /* ! * Initialize interpreters if not done previously */ ! if (!pltcl_pm_init_done) ! pltcl_init(); /* * Ensure that static pointers are saved/restored properly *************** pltcl_func_handler(PG_FUNCTION_ARGS) *** 505,514 **** pltcl_current_prodesc = prodesc; ! if (prodesc->lanpltrusted) ! interp = pltcl_safe_interp; ! else ! interp = pltcl_norm_interp; /************************************************************ * Create the tcl command to call the internal --- 577,583 ---- pltcl_current_prodesc = prodesc; ! interp = pltcl_fetch_interp(prodesc->lanpltrusted); /************************************************************ * Create the tcl command to call the internal *************** pltcl_trigger_handler(PG_FUNCTION_ARGS) *** 666,675 **** pltcl_current_prodesc = prodesc; ! if (prodesc->lanpltrusted) ! interp = pltcl_safe_interp; ! else ! interp = pltcl_norm_interp; tupdesc = trigdata->tg_relation->rd_att; --- 735,741 ---- pltcl_current_prodesc = prodesc; ! interp = pltcl_fetch_interp(prodesc->lanpltrusted); tupdesc = trigdata->tg_relation->rd_att; *************** compile_pltcl_function(Oid fn_oid, Oid t *** 1090,1099 **** prodesc->lanpltrusted = langStruct->lanpltrusted; ReleaseSysCache(langTup); ! if (prodesc->lanpltrusted) ! interp = pltcl_safe_interp; ! else ! interp = pltcl_norm_interp; /************************************************************ * Get the required information for input conversion of the --- 1156,1162 ---- prodesc->lanpltrusted = langStruct->lanpltrusted; ReleaseSysCache(langTup); ! interp = pltcl_fetch_interp(prodesc->lanpltrusted); /************************************************************ * Get the required information for input conversion of the