aboutsummaryrefslogtreecommitdiff
path: root/src/pl
diff options
context:
space:
mode:
Diffstat (limited to 'src/pl')
-rw-r--r--src/pl/plperl/plperl.c153
-rw-r--r--src/pl/plperl/spi_internal.c85
-rw-r--r--src/pl/plperl/spi_internal.h1
-rw-r--r--src/pl/plpgsql/src/pl_comp.c5
-rw-r--r--src/pl/plpgsql/src/pl_exec.c52
-rw-r--r--src/pl/plpgsql/src/plpgsql.h5
-rw-r--r--src/pl/plpython/plpython.c50
-rw-r--r--src/pl/tcl/pltcl.c110
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;