mirror of
https://github.com/postgres/postgres.git
synced 2025-07-28 23:42:10 +03:00
Add a new system view, pg_cursors, that displays the currently available
cursors. Patch from Joachim Wieland, review and ediorialization by Neil Conway. The view lists cursors defined by DECLARE CURSOR, using SPI, or via the Bind message of the frontend/backend protocol. This means the view does not list the unnamed portal or the portal created to implement EXECUTE. Because we do list SPI portals, there might be more rows in this view than you might expect if you are using SPI implicitly (e.g. via a procedural language). Per recent discussion on -hackers, the query string included in the view for cursors defined by DECLARE CURSOR is based on debug_query_string. That means it is not accurate if multiple queries separated by semicolons are submitted as one query string. However, there doesn't seem a trivial fix for that: debug_query_string is better than nothing. I also changed SPI_cursor_open() to include the source text for the portal it creates: AFAICS there is no reason not to do this. Update the documentation and regression tests, bump the catversion.
This commit is contained in:
@ -12,15 +12,19 @@
|
||||
* Portions Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.83 2005/11/22 18:17:27 momjian Exp $
|
||||
* $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.84 2006/01/18 06:49:27 neilc Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include "postgres.h"
|
||||
|
||||
#include "miscadmin.h"
|
||||
#include "access/heapam.h"
|
||||
#include "catalog/pg_type.h"
|
||||
#include "commands/portalcmds.h"
|
||||
#include "executor/executor.h"
|
||||
#include "funcapi.h"
|
||||
#include "miscadmin.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/hsearch.h"
|
||||
#include "utils/memutils.h"
|
||||
#include "utils/portal.h"
|
||||
@ -56,8 +60,8 @@ do { \
|
||||
\
|
||||
MemSet(key, 0, MAX_PORTALNAME_LEN); \
|
||||
StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
|
||||
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
|
||||
key, HASH_FIND, NULL); \
|
||||
hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
|
||||
key, HASH_FIND, NULL); \
|
||||
if (hentry) \
|
||||
PORTAL = hentry->portal; \
|
||||
else \
|
||||
@ -70,8 +74,8 @@ do { \
|
||||
\
|
||||
MemSet(key, 0, MAX_PORTALNAME_LEN); \
|
||||
StrNCpy(key, NAME, MAX_PORTALNAME_LEN); \
|
||||
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
|
||||
key, HASH_ENTER, &found); \
|
||||
hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
|
||||
key, HASH_ENTER, &found); \
|
||||
if (found) \
|
||||
elog(ERROR, "duplicate portal name"); \
|
||||
hentry->portal = PORTAL; \
|
||||
@ -85,8 +89,8 @@ do { \
|
||||
\
|
||||
MemSet(key, 0, MAX_PORTALNAME_LEN); \
|
||||
StrNCpy(key, PORTAL->name, MAX_PORTALNAME_LEN); \
|
||||
hentry = (PortalHashEnt*)hash_search(PortalHashTable, \
|
||||
key, HASH_REMOVE, NULL); \
|
||||
hentry = (PortalHashEnt *) hash_search(PortalHashTable, \
|
||||
key, HASH_REMOVE, NULL); \
|
||||
if (hentry == NULL) \
|
||||
elog(WARNING, "trying to delete portal name that does not exist"); \
|
||||
} while(0)
|
||||
@ -190,12 +194,15 @@ CreatePortal(const char *name, bool allowDup, bool dupSilent)
|
||||
"Portal");
|
||||
|
||||
/* initialize portal fields that don't start off zero */
|
||||
portal->status = PORTAL_NEW;
|
||||
portal->cleanup = PortalCleanup;
|
||||
portal->createSubid = GetCurrentSubTransactionId();
|
||||
portal->strategy = PORTAL_MULTI_QUERY;
|
||||
portal->cursorOptions = CURSOR_OPT_NO_SCROLL;
|
||||
portal->atStart = true;
|
||||
portal->atEnd = true; /* disallow fetches until query is set */
|
||||
portal->visible = true;
|
||||
portal->creation_time = GetCurrentTimestamp();
|
||||
|
||||
/* put portal in table (sets portal->name) */
|
||||
PortalHashTableInsert(portal, name);
|
||||
@ -756,3 +763,103 @@ AtSubCleanup_Portals(SubTransactionId mySubid)
|
||||
PortalDrop(portal, false);
|
||||
}
|
||||
}
|
||||
|
||||
/* Find all available cursors */
|
||||
Datum
|
||||
pg_cursor(PG_FUNCTION_ARGS)
|
||||
{
|
||||
FuncCallContext *funcctx;
|
||||
HASH_SEQ_STATUS *hash_seq;
|
||||
PortalHashEnt *hentry;
|
||||
|
||||
/* stuff done only on the first call of the function */
|
||||
if (SRF_IS_FIRSTCALL())
|
||||
{
|
||||
MemoryContext oldcontext;
|
||||
TupleDesc tupdesc;
|
||||
|
||||
/* create a function context for cross-call persistence */
|
||||
funcctx = SRF_FIRSTCALL_INIT();
|
||||
|
||||
/*
|
||||
* switch to memory context appropriate for multiple function
|
||||
* calls
|
||||
*/
|
||||
oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
|
||||
|
||||
if (PortalHashTable)
|
||||
{
|
||||
hash_seq = (HASH_SEQ_STATUS *) palloc(sizeof(HASH_SEQ_STATUS));
|
||||
hash_seq_init(hash_seq, PortalHashTable);
|
||||
funcctx->user_fctx = (void *) hash_seq;
|
||||
}
|
||||
else
|
||||
funcctx->user_fctx = NULL;
|
||||
|
||||
/*
|
||||
* build tupdesc for result tuples. This must match the
|
||||
* definition of the pg_cursors view in system_views.sql
|
||||
*/
|
||||
tupdesc = CreateTemplateTupleDesc(6, false);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 1, "name",
|
||||
TEXTOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 2, "statement",
|
||||
TEXTOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 3, "is_holdable",
|
||||
BOOLOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 4, "is_binary",
|
||||
BOOLOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 5, "is_scrollable",
|
||||
BOOLOID, -1, 0);
|
||||
TupleDescInitEntry(tupdesc, (AttrNumber) 6, "creation_time",
|
||||
TIMESTAMPTZOID, -1, 0);
|
||||
|
||||
funcctx->tuple_desc = BlessTupleDesc(tupdesc);
|
||||
MemoryContextSwitchTo(oldcontext);
|
||||
}
|
||||
|
||||
/* stuff done on every call of the function */
|
||||
funcctx = SRF_PERCALL_SETUP();
|
||||
hash_seq = (HASH_SEQ_STATUS *) funcctx->user_fctx;
|
||||
|
||||
/* if the hash table is uninitialized, we're done */
|
||||
if (hash_seq == NULL)
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
|
||||
/* loop until we find a visible portal or hit the end of the list */
|
||||
while ((hentry = hash_seq_search(hash_seq)) != NULL)
|
||||
{
|
||||
if (hentry->portal->visible)
|
||||
break;
|
||||
}
|
||||
|
||||
if (hentry)
|
||||
{
|
||||
Portal portal;
|
||||
Datum result;
|
||||
HeapTuple tuple;
|
||||
Datum values[6];
|
||||
bool nulls[6];
|
||||
|
||||
portal = hentry->portal;
|
||||
MemSet(nulls, 0, sizeof(nulls));
|
||||
|
||||
values[0] = DirectFunctionCall1(textin, CStringGetDatum(portal->name));
|
||||
if (!portal->sourceText)
|
||||
nulls[1] = true;
|
||||
else
|
||||
values[1] = DirectFunctionCall1(textin,
|
||||
CStringGetDatum(portal->sourceText));
|
||||
values[2] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_HOLD);
|
||||
values[3] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_BINARY);
|
||||
values[4] = BoolGetDatum(portal->cursorOptions & CURSOR_OPT_SCROLL);
|
||||
values[5] = TimestampTzGetDatum(portal->creation_time);
|
||||
|
||||
tuple = heap_form_tuple(funcctx->tuple_desc, values, nulls);
|
||||
result = HeapTupleGetDatum(tuple);
|
||||
SRF_RETURN_NEXT(funcctx, result);
|
||||
}
|
||||
|
||||
SRF_RETURN_DONE(funcctx);
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user