aboutsummaryrefslogtreecommitdiff
path: root/src/backend/executor/spi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/executor/spi.c')
-rw-r--r--src/backend/executor/spi.c478
1 files changed, 266 insertions, 212 deletions
diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c
index 636eed31eee..6650da9b626 100644
--- a/src/backend/executor/spi.c
+++ b/src/backend/executor/spi.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.126 2004/09/10 18:39:57 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/executor/spi.c,v 1.127 2004/09/13 20:06:46 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -34,13 +34,14 @@ static int _SPI_stack_depth = 0; /* allocated size of _SPI_stack */
static int _SPI_connected = -1;
static int _SPI_curid = -1;
-static int _SPI_execute(const char *src, int tcount, _SPI_plan *plan);
-static int _SPI_pquery(QueryDesc *queryDesc, bool runit,
- bool useCurrentSnapshot, int tcount);
+static void _SPI_prepare_plan(const char *src, _SPI_plan *plan);
static int _SPI_execute_plan(_SPI_plan *plan,
- Datum *Values, const char *Nulls,
- bool useCurrentSnapshot, int tcount);
+ Datum *Values, const char *Nulls,
+ Snapshot snapshot, Snapshot crosscheck_snapshot,
+ bool read_only, int tcount);
+
+static int _SPI_pquery(QueryDesc *queryDesc, int tcount);
static void _SPI_error_callback(void *arg);
@@ -252,9 +253,11 @@ SPI_pop(void)
_SPI_curid--;
}
+/* Parse, plan, and execute a querystring */
int
-SPI_exec(const char *src, int tcount)
+SPI_execute(const char *src, bool read_only, int tcount)
{
+ _SPI_plan plan;
int res;
if (src == NULL || tcount < 0)
@@ -264,14 +267,32 @@ SPI_exec(const char *src, int tcount)
if (res < 0)
return res;
- res = _SPI_execute(src, tcount, NULL);
+ plan.plancxt = NULL; /* doesn't have own context */
+ plan.query = src;
+ plan.nargs = 0;
+ plan.argtypes = NULL;
+
+ _SPI_prepare_plan(src, &plan);
+
+ res = _SPI_execute_plan(&plan, NULL, NULL,
+ InvalidSnapshot, InvalidSnapshot,
+ read_only, tcount);
_SPI_end_call(true);
return res;
}
+/* Obsolete version of SPI_execute */
int
-SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
+SPI_exec(const char *src, int tcount)
+{
+ return SPI_execute(src, false, tcount);
+}
+
+/* Execute a previously prepared plan */
+int
+SPI_execute_plan(void *plan, Datum *Values, const char *Nulls,
+ bool read_only, int tcount)
{
int res;
@@ -285,21 +306,36 @@ SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
if (res < 0)
return res;
- res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls, false, tcount);
+ res = _SPI_execute_plan((_SPI_plan *) plan,
+ Values, Nulls,
+ InvalidSnapshot, InvalidSnapshot,
+ read_only, tcount);
_SPI_end_call(true);
return res;
}
+/* Obsolete version of SPI_execute_plan */
+int
+SPI_execp(void *plan, Datum *Values, const char *Nulls, int tcount)
+{
+ return SPI_execute_plan(plan, Values, Nulls, false, tcount);
+}
+
/*
- * SPI_execp_current -- identical to SPI_execp, except that we expose the
- * Executor option to use a current snapshot instead of the normal
- * QuerySnapshot. This is currently not documented in spi.sgml because
- * it is only intended for use by RI triggers.
+ * SPI_execute_snapshot -- identical to SPI_execute_plan, except that we allow
+ * the caller to specify exactly which snapshots to use. This is currently
+ * not documented in spi.sgml because it is only intended for use by RI
+ * triggers.
+ *
+ * Passing snapshot == InvalidSnapshot will select the normal behavior of
+ * fetching a new snapshot for each query.
*/
-int
-SPI_execp_current(void *plan, Datum *Values, const char *Nulls,
- bool useCurrentSnapshot, int tcount)
+extern int
+SPI_execute_snapshot(void *plan,
+ Datum *Values, const char *Nulls,
+ Snapshot snapshot, Snapshot crosscheck_snapshot,
+ bool read_only, int tcount)
{
int res;
@@ -313,8 +349,10 @@ SPI_execp_current(void *plan, Datum *Values, const char *Nulls,
if (res < 0)
return res;
- res = _SPI_execute_plan((_SPI_plan *) plan, Values, Nulls,
- useCurrentSnapshot, tcount);
+ res = _SPI_execute_plan((_SPI_plan *) plan,
+ Values, Nulls,
+ snapshot, crosscheck_snapshot,
+ read_only, tcount);
_SPI_end_call(true);
return res;
@@ -341,12 +379,10 @@ SPI_prepare(const char *src, int nargs, Oid *argtypes)
plan.nargs = nargs;
plan.argtypes = argtypes;
- SPI_result = _SPI_execute(src, 0, &plan);
+ _SPI_prepare_plan(src, &plan);
- if (SPI_result >= 0) /* copy plan to procedure context */
- result = _SPI_copy_plan(&plan, _SPI_CPLAN_PROCXT);
- else
- result = NULL;
+ /* copy plan to procedure context */
+ result = _SPI_copy_plan(&plan, _SPI_CPLAN_PROCXT);
_SPI_end_call(true);
@@ -756,7 +792,9 @@ SPI_freetuptable(SPITupleTable *tuptable)
* Open a prepared SPI plan as a portal
*/
Portal
-SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
+SPI_cursor_open(const char *name, void *plan,
+ Datum *Values, const char *Nulls,
+ bool read_only)
{
_SPI_plan *spiplan = (_SPI_plan *) plan;
List *qtlist = spiplan->qtlist;
@@ -764,6 +802,7 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
Query *queryTree;
Plan *planTree;
ParamListInfo paramLI;
+ Snapshot snapshot;
MemoryContext oldcontext;
Portal portal;
int k;
@@ -785,9 +824,6 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
(errcode(ERRCODE_INVALID_CURSOR_DEFINITION),
errmsg("cannot open SELECT INTO query as cursor")));
- /* Increment CommandCounter to see changes made by now */
- CommandCounterIncrement();
-
/* Reset SPI result */
SPI_processed = 0;
SPI_tuptable = NULL;
@@ -867,9 +903,21 @@ SPI_cursor_open(const char *name, void *plan, Datum *Values, const char *Nulls)
portal->cursorOptions |= CURSOR_OPT_NO_SCROLL;
/*
+ * Set up the snapshot to use. (PortalStart will do CopySnapshot,
+ * so we skip that here.)
+ */
+ if (read_only)
+ snapshot = ActiveSnapshot;
+ else
+ {
+ CommandCounterIncrement();
+ snapshot = GetTransactionSnapshot();
+ }
+
+ /*
* Start portal execution.
*/
- PortalStart(portal, paramLI);
+ PortalStart(portal, paramLI, snapshot);
Assert(portal->strategy == PORTAL_ONE_SELECT);
@@ -1143,38 +1191,31 @@ spi_printtup(HeapTuple tuple, TupleDesc tupdesc, DestReceiver *self)
*/
/*
- * Plan and optionally execute a querystring.
+ * Parse and plan a querystring.
+ *
+ * At entry, plan->argtypes and plan->nargs must be valid.
*
- * If plan != NULL, just prepare plan trees and save them in *plan;
- * else execute immediately.
+ * Query and plan lists are stored into *plan.
*/
-static int
-_SPI_execute(const char *src, int tcount, _SPI_plan *plan)
+static void
+_SPI_prepare_plan(const char *src, _SPI_plan *plan)
{
List *raw_parsetree_list;
List *query_list_list;
List *plan_list;
ListCell *list_item;
ErrorContextCallback spierrcontext;
- int nargs = 0;
- Oid *argtypes = NULL;
- int res = 0;
-
- if (plan)
- {
- nargs = plan->nargs;
- argtypes = plan->argtypes;
- }
+ Oid *argtypes = plan->argtypes;
+ int nargs = plan->nargs;
- /* Increment CommandCounter to see changes made by now */
+ /*
+ * Increment CommandCounter to see changes made by now. We must do
+ * this to be sure of seeing any schema changes made by a just-preceding
+ * SPI command. (But we don't bother advancing the snapshot, since the
+ * planner generally operates under SnapshotNow rules anyway.)
+ */
CommandCounterIncrement();
- /* Reset state (only needed in case string is empty) */
- SPI_processed = 0;
- SPI_lastoid = InvalidOid;
- SPI_tuptable = NULL;
- _SPI_current->tuptable = NULL;
-
/*
* Setup error traceback support for ereport()
*/
@@ -1191,9 +1232,9 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
/*
* Do parse analysis and rule rewrite for each raw parsetree.
*
- * We save the querytrees from each raw parsetree as a separate sublist.
- * This allows _SPI_execute_plan() to know where the boundaries
- * between original queries fall.
+ * We save the querytrees from each raw parsetree as a separate
+ * sublist. This allows _SPI_execute_plan() to know where the
+ * boundaries between original queries fall.
*/
query_list_list = NIL;
plan_list = NIL;
@@ -1202,203 +1243,221 @@ _SPI_execute(const char *src, int tcount, _SPI_plan *plan)
{
Node *parsetree = (Node *) lfirst(list_item);
List *query_list;
- ListCell *query_list_item;
query_list = pg_analyze_and_rewrite(parsetree, argtypes, nargs);
query_list_list = lappend(query_list_list, query_list);
- /* Reset state for each original parsetree */
- /* (at most one of its querytrees will be marked canSetTag) */
- SPI_processed = 0;
- SPI_lastoid = InvalidOid;
- SPI_tuptable = NULL;
- _SPI_current->tuptable = NULL;
-
- foreach(query_list_item, query_list)
- {
- Query *queryTree = (Query *) lfirst(query_list_item);
- Plan *planTree;
- QueryDesc *qdesc;
- DestReceiver *dest;
-
- planTree = pg_plan_query(queryTree, NULL);
- plan_list = lappend(plan_list, planTree);
-
- dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
- if (queryTree->commandType == CMD_UTILITY)
- {
- if (IsA(queryTree->utilityStmt, CopyStmt))
- {
- CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
-
- if (stmt->filename == NULL)
- {
- res = SPI_ERROR_COPY;
- goto fail;
- }
- }
- else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
- IsA(queryTree->utilityStmt, ClosePortalStmt) ||
- IsA(queryTree->utilityStmt, FetchStmt))
- {
- res = SPI_ERROR_CURSOR;
- goto fail;
- }
- else if (IsA(queryTree->utilityStmt, TransactionStmt))
- {
- res = SPI_ERROR_TRANSACTION;
- goto fail;
- }
- res = SPI_OK_UTILITY;
- if (plan == NULL)
- {
- ProcessUtility(queryTree->utilityStmt, NULL, dest, NULL);
- CommandCounterIncrement();
- }
- }
- else if (plan == NULL)
- {
- qdesc = CreateQueryDesc(queryTree, planTree, dest,
- NULL, false);
- res = _SPI_pquery(qdesc, true, false,
- queryTree->canSetTag ? tcount : 0);
- if (res < 0)
- goto fail;
- CommandCounterIncrement();
- }
- else
- {
- qdesc = CreateQueryDesc(queryTree, planTree, dest,
- NULL, false);
- res = _SPI_pquery(qdesc, false, false, 0);
- if (res < 0)
- goto fail;
- }
- }
+ plan_list = list_concat(plan_list,
+ pg_plan_queries(query_list, NULL, false));
}
- if (plan)
- {
- plan->qtlist = query_list_list;
- plan->ptlist = plan_list;
- }
-
-fail:
+ plan->qtlist = query_list_list;
+ plan->ptlist = plan_list;
/*
* Pop the error context stack
*/
error_context_stack = spierrcontext.previous;
-
- return res;
}
+/*
+ * Execute the given plan with the given parameter values
+ *
+ * snapshot: query snapshot to use, or InvalidSnapshot for the normal
+ * behavior of taking a new snapshot for each query.
+ * crosscheck_snapshot: for RI use, all others pass InvalidSnapshot
+ * read_only: TRUE for read-only execution (no CommandCounterIncrement)
+ * tcount: execution tuple-count limit, or 0 for none
+ */
static int
_SPI_execute_plan(_SPI_plan *plan, Datum *Values, const char *Nulls,
- bool useCurrentSnapshot, int tcount)
+ Snapshot snapshot, Snapshot crosscheck_snapshot,
+ bool read_only, int tcount)
{
- List *query_list_list = plan->qtlist;
- ListCell *plan_list_item = list_head(plan->ptlist);
- ListCell *query_list_list_item;
- ErrorContextCallback spierrcontext;
- int nargs = plan->nargs;
- int res = 0;
- ParamListInfo paramLI;
-
- /* Increment CommandCounter to see changes made by now */
- CommandCounterIncrement();
+ volatile int res = 0;
+ Snapshot saveActiveSnapshot;
- /* Convert parameters to form wanted by executor */
- if (nargs > 0)
+ /* Be sure to restore ActiveSnapshot on error exit */
+ saveActiveSnapshot = ActiveSnapshot;
+ PG_TRY();
{
- int k;
-
- paramLI = (ParamListInfo)
- palloc0((nargs + 1) * sizeof(ParamListInfoData));
-
- for (k = 0; k < nargs; k++)
+ List *query_list_list = plan->qtlist;
+ ListCell *plan_list_item = list_head(plan->ptlist);
+ ListCell *query_list_list_item;
+ ErrorContextCallback spierrcontext;
+ int nargs = plan->nargs;
+ ParamListInfo paramLI;
+
+ /* Convert parameters to form wanted by executor */
+ if (nargs > 0)
{
- paramLI[k].kind = PARAM_NUM;
- paramLI[k].id = k + 1;
- paramLI[k].ptype = plan->argtypes[k];
- paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
- paramLI[k].value = Values[k];
- }
- paramLI[k].kind = PARAM_INVALID;
- }
- else
- paramLI = NULL;
+ int k;
- /* Reset state (only needed in case string is empty) */
- SPI_processed = 0;
- SPI_lastoid = InvalidOid;
- SPI_tuptable = NULL;
- _SPI_current->tuptable = NULL;
-
- /*
- * Setup error traceback support for ereport()
- */
- spierrcontext.callback = _SPI_error_callback;
- spierrcontext.arg = (void *) plan->query;
- spierrcontext.previous = error_context_stack;
- error_context_stack = &spierrcontext;
+ paramLI = (ParamListInfo)
+ palloc0((nargs + 1) * sizeof(ParamListInfoData));
- foreach(query_list_list_item, query_list_list)
- {
- List *query_list = lfirst(query_list_list_item);
- ListCell *query_list_item;
+ for (k = 0; k < nargs; k++)
+ {
+ paramLI[k].kind = PARAM_NUM;
+ paramLI[k].id = k + 1;
+ paramLI[k].ptype = plan->argtypes[k];
+ paramLI[k].isnull = (Nulls && Nulls[k] == 'n');
+ paramLI[k].value = Values[k];
+ }
+ paramLI[k].kind = PARAM_INVALID;
+ }
+ else
+ paramLI = NULL;
- /* Reset state for each original parsetree */
- /* (at most one of its querytrees will be marked canSetTag) */
+ /* Reset state (only needed in case string is empty) */
SPI_processed = 0;
SPI_lastoid = InvalidOid;
SPI_tuptable = NULL;
_SPI_current->tuptable = NULL;
- foreach(query_list_item, query_list)
+ /*
+ * Setup error traceback support for ereport()
+ */
+ spierrcontext.callback = _SPI_error_callback;
+ spierrcontext.arg = (void *) plan->query;
+ spierrcontext.previous = error_context_stack;
+ error_context_stack = &spierrcontext;
+
+ foreach(query_list_list_item, query_list_list)
{
- Query *queryTree = (Query *) lfirst(query_list_item);
- Plan *planTree;
- QueryDesc *qdesc;
- DestReceiver *dest;
+ List *query_list = lfirst(query_list_list_item);
+ ListCell *query_list_item;
- planTree = lfirst(plan_list_item);
- plan_list_item = lnext(plan_list_item);
+ /* Reset state for each original parsetree */
+ /* (at most one of its querytrees will be marked canSetTag) */
+ SPI_processed = 0;
+ SPI_lastoid = InvalidOid;
+ SPI_tuptable = NULL;
+ _SPI_current->tuptable = NULL;
- dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None, NULL);
- if (queryTree->commandType == CMD_UTILITY)
- {
- ProcessUtility(queryTree->utilityStmt, paramLI, dest, NULL);
- res = SPI_OK_UTILITY;
- CommandCounterIncrement();
- }
- else
+ foreach(query_list_item, query_list)
{
- qdesc = CreateQueryDesc(queryTree, planTree, dest,
- paramLI, false);
- res = _SPI_pquery(qdesc, true, useCurrentSnapshot,
- queryTree->canSetTag ? tcount : 0);
+ Query *queryTree = (Query *) lfirst(query_list_item);
+ Plan *planTree;
+ QueryDesc *qdesc;
+ DestReceiver *dest;
+
+ planTree = lfirst(plan_list_item);
+ plan_list_item = lnext(plan_list_item);
+
+ if (queryTree->commandType == CMD_UTILITY)
+ {
+ if (IsA(queryTree->utilityStmt, CopyStmt))
+ {
+ CopyStmt *stmt = (CopyStmt *) queryTree->utilityStmt;
+
+ if (stmt->filename == NULL)
+ {
+ res = SPI_ERROR_COPY;
+ goto fail;
+ }
+ }
+ else if (IsA(queryTree->utilityStmt, DeclareCursorStmt) ||
+ IsA(queryTree->utilityStmt, ClosePortalStmt) ||
+ IsA(queryTree->utilityStmt, FetchStmt))
+ {
+ res = SPI_ERROR_CURSOR;
+ goto fail;
+ }
+ else if (IsA(queryTree->utilityStmt, TransactionStmt))
+ {
+ res = SPI_ERROR_TRANSACTION;
+ goto fail;
+ }
+ }
+
+ if (read_only && !QueryIsReadOnly(queryTree))
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ /* translator: %s is a SQL statement name */
+ errmsg("%s is not allowed in a non-volatile function",
+ CreateQueryTag(queryTree))));
+ /*
+ * If not read-only mode, advance the command counter before
+ * each command.
+ */
+ if (!read_only)
+ CommandCounterIncrement();
+
+ dest = CreateDestReceiver(queryTree->canSetTag ? SPI : None,
+ NULL);
+
+ if (snapshot == InvalidSnapshot)
+ {
+ /*
+ * Default read_only behavior is to use the entry-time
+ * ActiveSnapshot; if read-write, grab a full new snap.
+ */
+ if (read_only)
+ ActiveSnapshot = CopySnapshot(saveActiveSnapshot);
+ else
+ ActiveSnapshot = CopySnapshot(GetTransactionSnapshot());
+ }
+ else
+ {
+ /*
+ * We interpret read_only with a specified snapshot to be
+ * exactly that snapshot, but read-write means use the
+ * snap with advancing of command ID.
+ */
+ ActiveSnapshot = CopySnapshot(snapshot);
+ if (!read_only)
+ ActiveSnapshot->curcid = GetCurrentCommandId();
+ }
+
+ if (queryTree->commandType == CMD_UTILITY)
+ {
+ ProcessUtility(queryTree->utilityStmt, paramLI,
+ dest, NULL);
+ res = SPI_OK_UTILITY;
+ }
+ else
+ {
+ qdesc = CreateQueryDesc(queryTree, planTree,
+ ActiveSnapshot,
+ crosscheck_snapshot,
+ dest,
+ paramLI, false);
+ res = _SPI_pquery(qdesc,
+ queryTree->canSetTag ? tcount : 0);
+ FreeQueryDesc(qdesc);
+ }
+ FreeSnapshot(ActiveSnapshot);
+ ActiveSnapshot = NULL;
+ /* we know that the receiver doesn't need a destroy call */
if (res < 0)
goto fail;
- CommandCounterIncrement();
}
}
- }
fail:
- /*
- * Pop the error context stack
- */
- error_context_stack = spierrcontext.previous;
+ /*
+ * Pop the error context stack
+ */
+ error_context_stack = spierrcontext.previous;
+ }
+ PG_CATCH();
+ {
+ /* Restore global vars and propagate error */
+ ActiveSnapshot = saveActiveSnapshot;
+ PG_RE_THROW();
+ }
+ PG_END_TRY();
+
+ ActiveSnapshot = saveActiveSnapshot;
return res;
}
static int
-_SPI_pquery(QueryDesc *queryDesc, bool runit,
- bool useCurrentSnapshot, int tcount)
+_SPI_pquery(QueryDesc *queryDesc, int tcount)
{
int operation = queryDesc->operation;
int res;
@@ -1427,9 +1486,6 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit,
return SPI_ERROR_OPUNKNOWN;
}
- if (!runit) /* plan preparation, don't execute */
- return res;
-
#ifdef SPI_EXECUTOR_STATS
if (ShowExecutorStats)
ResetUsage();
@@ -1437,7 +1493,7 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit,
AfterTriggerBeginQuery();
- ExecutorStart(queryDesc, useCurrentSnapshot, false);
+ ExecutorStart(queryDesc, false);
ExecutorRun(queryDesc, ForwardScanDirection, (long) tcount);
@@ -1467,8 +1523,6 @@ _SPI_pquery(QueryDesc *queryDesc, bool runit,
res = SPI_OK_UTILITY;
}
- FreeQueryDesc(queryDesc);
-
#ifdef SPI_EXECUTOR_STATS
if (ShowExecutorStats)
ShowUsage("SPI EXECUTOR STATS");