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