1
0
mirror of https://github.com/postgres/postgres.git synced 2025-09-02 04:21:28 +03:00

Redesign query-snapshot timing so that volatile functions in READ COMMITTED

mode see a fresh snapshot for each command in the function, rather than
using the latest interactive command's snapshot.  Also, suppress fresh
snapshots as well as CommandCounterIncrement inside STABLE and IMMUTABLE
functions, instead using the snapshot taken for the most closely nested
regular query.  (This behavior is only sane for read-only functions, so
the patch also enforces that such functions contain only SELECT commands.)
As per my proposal of 6-Sep-2004; I note that I floated essentially the
same proposal on 19-Jun-2002, but that discussion tailed off without any
action.  Since 8.0 seems like the right place to be taking possibly
nontrivial backwards compatibility hits, let's get it done now.
This commit is contained in:
Tom Lane
2004-09-13 20:10:13 +00:00
parent d69528881a
commit b2c4071299
41 changed files with 1764 additions and 834 deletions

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -15,4 +15,5 @@ int spi_WARNING(void);
int spi_ERROR(void);
/* this is actually in plperl.c */
HV *plperl_spi_exec(char *, int);

View File

@@ -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.
*/

View File

@@ -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),

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;