*** pgsql/src/pl/tcl/pltcl.c 2010/01/25 01:58:19 1.128.2.1 --- pgsql/src/pl/tcl/pltcl.c 2010/05/13 18:29:19 1.128.2.2 *************** *** 2,8 **** * pltcl.c - PostgreSQL support for Tcl as * procedural language (PL) * ! * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.128 2009/06/11 14:49:14 momjian Exp $ * **********************************************************************/ --- 2,8 ---- * pltcl.c - PostgreSQL support for Tcl as * procedural language (PL) * ! * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.128.2.1 2010/01/25 01:58:19 tgl Exp $ * **********************************************************************/ *************** typedef struct pltcl_query_desc *** 120,126 **** * 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; --- 120,127 ---- * 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; *************** Datum pltcl_call_handler(PG_FUNCTION_AR *** 139,146 **** 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 void pltcl_init_load_unknown(Tcl_Interp *interp); static Datum pltcl_func_handler(PG_FUNCTION_ARGS); --- 140,147 ---- Datum pltclu_call_handler(PG_FUNCTION_ARGS); void _PG_init(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); *************** _PG_init(void) *** 335,366 **** } /********************************************************************** - * 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 **********************************************************************/ static void pltcl_init_interp(Tcl_Interp *interp) --- 336,346 ---- } /********************************************************************** * 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) *** 387,392 **** --- 367,408 ---- 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) *** 395,400 **** --- 411,421 ---- 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; *************** pltcl_init_load_unknown(Tcl_Interp *inte *** 404,450 **** /************************************************************ * 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], --- 425,496 ---- /************************************************************ * 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. ! ************************************************************/ ! 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 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 *** 458,465 **** --- 504,522 ---- } } 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) *** 481,491 **** pltcl_proc_desc *save_prodesc; /* - * Initialize interpreters if first time through - */ - pltcl_init_all(); - - /* * Ensure that static pointers are saved/restored properly */ save_fcinfo = pltcl_current_fcinfo; --- 538,543 ---- *************** pltcl_func_handler(PG_FUNCTION_ARGS) *** 558,567 **** pltcl_current_prodesc = prodesc; ! if (prodesc->lanpltrusted) ! interp = pltcl_safe_interp; ! else ! interp = pltcl_norm_interp; /************************************************************ * Create the tcl command to call the internal --- 610,616 ---- pltcl_current_prodesc = prodesc; ! interp = pltcl_fetch_interp(prodesc->lanpltrusted); /************************************************************ * Create the tcl command to call the internal *************** pltcl_trigger_handler(PG_FUNCTION_ARGS) *** 719,728 **** pltcl_current_prodesc = prodesc; ! if (prodesc->lanpltrusted) ! interp = pltcl_safe_interp; ! else ! interp = pltcl_norm_interp; tupdesc = trigdata->tg_relation->rd_att; --- 768,774 ---- pltcl_current_prodesc = prodesc; ! interp = pltcl_fetch_interp(prodesc->lanpltrusted); tupdesc = trigdata->tg_relation->rd_att; *************** compile_pltcl_function(Oid fn_oid, Oid t *** 1156,1165 **** 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 --- 1202,1208 ---- prodesc->lanpltrusted = langStruct->lanpltrusted; ReleaseSysCache(langTup); ! interp = pltcl_fetch_interp(prodesc->lanpltrusted); /************************************************************ * Get the required information for input conversion of the