*** a/doc/src/sgml/spi.sgml
--- b/doc/src/sgml/spi.sgml
***************
*** 2741,2746 **** SPIPlanPtr SPI_saveplan(SPIPlanPtr plan)
--- 2741,3025 ----
+
+
+
+ SPI_register_tuplestore
+
+
+ SPI_register_tuplestore
+ 3
+
+
+
+ SPI_register_tuplestore
+ make a tuplestore available by name in SPI queries
+
+
+
+
+ int SPI_register_tuplestore(Tsr tsr)
+
+
+
+
+ Description
+
+
+ SPI_register_tuplestore makes a tuplestore, with
+ associated information, available to queries planned and executed through
+ the current SPI connection.
+
+
+
+
+ Arguments
+
+
+
+ Tsr tsr
+
+
+ the tuplestore registry entry
+
+
+
+
+
+
+
+ Return Value
+
+
+ If the execution of the command was successful then the following
+ (nonnegative) value will be returned:
+
+
+
+ SPI_OK_TSR_REGISTER
+
+
+ if the tuplestore has been successfully registered by name
+
+
+
+
+
+
+
+ On error, one of the following negative values is returned:
+
+
+
+ SPI_ERROR_ARGUMENT
+
+
+ if tsr is NULL or its
+ name field is NULL
+
+
+
+
+
+ SPI_ERROR_UNCONNECTED
+
+
+ if called from an unconnected procedure
+
+
+
+
+
+ SPI_ERROR_TSR_DUPLICATE
+
+
+ if the name specified in the name field of
+ tsr is already registered for this connection
+
+
+
+
+
+
+
+
+
+
+
+ SPI_unregister_tuplestore
+
+
+ SPI_unregister_tuplestore
+ 3
+
+
+
+ SPI_unregister_tuplestore
+ remove a tuplestore from the registry
+
+
+
+
+ int SPI_unregister_tuplestore(const char * name)
+
+
+
+
+ Description
+
+
+ SPI_unregister_tuplestore removes a tuplestore from
+ the registry for the current connection.
+
+
+
+
+ Arguments
+
+
+
+ const char * name
+
+
+ the tuplestore registry entry name
+
+
+
+
+
+
+
+ Return Value
+
+
+ If the execution of the command was successful then the following
+ (nonnegative) value will be returned:
+
+
+
+ SPI_OK_TSR_UNREGISTER
+
+
+ if the tuplestore has been successfully removed from the registry
+
+
+
+
+
+
+
+ On error, one of the following negative values is returned:
+
+
+
+ SPI_ERROR_ARGUMENT
+
+
+ if name is NULL
+
+
+
+
+
+ SPI_ERROR_UNCONNECTED
+
+
+ if called from an unconnected procedure
+
+
+
+
+
+ SPI_ERROR_TSR_NOT_FOUND
+
+
+ if name is not found in the registry for the
+ current connection
+
+
+
+
+
+
+
+
+
+
+
+ SPI_get_caller_tuplestore
+
+
+ SPI_get_caller_tuplestore
+ 3
+
+
+
+ SPI_get_caller_tuplestore
+ return a tuplestore from the registry
+
+
+
+
+ int SPI_get_caller_tuplestore(const char * name)
+
+
+
+
+ Description
+
+
+ SPI_get_caller_tuplestore returns a tuplestore from
+ the registry for the current connection.
+
+
+
+
+ Arguments
+
+
+
+ const char * name
+
+
+ the tuplestore registry entry name
+
+
+
+
+
+
+
+ Return Value
+
+
+ Pointer to the tuplestore registry entry; or NULL if
+ unsuccessful. On error, SPI_result is set thus:
+
+
+
+ SPI_ERROR_ARGUMENT
+
+
+ if name is NULL
+
+
+
+
+
+ SPI_ERROR_UNCONNECTED
+
+
+ if called from an unconnected procedure
+
+
+
+
+
+
+
+
+
+
*** a/src/backend/executor/spi.c
--- b/src/backend/executor/spi.c
***************
*** 130,135 **** SPI_connect(void)
--- 130,136 ----
_SPI_current->procCxt = NULL; /* in case we fail to create 'em */
_SPI_current->execCxt = NULL;
_SPI_current->connectSubid = GetCurrentSubTransactionId();
+ _SPI_current->tuplestores = NIL;
/*
* Create memory contexts for this procedure
***************
*** 1591,1596 **** SPI_result_code_string(int code)
--- 1592,1601 ----
return "SPI_ERROR_NOOUTFUNC";
case SPI_ERROR_TYPUNKNOWN:
return "SPI_ERROR_TYPUNKNOWN";
+ case SPI_ERROR_TSR_DUPLICATE:
+ return "SPI_ERROR_TSR_DUPLICATE";
+ case SPI_ERROR_TSR_NOT_FOUND:
+ return "SPI_ERROR_TSR_NOT_FOUND";
case SPI_OK_CONNECT:
return "SPI_OK_CONNECT";
case SPI_OK_FINISH:
***************
*** 1619,1624 **** SPI_result_code_string(int code)
--- 1624,1633 ----
return "SPI_OK_UPDATE_RETURNING";
case SPI_OK_REWRITTEN:
return "SPI_OK_REWRITTEN";
+ case SPI_OK_TSR_REGISTER:
+ return "SPI_OK_TSR_REGISTER";
+ case SPI_OK_TSR_UNREGISTER:
+ return "SPI_OK_TSR_UNREGISTER";
}
/* Unrecognized code ... return something useful ... */
sprintf(buf, "Unrecognized SPI code %d", code);
***************
*** 2694,2696 **** _SPI_save_plan(SPIPlanPtr plan)
--- 2703,2838 ----
return newplan;
}
+
+ /*
+ * Internal lookup of Tsr by name.
+ */
+ static Tsr
+ _SPI_find_tsr_by_name(const char *name)
+ {
+ Tsr match = NULL;
+ ListCell *lc;
+
+ /* internal static function; any error is bug in SPI itself */
+ Assert(name != NULL);
+ Assert(_SPI_curid >= 0);
+ Assert(_SPI_curid == _SPI_connected);
+ Assert(_SPI_current == &(_SPI_stack[_SPI_curid]));
+
+ foreach(lc, _SPI_current->tuplestores)
+ {
+ Tsr tsr = (Tsr) lfirst(lc);
+
+ if (strcmp(tsr->name, name) == 0)
+ {
+ match = tsr;
+ break;
+ }
+ }
+
+ return match;
+ }
+
+ /*
+ * Register a named tuplestore for use by the planner and executor on
+ * subsequent calls using this SPI connection.
+ */
+ int
+ SPI_register_tuplestore(Tsr tsr)
+ {
+ Tsr match;
+ int res;
+
+ if (tsr == NULL || tsr->name == NULL)
+ return SPI_ERROR_ARGUMENT;
+
+ res = _SPI_begin_call(false); /* keep current memory context */
+ if (res < 0)
+ return res;
+
+ match = _SPI_find_tsr_by_name(tsr->name);
+ if (match)
+ res = SPI_ERROR_TSR_DUPLICATE;
+ else
+ {
+ _SPI_current->tuplestores = lappend(_SPI_current->tuplestores, tsr);
+ res = SPI_OK_TSR_REGISTER;
+ }
+
+ _SPI_end_call(false);
+
+ return res;
+ }
+
+ /*
+ * Unregister a named tuplestore by name. This will probably be a rarely used
+ * function, since SPI_finish will clear it automatically.
+ */
+ int
+ SPI_unregister_tuplestore(const char *name)
+ {
+ Tsr match;
+ int res;
+
+ if (name == NULL)
+ return SPI_ERROR_ARGUMENT;
+
+ res = _SPI_begin_call(false); /* keep current memory context */
+ if (res < 0)
+ return res;
+
+ match = _SPI_find_tsr_by_name(name);
+ if (match)
+ {
+ _SPI_current->tuplestores = list_delete(_SPI_current->tuplestores,
+ match);
+ res = SPI_OK_TSR_UNREGISTER;
+ }
+ else
+ res = SPI_ERROR_TSR_NOT_FOUND;
+
+ _SPI_end_call(false);
+
+ return res;
+ }
+
+ /*
+ * This returns a Tsr if there is a name match at the caller level. It must
+ * quietly return NULL if there is no SPI caller or if no match is found.
+ *
+ * Normally a tuplestore is expected to be used on a specific depth of SPI
+ * connection, but we tolerate a call if the next level has been opened in
+ * case someone wants to pass through the Tsr.
+ */
+ Tsr
+ SPI_get_caller_tuplestore(const char *name)
+ {
+ Tsr match;
+ ListCell *lc;
+
+ if (name == NULL)
+ {
+ SPI_result = SPI_ERROR_ARGUMENT;
+ return NULL;
+ }
+
+ if (_SPI_curid < 0)
+ {
+ SPI_result = SPI_ERROR_UNCONNECTED;
+ return NULL;
+ }
+
+ match = NULL;
+ foreach(lc, _SPI_stack[_SPI_curid].tuplestores)
+ {
+ Tsr tsr = (Tsr) lfirst(lc);
+
+ if (strcmp(tsr->name, name) == 0)
+ {
+ match = tsr;
+ break;
+ }
+ }
+
+ return match;
+ }
*** a/src/include/executor/spi.h
--- b/src/include/executor/spi.h
***************
*** 43,48 **** typedef struct _SPI_plan *SPIPlanPtr;
--- 43,50 ----
#define SPI_ERROR_NOATTRIBUTE (-9)
#define SPI_ERROR_NOOUTFUNC (-10)
#define SPI_ERROR_TYPUNKNOWN (-11)
+ #define SPI_ERROR_TSR_DUPLICATE (-12)
+ #define SPI_ERROR_TSR_NOT_FOUND (-13)
#define SPI_OK_CONNECT 1
#define SPI_OK_FINISH 2
***************
*** 58,63 **** typedef struct _SPI_plan *SPIPlanPtr;
--- 60,67 ----
#define SPI_OK_DELETE_RETURNING 12
#define SPI_OK_UPDATE_RETURNING 13
#define SPI_OK_REWRITTEN 14
+ #define SPI_OK_TSR_REGISTER 15
+ #define SPI_OK_TSR_UNREGISTER 16
extern PGDLLIMPORT uint32 SPI_processed;
extern PGDLLIMPORT Oid SPI_lastoid;
***************
*** 143,148 **** extern void SPI_scroll_cursor_fetch(Portal, FetchDirection direction, long count
--- 147,156 ----
extern void SPI_scroll_cursor_move(Portal, FetchDirection direction, long count);
extern void SPI_cursor_close(Portal portal);
+ extern int SPI_register_tuplestore(Tsr tsr);
+ extern int SPI_unregister_tuplestore(const char *name);
+ extern Tsr SPI_get_caller_tuplestore(const char *name);
+
extern void AtEOXact_SPI(bool isCommit);
extern void AtEOSubXact_SPI(bool isCommit, SubTransactionId mySubid);
*** a/src/include/executor/spi_priv.h
--- b/src/include/executor/spi_priv.h
***************
*** 31,36 **** typedef struct
--- 31,37 ----
MemoryContext execCxt; /* executor context */
MemoryContext savedcxt; /* context of SPI_connect's caller */
SubTransactionId connectSubid; /* ID of connecting subtransaction */
+ List *tuplestores; /* registered named tuplestores */
} _SPI_connection;
/*
*** a/src/include/utils/tuplestore.h
--- b/src/include/utils/tuplestore.h
***************
*** 40,45 ****
--- 40,60 ----
typedef struct Tuplestorestate Tuplestorestate;
/*
+ * Tuplestore Relation data; used for parsing named tuplestores, like
+ * transition tables in AFTER triggers.
+ */
+ typedef struct TsrData
+ {
+ char *name; /* name used to identify the tuplestore */
+ Tuplestorestate *tstate; /* data (or tids) */
+ TupleDesc tupdesc; /* description of result rows */
+ Oid relid; /* rel to use for statistics, if any */
+ } TsrData;
+
+ typedef TsrData *Tsr;
+
+
+ /*
* Currently we only need to store MinimalTuples, but it would be easy
* to support the same behavior for IndexTuples and/or bare Datums.
*/