From a9545b3aef0d41fdb84bc6a30fa2e563020acad2 Mon Sep 17 00:00:00 2001 From: Tom Lane Date: Mon, 11 Jun 2007 22:22:42 +0000 Subject: Improve UPDATE/DELETE WHERE CURRENT OF so that they can be used from plpgsql with a plpgsql-defined cursor. The underlying mechanism for this is that the main SQL engine will now take "WHERE CURRENT OF $n" where $n is a refcursor parameter. Not sure if we should document that fact or consider it an implementation detail. Per discussion with Pavel Stehule. --- src/backend/executor/execCurrent.c | 74 ++++++++++++++++++++++++++++++++------ 1 file changed, 63 insertions(+), 11 deletions(-) (limited to 'src/backend/executor/execCurrent.c') diff --git a/src/backend/executor/execCurrent.c b/src/backend/executor/execCurrent.c index ce95d58b81b..72bccd4438e 100644 --- a/src/backend/executor/execCurrent.c +++ b/src/backend/executor/execCurrent.c @@ -6,26 +6,29 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.1 2007/06/11 01:16:22 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/execCurrent.c,v 1.2 2007/06/11 22:22:40 tgl Exp $ * *------------------------------------------------------------------------- */ #include "postgres.h" +#include "catalog/pg_type.h" #include "executor/executor.h" +#include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/portal.h" +static char *fetch_param_value(ExprContext *econtext, int paramId); static ScanState *search_plan_tree(PlanState *node, Oid table_oid); /* * execCurrentOf * - * Given the name of a cursor and the OID of a table, determine which row - * of the table is currently being scanned by the cursor, and return its - * TID into *current_tid. + * Given a CURRENT OF expression and the OID of a table, determine which row + * of the table is currently being scanned by the cursor named by CURRENT OF, + * and return the row's TID into *current_tid. * * Returns TRUE if a row was identified. Returns FALSE if the cursor is valid * for the table but is not currently scanning a row of the table (this is a @@ -33,14 +36,25 @@ static ScanState *search_plan_tree(PlanState *node, Oid table_oid); * valid updatable scan of the specified table. */ bool -execCurrentOf(char *cursor_name, Oid table_oid, +execCurrentOf(CurrentOfExpr *cexpr, + ExprContext *econtext, + Oid table_oid, ItemPointer current_tid) { + char *cursor_name; char *table_name; Portal portal; QueryDesc *queryDesc; ScanState *scanstate; - HeapTuple tup; + bool lisnull; + Oid tuple_tableoid; + ItemPointer tuple_tid; + + /* Get the cursor name --- may have to look up a parameter reference */ + if (cexpr->cursor_name) + cursor_name = cexpr->cursor_name; + else + cursor_name = fetch_param_value(econtext, cexpr->cursor_param); /* Fetch table name for possible use in error messages */ table_name = get_rel_name(table_oid); @@ -100,16 +114,54 @@ execCurrentOf(char *cursor_name, Oid table_oid, if (TupIsNull(scanstate->ss_ScanTupleSlot)) return false; - tup = scanstate->ss_ScanTupleSlot->tts_tuple; - if (tup == NULL) - elog(ERROR, "CURRENT OF applied to non-materialized tuple"); - Assert(tup->t_tableOid == table_oid); + /* Use slot_getattr to catch any possible mistakes */ + tuple_tableoid = DatumGetObjectId(slot_getattr(scanstate->ss_ScanTupleSlot, + TableOidAttributeNumber, + &lisnull)); + Assert(!lisnull); + tuple_tid = (ItemPointer) + DatumGetPointer(slot_getattr(scanstate->ss_ScanTupleSlot, + SelfItemPointerAttributeNumber, + &lisnull)); + Assert(!lisnull); + + Assert(tuple_tableoid == table_oid); - *current_tid = tup->t_self; + *current_tid = *tuple_tid; return true; } +/* + * fetch_param_value + * + * Fetch the string value of a param, verifying it is of type REFCURSOR. + */ +static char * +fetch_param_value(ExprContext *econtext, int paramId) +{ + ParamListInfo paramInfo = econtext->ecxt_param_list_info; + + if (paramInfo && + paramId > 0 && paramId <= paramInfo->numParams) + { + ParamExternData *prm = ¶mInfo->params[paramId - 1]; + + if (OidIsValid(prm->ptype) && !prm->isnull) + { + Assert(prm->ptype == REFCURSOROID); + /* We know that refcursor uses text's I/O routines */ + return DatumGetCString(DirectFunctionCall1(textout, + prm->value)); + } + } + + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("no value found for parameter %d", paramId))); + return NULL; +} + /* * search_plan_tree * -- cgit v1.2.3