diff options
Diffstat (limited to 'src/pl')
-rw-r--r-- | src/pl/plperl/plperl.c | 153 | ||||
-rw-r--r-- | src/pl/plperl/spi_internal.c | 85 | ||||
-rw-r--r-- | src/pl/plperl/spi_internal.h | 1 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_comp.c | 5 | ||||
-rw-r--r-- | src/pl/plpgsql/src/pl_exec.c | 52 | ||||
-rw-r--r-- | src/pl/plpgsql/src/plpgsql.h | 5 | ||||
-rw-r--r-- | src/pl/plpython/plpython.c | 50 | ||||
-rw-r--r-- | src/pl/tcl/pltcl.c | 110 |
8 files changed, 281 insertions, 180 deletions
diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index ae4a157ac40..c517ca0c3c7 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -33,7 +33,7 @@ * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.50 2004/08/30 02:54:41 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.51 2004/09/13 20:08:59 tgl Exp $ * **********************************************************************/ @@ -53,6 +53,7 @@ #include "executor/spi.h" #include "fmgr.h" #include "tcop/tcopprot.h" +#include "utils/lsyscache.h" #include "utils/syscache.h" #include "utils/typcache.h" @@ -77,6 +78,7 @@ typedef struct plperl_proc_desc char *proname; TransactionId fn_xmin; CommandId fn_cmin; + bool fn_readonly; bool lanpltrusted; bool fn_retistuple; /* true, if function returns tuple */ bool fn_retisset; /* true, if function returns set */ @@ -98,11 +100,13 @@ static int plperl_firstcall = 1; static bool plperl_safe_init_done = false; static PerlInterpreter *plperl_interp = NULL; static HV *plperl_proc_hash = NULL; -static AV *g_row_keys = NULL; static AV *g_column_keys = NULL; static SV *srf_perlret = NULL; /* keep returned value */ static int g_attr_num = 0; +/* this is saved and restored by plperl_call_handler */ +static plperl_proc_desc *plperl_current_prodesc = NULL; + /********************************************************************** * Forward declarations **********************************************************************/ @@ -119,6 +123,7 @@ static plperl_proc_desc *compile_plperl_function(Oid fn_oid, bool is_trigger); static SV *plperl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc); static void plperl_init_shared_libs(pTHX); +static HV *plperl_spi_execute_fetch_result(SPITupleTable *, int, int); /* @@ -435,7 +440,6 @@ static AV * plperl_get_keys(HV *hv) { AV *ret; - SV **svp; int key_count; SV *val; char *key; @@ -445,7 +449,7 @@ plperl_get_keys(HV *hv) ret = newAV(); hv_iterinit(hv); - while (val = hv_iternextsv(hv, (char **) &key, &klen)) + while ((val = hv_iternextsv(hv, (char **) &key, &klen))) { av_store(ret, key_count, eval_pv(key, TRUE)); key_count++; @@ -592,26 +596,43 @@ Datum plperl_call_handler(PG_FUNCTION_ARGS) { Datum retval; + plperl_proc_desc *save_prodesc; - /************************************************************ - * Initialize interpreter - ************************************************************/ + /* + * Initialize interpreter if first time through + */ plperl_init_all(); - /************************************************************ - * Connect to SPI manager - ************************************************************/ - if (SPI_connect() != SPI_OK_CONNECT) - elog(ERROR, "could not connect to SPI manager"); + /* + * Ensure that static pointers are saved/restored properly + */ + save_prodesc = plperl_current_prodesc; - /************************************************************ - * Determine if called as function or trigger and - * call appropriate subhandler - ************************************************************/ - if (CALLED_AS_TRIGGER(fcinfo)) - retval = PointerGetDatum(plperl_trigger_handler(fcinfo)); - else - retval = plperl_func_handler(fcinfo); + PG_TRY(); + { + /************************************************************ + * Connect to SPI manager + ************************************************************/ + if (SPI_connect() != SPI_OK_CONNECT) + elog(ERROR, "could not connect to SPI manager"); + + /************************************************************ + * Determine if called as function or trigger and + * call appropriate subhandler + ************************************************************/ + if (CALLED_AS_TRIGGER(fcinfo)) + retval = PointerGetDatum(plperl_trigger_handler(fcinfo)); + else + retval = plperl_func_handler(fcinfo); + } + PG_CATCH(); + { + plperl_current_prodesc = save_prodesc; + PG_RE_THROW(); + } + PG_END_TRY(); + + plperl_current_prodesc = save_prodesc; return retval; } @@ -821,7 +842,6 @@ plperl_call_perl_trigger_func(plperl_proc_desc *desc, FunctionCallInfo fcinfo, S SV *retval; int i; int count; - char *ret_test; ENTER; SAVETMPS; @@ -874,6 +894,9 @@ plperl_func_handler(PG_FUNCTION_ARGS) /* Find or compile the function */ prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, false); + + plperl_current_prodesc = prodesc; + /************************************************************ * Call the Perl function if not returning set ************************************************************/ @@ -1002,7 +1025,6 @@ plperl_func_handler(PG_FUNCTION_ARGS) { HV *row_hv; SV **svp; - char *row_key; svp = av_fetch(ret_av, call_cntr, FALSE); @@ -1052,7 +1074,6 @@ plperl_func_handler(PG_FUNCTION_ARGS) if (SRF_IS_FIRSTCALL()) { MemoryContext oldcontext; - int i; funcctx = SRF_FIRSTCALL_INIT(); oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx); @@ -1067,7 +1088,6 @@ plperl_func_handler(PG_FUNCTION_ARGS) Datum result; AV *array; SV **svp; - int i; array = (AV *) SvRV(perlret); svp = av_fetch(array, funcctx->call_cntr, FALSE); @@ -1158,6 +1178,8 @@ plperl_trigger_handler(PG_FUNCTION_ARGS) /* Find or compile the function */ prodesc = compile_plperl_function(fcinfo->flinfo->fn_oid, true); + plperl_current_prodesc = prodesc; + /************************************************************ * Call the Perl function ************************************************************/ @@ -1323,6 +1345,10 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); + /* Remember if function is STABLE/IMMUTABLE */ + prodesc->fn_readonly = + (procStruct->provolatile != PROVOLATILE_VOLATILE); + /************************************************************ * Lookup the pg_language tuple by Oid ************************************************************/ @@ -1560,3 +1586,82 @@ plperl_build_tuple_argument(HeapTuple tuple, TupleDesc tupdesc) output = perl_eval_pv(SvPV(output, PL_na), TRUE); return output; } + + +HV * +plperl_spi_exec(char *query, int limit) +{ + HV *ret_hv; + int spi_rv; + + spi_rv = SPI_execute(query, plperl_current_prodesc->fn_readonly, limit); + ret_hv = plperl_spi_execute_fetch_result(SPI_tuptable, SPI_processed, spi_rv); + + return ret_hv; +} + +static HV * +plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc) +{ + int i; + char *attname; + char *attdata; + + HV *array; + + array = newHV(); + + for (i = 0; i < tupdesc->natts; i++) + { + /************************************************************ + * Get the attribute name + ************************************************************/ + attname = tupdesc->attrs[i]->attname.data; + + /************************************************************ + * Get the attributes value + ************************************************************/ + attdata = SPI_getvalue(tuple, tupdesc, i + 1); + if (attdata) + hv_store(array, attname, strlen(attname), newSVpv(attdata, 0), 0); + else + hv_store(array, attname, strlen(attname), newSVpv("undef", 0), 0); + } + return array; +} + +static HV * +plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed, int status) +{ + HV *result; + + result = newHV(); + + hv_store(result, "status", strlen("status"), + newSVpv((char *) SPI_result_code_string(status), 0), 0); + hv_store(result, "processed", strlen("processed"), + newSViv(processed), 0); + + if (status == SPI_OK_SELECT) + { + if (processed) + { + AV *rows; + HV *row; + int i; + + rows = newAV(); + for (i = 0; i < processed; i++) + { + row = plperl_hash_from_tuple(tuptable->vals[i], tuptable->tupdesc); + av_store(rows, i, newRV_noinc((SV *) row)); + } + hv_store(result, "rows", strlen("rows"), + newRV_noinc((SV *) rows), 0); + } + } + + SPI_freetuptable(tuptable); + + return result; +} diff --git a/src/pl/plperl/spi_internal.c b/src/pl/plperl/spi_internal.c index 5c3bb38a534..390e76a7e77 100644 --- a/src/pl/plperl/spi_internal.c +++ b/src/pl/plperl/spi_internal.c @@ -1,15 +1,12 @@ -#include "postgres.h" -#include "executor/spi.h" -#include "utils/syscache.h" /* * This kludge is necessary because of the conflicting * definitions of 'DEBUG' between postgres and perl. * we'll live. */ -#include "spi_internal.h" +#include "postgres.h" -static HV *plperl_spi_execute_fetch_result(SPITupleTable *, int, int); +#include "spi_internal.h" int @@ -47,81 +44,3 @@ spi_ERROR(void) { return ERROR; } - -HV * -plperl_spi_exec(char *query, int limit) -{ - HV *ret_hv; - int spi_rv; - - spi_rv = SPI_exec(query, limit); - ret_hv = plperl_spi_execute_fetch_result(SPI_tuptable, SPI_processed, spi_rv); - - return ret_hv; -} - -static HV * -plperl_hash_from_tuple(HeapTuple tuple, TupleDesc tupdesc) -{ - int i; - char *attname; - char *attdata; - - HV *array; - - array = newHV(); - - for (i = 0; i < tupdesc->natts; i++) - { - /************************************************************ - * Get the attribute name - ************************************************************/ - attname = tupdesc->attrs[i]->attname.data; - - /************************************************************ - * Get the attributes value - ************************************************************/ - attdata = SPI_getvalue(tuple, tupdesc, i + 1); - if (attdata) - hv_store(array, attname, strlen(attname), newSVpv(attdata, 0), 0); - else - hv_store(array, attname, strlen(attname), newSVpv("undef", 0), 0); - } - return array; -} - -static HV * -plperl_spi_execute_fetch_result(SPITupleTable *tuptable, int processed, int status) -{ - HV *result; - - result = newHV(); - - hv_store(result, "status", strlen("status"), - newSVpv((char *) SPI_result_code_string(status), 0), 0); - hv_store(result, "processed", strlen("processed"), - newSViv(processed), 0); - - if (status == SPI_OK_SELECT) - { - if (processed) - { - AV *rows; - HV *row; - int i; - - rows = newAV(); - for (i = 0; i < processed; i++) - { - row = plperl_hash_from_tuple(tuptable->vals[i], tuptable->tupdesc); - av_store(rows, i, newRV_noinc((SV *) row)); - } - hv_store(result, "rows", strlen("rows"), - newRV_noinc((SV *) rows), 0); - } - } - - SPI_freetuptable(tuptable); - - return result; -} diff --git a/src/pl/plperl/spi_internal.h b/src/pl/plperl/spi_internal.h index 1f1984a1570..b66f43eb2ec 100644 --- a/src/pl/plperl/spi_internal.h +++ b/src/pl/plperl/spi_internal.h @@ -15,4 +15,5 @@ int spi_WARNING(void); int spi_ERROR(void); +/* this is actually in plperl.c */ HV *plperl_spi_exec(char *, int); diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 02b61c7396a..ea95bef629b 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.81 2004/08/30 02:54:42 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.82 2004/09/13 20:09:20 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -578,6 +578,9 @@ do_compile(FunctionCallInfo fcinfo, break; } + /* Remember if function is STABLE/IMMUTABLE */ + function->fn_readonly = (procStruct->provolatile != PROVOLATILE_VOLATILE); + /* * Create the magic FOUND variable. */ diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 902723843c7..172c0f0fd02 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.118 2004/08/30 02:54:42 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.119 2004/09/13 20:09:20 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -897,6 +897,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) * sub-transaction */ MemoryContext oldcontext = CurrentMemoryContext; + ResourceOwner oldowner = CurrentResourceOwner; volatile bool caught = false; int xrc; @@ -907,12 +908,15 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) BeginInternalSubTransaction(NULL); /* Want to run statements inside function's memory context */ MemoryContextSwitchTo(oldcontext); + if ((xrc = SPI_connect()) != SPI_OK_CONNECT) elog(ERROR, "SPI_connect failed: %s", SPI_result_code_string(xrc)); PG_TRY(); - rc = exec_stmts(estate, block->body); + { + rc = exec_stmts(estate, block->body); + } PG_CATCH(); { ErrorData *edata; @@ -927,6 +931,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) /* Abort the inner transaction (and inner SPI connection) */ RollbackAndReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); + CurrentResourceOwner = oldowner; SPI_pop(); @@ -958,8 +963,11 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block) if ((xrc = SPI_finish()) != SPI_OK_FINISH) elog(ERROR, "SPI_finish failed: %s", SPI_result_code_string(xrc)); + ReleaseCurrentSubTransaction(); MemoryContextSwitchTo(oldcontext); + CurrentResourceOwner = oldowner; + SPI_pop(); } } @@ -1984,6 +1992,8 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate, estate->retistuple = func->fn_retistuple; estate->retisset = func->fn_retset; + estate->readonly_func = func->fn_readonly; + estate->rettupdesc = NULL; estate->exitlabel = NULL; @@ -2019,7 +2029,7 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate, static void exec_eval_cleanup(PLpgSQL_execstate *estate) { - /* Clear result of a full SPI_exec */ + /* Clear result of a full SPI_execute */ if (estate->eval_tuptable != NULL) SPI_freetuptable(estate->eval_tuptable); estate->eval_tuptable = NULL; @@ -2120,7 +2130,7 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, exec_prepare_plan(estate, expr); /* - * Now build up the values and nulls arguments for SPI_execp() + * Now build up the values and nulls arguments for SPI_execute_plan() */ values = (Datum *) palloc(expr->nparams * sizeof(Datum)); nulls = (char *) palloc(expr->nparams * sizeof(char)); @@ -2142,7 +2152,8 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, /* * Execute the plan */ - rc = SPI_execp(expr->plan, values, nulls, 0); + rc = SPI_execute_plan(expr->plan, values, nulls, + estate->readonly_func, 0); switch (rc) { case SPI_OK_UTILITY: @@ -2168,12 +2179,12 @@ exec_stmt_execsql(PLpgSQL_execstate *estate, errhint("If you want to discard the results, use PERFORM instead."))); default: - elog(ERROR, "SPI_execp failed executing query \"%s\": %s", + elog(ERROR, "SPI_execute_plan failed executing query \"%s\": %s", expr->query, SPI_result_code_string(rc)); } /* - * Release any result tuples from SPI_execp (probably shouldn't be + * Release any result tuples from SPI_execute_plan (probably shouldn't be * any) */ SPI_freetuptable(SPI_tuptable); @@ -2220,11 +2231,11 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate, exec_eval_cleanup(estate); /* - * Call SPI_exec() without preparing a saved plan. The returncode can + * Call SPI_execute() without preparing a saved plan. The returncode can * be any standard OK. Note that while a SELECT is allowed, its * results will be discarded. */ - exec_res = SPI_exec(querystr, 0); + exec_res = SPI_execute(querystr, estate->readonly_func, 0); switch (exec_res) { case SPI_OK_SELECT: @@ -2249,7 +2260,7 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate, * behavior is not consistent with SELECT INTO in a normal * plpgsql context. (We need to reimplement EXECUTE to parse * the string as a plpgsql command, not just feed it to - * SPI_exec.) However, CREATE AS should be allowed ... and + * SPI_execute.) However, CREATE AS should be allowed ... and * since it produces the same parsetree as SELECT INTO, * there's no way to tell the difference except to look at the * source text. Wotta kluge! @@ -2284,12 +2295,12 @@ exec_stmt_dynexecute(PLpgSQL_execstate *estate, errhint("Use a BEGIN block with an EXCEPTION clause instead."))); default: - elog(ERROR, "SPI_exec failed executing query \"%s\": %s", + elog(ERROR, "SPI_execute failed executing query \"%s\": %s", querystr, SPI_result_code_string(exec_res)); break; } - /* Release any result from SPI_exec, as well as the querystring */ + /* Release any result from SPI_execute, as well as the querystring */ SPI_freetuptable(SPI_tuptable); pfree(querystr); @@ -2357,7 +2368,8 @@ exec_stmt_dynfors(PLpgSQL_execstate *estate, PLpgSQL_stmt_dynfors *stmt) if (plan == NULL) elog(ERROR, "SPI_prepare failed for \"%s\": %s", querystr, SPI_result_code_string(SPI_result)); - portal = SPI_cursor_open(NULL, plan, NULL, NULL); + portal = SPI_cursor_open(NULL, plan, NULL, NULL, + estate->readonly_func); if (portal == NULL) elog(ERROR, "could not open implicit cursor for query \"%s\": %s", querystr, SPI_result_code_string(SPI_result)); @@ -2549,7 +2561,8 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt) if (curplan == NULL) elog(ERROR, "SPI_prepare failed for \"%s\": %s", querystr, SPI_result_code_string(SPI_result)); - portal = SPI_cursor_open(curname, curplan, NULL, NULL); + portal = SPI_cursor_open(curname, curplan, NULL, NULL, + estate->readonly_func); if (portal == NULL) elog(ERROR, "could not open cursor for query \"%s\": %s", querystr, SPI_result_code_string(SPI_result)); @@ -2643,7 +2656,8 @@ exec_stmt_open(PLpgSQL_execstate *estate, PLpgSQL_stmt_open *stmt) * Open the cursor * ---------- */ - portal = SPI_cursor_open(curname, query->plan, values, nulls); + portal = SPI_cursor_open(curname, query->plan, values, nulls, + estate->readonly_func); if (portal == NULL) elog(ERROR, "could not open cursor: %s", SPI_result_code_string(SPI_result)); @@ -3470,7 +3484,7 @@ exec_run_select(PLpgSQL_execstate *estate, exec_prepare_plan(estate, expr); /* - * Now build up the values and nulls arguments for SPI_execp() + * Now build up the values and nulls arguments for SPI_execute_plan() */ values = (Datum *) palloc(expr->nparams * sizeof(Datum)); nulls = (char *) palloc(expr->nparams * sizeof(char)); @@ -3494,7 +3508,8 @@ exec_run_select(PLpgSQL_execstate *estate, */ if (portalP != NULL) { - *portalP = SPI_cursor_open(NULL, expr->plan, values, nulls); + *portalP = SPI_cursor_open(NULL, expr->plan, values, nulls, + estate->readonly_func); if (*portalP == NULL) elog(ERROR, "could not open implicit cursor for query \"%s\": %s", expr->query, SPI_result_code_string(SPI_result)); @@ -3506,7 +3521,8 @@ exec_run_select(PLpgSQL_execstate *estate, /* * Execute the query */ - rc = SPI_execp(expr->plan, values, nulls, maxtuples); + rc = SPI_execute_plan(expr->plan, values, nulls, + estate->readonly_func, maxtuples); if (rc != SPI_OK_SELECT) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h index c3919fc3753..26f1bd156b0 100644 --- a/src/pl/plpgsql/src/plpgsql.h +++ b/src/pl/plpgsql/src/plpgsql.h @@ -3,7 +3,7 @@ * procedural language * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.53 2004/08/30 02:54:42 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.54 2004/09/13 20:09:21 tgl Exp $ * * This software is copyrighted by Jan Wieck - Hamburg. * @@ -585,6 +585,7 @@ typedef struct PLpgSQL_function Oid fn_rettypioparam; bool fn_retistuple; bool fn_retset; + bool fn_readonly; int fn_nargs; int fn_argvarnos[FUNC_MAX_ARGS]; @@ -615,6 +616,8 @@ typedef struct bool retistuple; bool retisset; + bool readonly_func; + TupleDesc rettupdesc; char *exitlabel; diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index ecbf2dbce8a..dd4ef3e455a 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -29,7 +29,7 @@ * MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.55 2004/08/30 02:54:42 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.56 2004/09/13 20:09:30 tgl Exp $ * ********************************************************************* */ @@ -131,6 +131,7 @@ typedef struct PLyProcedure char *pyname; /* Python name of procedure */ TransactionId fn_xmin; CommandId fn_cmin; + bool fn_readonly; PLyTypeInfo result; /* also used to store info for trigger * tuple type */ PLyTypeInfo args[FUNC_MAX_ARGS]; @@ -257,11 +258,9 @@ static PyObject *PLyString_FromString(const char *); static int PLy_first_call = 1; /* - * Last function called by postgres backend - * - * XXX replace this with errcontext mechanism + * Currently active plpython function */ -static PLyProcedure *PLy_last_procedure = NULL; +static PLyProcedure *PLy_curr_procedure = NULL; /* * When a callback from Python into PG incurs an error, we temporarily store @@ -322,6 +321,7 @@ Datum plpython_call_handler(PG_FUNCTION_ARGS) { Datum retval; + PLyProcedure *save_curr_proc; PLyProcedure *volatile proc = NULL; PLy_init_all(); @@ -329,6 +329,8 @@ plpython_call_handler(PG_FUNCTION_ARGS) if (SPI_connect() != SPI_OK_CONNECT) elog(ERROR, "could not connect to SPI manager"); + save_curr_proc = PLy_curr_procedure; + PG_TRY(); { if (CALLED_AS_TRIGGER(fcinfo)) @@ -338,17 +340,20 @@ plpython_call_handler(PG_FUNCTION_ARGS) proc = PLy_procedure_get(fcinfo, RelationGetRelid(tdata->tg_relation)); + PLy_curr_procedure = proc; trv = PLy_trigger_handler(fcinfo, proc); retval = PointerGetDatum(trv); } else { proc = PLy_procedure_get(fcinfo, InvalidOid); + PLy_curr_procedure = proc; retval = PLy_function_handler(fcinfo, proc); } } PG_CATCH(); { + PLy_curr_procedure = save_curr_proc; if (proc) { /* note: Py_DECREF needs braces around it, as of 2003/08 */ @@ -359,6 +364,8 @@ plpython_call_handler(PG_FUNCTION_ARGS) } PG_END_TRY(); + PLy_curr_procedure = save_curr_proc; + Py_DECREF(proc->me); return retval; @@ -795,14 +802,10 @@ static PyObject * PLy_procedure_call(PLyProcedure * proc, char *kargs, PyObject * vargs) { PyObject *rv; - PLyProcedure *current; - current = PLy_last_procedure; - PLy_last_procedure = proc; PyDict_SetItemString(proc->globals, kargs, vargs); rv = PyEval_EvalCode((PyCodeObject *) proc->code, proc->globals, proc->globals); - PLy_last_procedure = current; /* * If there was an error in a PG callback, propagate that no matter @@ -1005,6 +1008,9 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid, strcpy(proc->pyname, procName); proc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); proc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); + /* Remember if function is STABLE/IMMUTABLE */ + proc->fn_readonly = + (procStruct->provolatile != PROVOLATILE_VOLATILE); PLy_typeinfo_init(&proc->result); for (i = 0; i < FUNC_MAX_ARGS; i++) PLy_typeinfo_init(&proc->args[i]); @@ -1935,7 +1941,8 @@ PLy_spi_prepare(PyObject * self, PyObject * args) PyErr_SetString(PLy_exc_spi_error, "Unknown error in PLy_spi_prepare"); /* XXX this oughta be replaced with errcontext mechanism */ - PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure)); + PLy_elog(WARNING, "in function %s:", + PLy_procedure_name(PLy_curr_procedure)); return NULL; } PG_END_TRY(); @@ -2054,7 +2061,8 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit) } } - rv = SPI_execp(plan->plan, plan->values, nulls, limit); + rv = SPI_execute_plan(plan->plan, plan->values, nulls, + PLy_curr_procedure->fn_readonly, limit); pfree(nulls); } @@ -2080,7 +2088,9 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit) if (!PyErr_Occurred()) PyErr_SetString(PLy_exc_error, "Unknown error in PLy_spi_execute_plan"); - PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure)); + /* XXX this oughta be replaced with errcontext mechanism */ + PLy_elog(WARNING, "in function %s:", + PLy_procedure_name(PLy_curr_procedure)); return NULL; } PG_END_TRY(); @@ -2098,7 +2108,7 @@ PLy_spi_execute_plan(PyObject * ob, PyObject * list, int limit) if (rv < 0) { PLy_exception_set(PLy_exc_spi_error, - "SPI_execp failed: %s", + "SPI_execute_plan failed: %s", SPI_result_code_string(rv)); return NULL; } @@ -2114,7 +2124,9 @@ PLy_spi_execute_query(char *query, int limit) oldcontext = CurrentMemoryContext; PG_TRY(); - rv = SPI_exec(query, limit); + { + rv = SPI_execute(query, PLy_curr_procedure->fn_readonly, limit); + } PG_CATCH(); { MemoryContextSwitchTo(oldcontext); @@ -2123,7 +2135,9 @@ PLy_spi_execute_query(char *query, int limit) if (!PyErr_Occurred()) PyErr_SetString(PLy_exc_spi_error, "Unknown error in PLy_spi_execute_query"); - PLy_elog(WARNING, "in function %s:", PLy_procedure_name(PLy_last_procedure)); + /* XXX this oughta be replaced with errcontext mechanism */ + PLy_elog(WARNING, "in function %s:", + PLy_procedure_name(PLy_curr_procedure)); return NULL; } PG_END_TRY(); @@ -2131,7 +2145,7 @@ PLy_spi_execute_query(char *query, int limit) if (rv < 0) { PLy_exception_set(PLy_exc_spi_error, - "SPI_exec failed: %s", + "SPI_execute failed: %s", SPI_result_code_string(rv)); return NULL; } @@ -2375,7 +2389,9 @@ PLy_output(volatile int level, PyObject * self, PyObject * args) oldcontext = CurrentMemoryContext; PG_TRY(); - elog(level, "%s", sv); + { + elog(level, "%s", sv); + } PG_CATCH(); { MemoryContextSwitchTo(oldcontext); diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index a59cef23f91..f344d4ad676 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -31,7 +31,7 @@ * ENHANCEMENTS, OR MODIFICATIONS. * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.91 2004/08/30 02:54:42 momjian Exp $ + * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.92 2004/09/13 20:09:39 tgl Exp $ * **********************************************************************/ @@ -87,13 +87,17 @@ utf_e2u(unsigned char *src) pfree(_pltcl_utf_dst); } while (0) #define UTF_U2E(x) (_pltcl_utf_dst=utf_u2e(_pltcl_utf_src=(x))) #define UTF_E2U(x) (_pltcl_utf_dst=utf_e2u(_pltcl_utf_src=(x))) -#else /* PLTCL_UTF */ + +#else /* !PLTCL_UTF */ + #define UTF_BEGIN #define UTF_END #define UTF_U2E(x) (x) #define UTF_E2U(x) (x) + #endif /* PLTCL_UTF */ + /********************************************************************** * The information we cache about loaded procedures **********************************************************************/ @@ -102,6 +106,7 @@ typedef struct pltcl_proc_desc char *proname; TransactionId fn_xmin; CommandId fn_cmin; + bool fn_readonly; bool lanpltrusted; FmgrInfo result_in_func; Oid result_typioparam; @@ -137,7 +142,10 @@ static Tcl_Interp *pltcl_safe_interp = NULL; static Tcl_HashTable *pltcl_proc_hash = NULL; static Tcl_HashTable *pltcl_norm_query_hash = NULL; static Tcl_HashTable *pltcl_safe_query_hash = NULL; + +/* these are saved and restored by pltcl_call_handler */ static FunctionCallInfo pltcl_current_fcinfo = NULL; +static pltcl_proc_desc *pltcl_current_prodesc = NULL; /* * When a callback from Tcl into PG incurs an error, we temporarily store @@ -179,11 +187,11 @@ static int pltcl_argisnull(ClientData cdata, Tcl_Interp *interp, static int pltcl_returnnull(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]); -static int pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, +static int pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]); static int pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]); -static int pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, +static int pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]); static int pltcl_SPI_lastoid(ClientData cdata, Tcl_Interp *interp, int argc, CONST84 char *argv[]); @@ -307,11 +315,11 @@ pltcl_init_interp(Tcl_Interp *interp) pltcl_returnnull, NULL, NULL); Tcl_CreateCommand(interp, "spi_exec", - pltcl_SPI_exec, NULL, NULL); + pltcl_SPI_execute, NULL, NULL); Tcl_CreateCommand(interp, "spi_prepare", pltcl_SPI_prepare, NULL, NULL); Tcl_CreateCommand(interp, "spi_execp", - pltcl_SPI_execp, NULL, NULL); + pltcl_SPI_execute_plan, NULL, NULL); Tcl_CreateCommand(interp, "spi_lastoid", pltcl_SPI_lastoid, NULL, NULL); } @@ -334,8 +342,9 @@ pltcl_init_load_unknown(Tcl_Interp *interp) /************************************************************ * Check if table pltcl_modules exists ************************************************************/ - spi_rc = SPI_exec("select 1 from pg_catalog.pg_class " - "where relname = 'pltcl_modules'", 1); + 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"); @@ -348,9 +357,10 @@ pltcl_init_load_unknown(Tcl_Interp *interp) ************************************************************/ Tcl_DStringInit(&unknown_src); - spi_rc = SPI_exec("select modseq, modsrc from pltcl_modules " - "where modname = 'unknown' " - "order by modseq", 0); + 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"); @@ -405,30 +415,46 @@ pltcl_call_handler(PG_FUNCTION_ARGS) { Datum retval; FunctionCallInfo save_fcinfo; + pltcl_proc_desc *save_prodesc; - /************************************************************ + /* * Initialize interpreters if first time through - ************************************************************/ + */ pltcl_init_all(); - /************************************************************ - * Determine if called as function or trigger and - * call appropriate subhandler - ************************************************************/ + /* + * Ensure that static pointers are saved/restored properly + */ save_fcinfo = pltcl_current_fcinfo; + save_prodesc = pltcl_current_prodesc; - if (CALLED_AS_TRIGGER(fcinfo)) + PG_TRY(); { - pltcl_current_fcinfo = NULL; - retval = PointerGetDatum(pltcl_trigger_handler(fcinfo)); + /* + * Determine if called as function or trigger and + * call appropriate subhandler + */ + if (CALLED_AS_TRIGGER(fcinfo)) + { + pltcl_current_fcinfo = NULL; + retval = PointerGetDatum(pltcl_trigger_handler(fcinfo)); + } + else + { + pltcl_current_fcinfo = fcinfo; + retval = pltcl_func_handler(fcinfo); + } } - else + PG_CATCH(); { - pltcl_current_fcinfo = fcinfo; - retval = pltcl_func_handler(fcinfo); + pltcl_current_fcinfo = save_fcinfo; + pltcl_current_prodesc = save_prodesc; + PG_RE_THROW(); } + PG_END_TRY(); pltcl_current_fcinfo = save_fcinfo; + pltcl_current_prodesc = save_prodesc; return retval; } @@ -467,6 +493,8 @@ pltcl_func_handler(PG_FUNCTION_ARGS) /* Find or compile the function */ prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, InvalidOid); + pltcl_current_prodesc = prodesc; + if (prodesc->lanpltrusted) interp = pltcl_safe_interp; else @@ -643,6 +671,8 @@ pltcl_trigger_handler(PG_FUNCTION_ARGS) prodesc = compile_pltcl_function(fcinfo->flinfo->fn_oid, RelationGetRelid(trigdata->tg_relation)); + pltcl_current_prodesc = prodesc; + if (prodesc->lanpltrusted) interp = pltcl_safe_interp; else @@ -1030,6 +1060,10 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid) prodesc->fn_xmin = HeapTupleHeaderGetXmin(procTup->t_data); prodesc->fn_cmin = HeapTupleHeaderGetCmin(procTup->t_data); + /* Remember if function is STABLE/IMMUTABLE */ + prodesc->fn_readonly = + (procStruct->provolatile != PROVOLATILE_VOLATILE); + /************************************************************ * Lookup the pg_language tuple by Oid ************************************************************/ @@ -1336,7 +1370,7 @@ pltcl_elog(ClientData cdata, Tcl_Interp *interp, /********************************************************************** * pltcl_quote() - quote literal strings that are to - * be used in SPI_exec query strings + * be used in SPI_execute query strings **********************************************************************/ static int pltcl_quote(ClientData cdata, Tcl_Interp *interp, @@ -1484,12 +1518,12 @@ pltcl_returnnull(ClientData cdata, Tcl_Interp *interp, /********************************************************************** - * pltcl_SPI_exec() - The builtin SPI_exec command + * pltcl_SPI_execute() - The builtin SPI_execute command * for the Tcl interpreter **********************************************************************/ static int -pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, - int argc, CONST84 char *argv[]) +pltcl_SPI_execute(ClientData cdata, Tcl_Interp *interp, + int argc, CONST84 char *argv[]) { volatile int my_rc; int spi_rc; @@ -1570,7 +1604,8 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, PG_TRY(); { UTF_BEGIN; - spi_rc = SPI_exec(UTF_U2E(argv[query_idx]), count); + spi_rc = SPI_execute(UTF_U2E(argv[query_idx]), + pltcl_current_prodesc->fn_readonly, count); UTF_END; } PG_CATCH(); @@ -1603,7 +1638,7 @@ pltcl_SPI_exec(ClientData cdata, Tcl_Interp *interp, break; default: - Tcl_AppendResult(interp, "pltcl: SPI_exec failed: ", + Tcl_AppendResult(interp, "pltcl: SPI_execute failed: ", SPI_result_code_string(spi_rc), NULL); SPI_freetuptable(SPI_tuptable); return TCL_ERROR; @@ -1840,11 +1875,11 @@ pltcl_SPI_prepare(ClientData cdata, Tcl_Interp *interp, /********************************************************************** - * pltcl_SPI_execp() - Execute a prepared plan + * pltcl_SPI_execute_plan() - Execute a prepared plan **********************************************************************/ static int -pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, - int argc, CONST84 char *argv[]) +pltcl_SPI_execute_plan(ClientData cdata, Tcl_Interp *interp, + int argc, CONST84 char *argv[]) { volatile int my_rc; int spi_rc; @@ -1992,7 +2027,7 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, } /************************************************************ - * Setup the value array for the SPI_execp() using + * Setup the value array for SPI_execute_plan() using * the type specific input functions ************************************************************/ oldcontext = CurrentMemoryContext; @@ -2046,7 +2081,10 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, ************************************************************/ oldcontext = CurrentMemoryContext; PG_TRY(); - spi_rc = SPI_execp(qdesc->plan, argvalues, nulls, count); + { + spi_rc = SPI_execute_plan(qdesc->plan, argvalues, nulls, + pltcl_current_prodesc->fn_readonly, count); + } PG_CATCH(); { MemoryContextSwitchTo(oldcontext); @@ -2058,7 +2096,7 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, PG_END_TRY(); /************************************************************ - * Check the return code from SPI_execp() + * Check the return code from SPI_execute_plan() ************************************************************/ switch (spi_rc) { @@ -2080,7 +2118,7 @@ pltcl_SPI_execp(ClientData cdata, Tcl_Interp *interp, break; default: - Tcl_AppendResult(interp, "pltcl: SPI_execp failed: ", + Tcl_AppendResult(interp, "pltcl: SPI_execute_plan failed: ", SPI_result_code_string(spi_rc), NULL); SPI_freetuptable(SPI_tuptable); return TCL_ERROR; |