portalmem.c

来自「postgresql8.3.4源码,开源数据库」· C语言 代码 · 共 960 行 · 第 1/2 页

C
960
字号
/*------------------------------------------------------------------------- * * portalmem.c *	  backend portal memory management * * Portals are objects representing the execution state of a query. * This module provides memory management services for portals, but it * doesn't actually run the executor for them. * * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION *	  $PostgreSQL: pgsql/src/backend/utils/mmgr/portalmem.c,v 1.106.2.1 2008/04/02 18:32:00 tgl Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "access/heapam.h"#include "access/xact.h"#include "catalog/pg_type.h"#include "commands/portalcmds.h"#include "miscadmin.h"#include "utils/builtins.h"#include "utils/memutils.h"/* * Estimate of the maximum number of open portals a user would have, * used in initially sizing the PortalHashTable in EnablePortalManager(). * Since the hash table can expand, there's no need to make this overly * generous, and keeping it small avoids unnecessary overhead in the * hash_seq_search() calls executed during transaction end. */#define PORTALS_PER_USER	   16/* ---------------- *		Global state * ---------------- */#define MAX_PORTALNAME_LEN		NAMEDATALENtypedef struct portalhashent{	char		portalname[MAX_PORTALNAME_LEN];	Portal		portal;} PortalHashEnt;static HTAB *PortalHashTable = NULL;#define PortalHashTableLookup(NAME, PORTAL) \do { \	PortalHashEnt *hentry; \	\	hentry = (PortalHashEnt *) hash_search(PortalHashTable, \										   (NAME), HASH_FIND, NULL); \	if (hentry) \		PORTAL = hentry->portal; \	else \		PORTAL = NULL; \} while(0)#define PortalHashTableInsert(PORTAL, NAME) \do { \	PortalHashEnt *hentry; bool found; \	\	hentry = (PortalHashEnt *) hash_search(PortalHashTable, \										   (NAME), HASH_ENTER, &found); \	if (found) \		elog(ERROR, "duplicate portal name"); \	hentry->portal = PORTAL; \	/* To avoid duplicate storage, make PORTAL->name point to htab entry */ \	PORTAL->name = hentry->portalname; \} while(0)#define PortalHashTableDelete(PORTAL) \do { \	PortalHashEnt *hentry; \	\	hentry = (PortalHashEnt *) hash_search(PortalHashTable, \										   PORTAL->name, HASH_REMOVE, NULL); \	if (hentry == NULL) \		elog(WARNING, "trying to delete portal name that does not exist"); \} while(0)static MemoryContext PortalMemory = NULL;/* ---------------------------------------------------------------- *				   public portal interface functions * ---------------------------------------------------------------- *//* * EnablePortalManager *		Enables the portal management module at backend startup. */voidEnablePortalManager(void){	HASHCTL		ctl;	Assert(PortalMemory == NULL);	PortalMemory = AllocSetContextCreate(TopMemoryContext,										 "PortalMemory",										 ALLOCSET_DEFAULT_MINSIZE,										 ALLOCSET_DEFAULT_INITSIZE,										 ALLOCSET_DEFAULT_MAXSIZE);	ctl.keysize = MAX_PORTALNAME_LEN;	ctl.entrysize = sizeof(PortalHashEnt);	/*	 * use PORTALS_PER_USER as a guess of how many hash table entries to	 * create, initially	 */	PortalHashTable = hash_create("Portal hash", PORTALS_PER_USER,								  &ctl, HASH_ELEM);}/* * GetPortalByName *		Returns a portal given a portal name, or NULL if name not found. */PortalGetPortalByName(const char *name){	Portal		portal;	if (PointerIsValid(name))		PortalHashTableLookup(name, portal);	else		portal = NULL;	return portal;}/* * PortalListGetPrimaryStmt *		Get the "primary" stmt within a portal, ie, the one marked canSetTag. * * Returns NULL if no such stmt.  If multiple PlannedStmt structs within the * portal are marked canSetTag, returns the first one.	Neither of these * cases should occur in present usages of this function. * * Copes if given a list of Querys --- can't happen in a portal, but this * code also supports plancache.c, which needs both cases. * * Note: the reason this is just handed a List is so that plancache.c * can share the code.	For use with a portal, use PortalGetPrimaryStmt * rather than calling this directly. */Node *PortalListGetPrimaryStmt(List *stmts){	ListCell   *lc;	foreach(lc, stmts)	{		Node	   *stmt = (Node *) lfirst(lc);		if (IsA(stmt, PlannedStmt))		{			if (((PlannedStmt *) stmt)->canSetTag)				return stmt;		}		else if (IsA(stmt, Query))		{			if (((Query *) stmt)->canSetTag)				return stmt;		}		else		{			/* Utility stmts are assumed canSetTag if they're the only stmt */			if (list_length(stmts) == 1)				return stmt;		}	}	return NULL;}/* * CreatePortal *		Returns a new portal given a name. * * allowDup: if true, automatically drop any pre-existing portal of the * same name (if false, an error is raised). * * dupSilent: if true, don't even emit a WARNING. */PortalCreatePortal(const char *name, bool allowDup, bool dupSilent){	Portal		portal;	AssertArg(PointerIsValid(name));	portal = GetPortalByName(name);	if (PortalIsValid(portal))	{		if (!allowDup)			ereport(ERROR,					(errcode(ERRCODE_DUPLICATE_CURSOR),					 errmsg("cursor \"%s\" already exists", name)));		if (!dupSilent)			ereport(WARNING,					(errcode(ERRCODE_DUPLICATE_CURSOR),					 errmsg("closing existing cursor \"%s\"",							name)));		PortalDrop(portal, false);	}	/* make new portal structure */	portal = (Portal) MemoryContextAllocZero(PortalMemory, sizeof *portal);	/* initialize portal heap context; typically it won't store much */	portal->heap = AllocSetContextCreate(PortalMemory,										 "PortalHeapMemory",										 ALLOCSET_SMALL_MINSIZE,										 ALLOCSET_SMALL_INITSIZE,										 ALLOCSET_SMALL_MAXSIZE);	/* create a resource owner for the portal */	portal->resowner = ResourceOwnerCreate(CurTransactionResourceOwner,										   "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 = GetCurrentStatementStartTimestamp();	/* put portal in table (sets portal->name) */	PortalHashTableInsert(portal, name);	return portal;}/* * CreateNewPortal *		Create a new portal, assigning it a random nonconflicting name. */PortalCreateNewPortal(void){	static unsigned int unnamed_portal_count = 0;	char		portalname[MAX_PORTALNAME_LEN];	/* Select a nonconflicting name */	for (;;)	{		unnamed_portal_count++;		sprintf(portalname, "<unnamed portal %u>", unnamed_portal_count);		if (GetPortalByName(portalname) == NULL)			break;	}	return CreatePortal(portalname, false, false);}/* * PortalDefineQuery *		A simple subroutine to establish a portal's query. * * Notes: commandTag shall be NULL if and only if the original query string * (before rewriting) was an empty string.	Also, the passed commandTag must * be a pointer to a constant string, since it is not copied. * * If cplan is provided, then it is a cached plan containing the stmts, * and the caller must have done RevalidateCachedPlan(), causing a refcount * increment.  The refcount will be released when the portal is destroyed. * * If cplan is NULL, then it is the caller's responsibility to ensure that * the passed plan trees have adequate lifetime.  Typically this is done by * copying them into the portal's heap context. * * The caller is also responsible for ensuring that the passed prepStmtName * and sourceText (if not NULL) have adequate lifetime. * * NB: this function mustn't do much beyond storing the passed values; in * particular don't do anything that risks elog(ERROR).  If that were to * happen here before storing the cplan reference, we'd leak the plancache * refcount that the caller is trying to hand off to us. */voidPortalDefineQuery(Portal portal,				  const char *prepStmtName,				  const char *sourceText,				  const char *commandTag,				  List *stmts,				  CachedPlan *cplan){	AssertArg(PortalIsValid(portal));	AssertState(portal->status == PORTAL_NEW);	Assert(commandTag != NULL || stmts == NIL);	portal->prepStmtName = prepStmtName;	portal->sourceText = sourceText;	portal->commandTag = commandTag;	portal->stmts = stmts;	portal->cplan = cplan;	portal->status = PORTAL_DEFINED;}/* * PortalReleaseCachedPlan *		Release a portal's reference to its cached plan, if any. */static voidPortalReleaseCachedPlan(Portal portal){	if (portal->cplan)	{		ReleaseCachedPlan(portal->cplan, false);		portal->cplan = NULL;	}}/* * PortalCreateHoldStore *		Create the tuplestore for a portal. */voidPortalCreateHoldStore(Portal portal){	MemoryContext oldcxt;	Assert(portal->holdContext == NULL);	Assert(portal->holdStore == NULL);	/*	 * Create the memory context that is used for storage of the tuple set.	 * Note this is NOT a child of the portal's heap memory.	 */	portal->holdContext =		AllocSetContextCreate(PortalMemory,							  "PortalHoldContext",							  ALLOCSET_DEFAULT_MINSIZE,							  ALLOCSET_DEFAULT_INITSIZE,							  ALLOCSET_DEFAULT_MAXSIZE);	/* Create the tuple store, selecting cross-transaction temp files. */	oldcxt = MemoryContextSwitchTo(portal->holdContext);	/* XXX: Should maintenance_work_mem be used for the portal size? */	portal->holdStore = tuplestore_begin_heap(true, true, work_mem);	MemoryContextSwitchTo(oldcxt);}/* * PortalDrop *		Destroy the portal. */voidPortalDrop(Portal portal, bool isTopCommit){	AssertArg(PortalIsValid(portal));	/* Not sure if this case can validly happen or not... */	if (portal->status == PORTAL_ACTIVE)		elog(ERROR, "cannot drop active portal");	/*	 * Remove portal from hash table.  Because we do this first, we will not	 * come back to try to remove the portal again if there's any error in the	 * subsequent steps.  Better to leak a little memory than to get into an	 * infinite error-recovery loop.	 */	PortalHashTableDelete(portal);	/* let portalcmds.c clean up the state it knows about */	if (PointerIsValid(portal->cleanup))		(*portal->cleanup) (portal);	/* drop cached plan reference, if any */	if (portal->cplan)		PortalReleaseCachedPlan(portal);	/*	 * Release any resources still attached to the portal.	There are several	 * cases being covered here:	 *	 * Top transaction commit (indicated by isTopCommit): normally we should	 * do nothing here and let the regular end-of-transaction resource	 * releasing mechanism handle these resources too.	However, if we have a	 * FAILED portal (eg, a cursor that got an error), we'd better clean up	 * its resources to avoid resource-leakage warning messages.	 *	 * Sub transaction commit: never comes here at all, since we don't kill	 * any portals in AtSubCommit_Portals().	 *	 * Main or sub transaction abort: we will do nothing here because	 * portal->resowner was already set NULL; the resources were already	 * cleaned up in transaction abort.	 *	 * Ordinary portal drop: must release resources.  However, if the portal	 * is not FAILED then we do not release its locks.	The locks become the	 * responsibility of the transaction's ResourceOwner (since it is the	 * parent of the portal's owner) and will be released when the transaction	 * eventually ends.	 */	if (portal->resowner &&		(!isTopCommit || portal->status == PORTAL_FAILED))	{		bool		isCommit = (portal->status != PORTAL_FAILED);		ResourceOwnerRelease(portal->resowner,							 RESOURCE_RELEASE_BEFORE_LOCKS,							 isCommit, false);		ResourceOwnerRelease(portal->resowner,							 RESOURCE_RELEASE_LOCKS,							 isCommit, false);		ResourceOwnerRelease(portal->resowner,							 RESOURCE_RELEASE_AFTER_LOCKS,							 isCommit, false);		ResourceOwnerDelete(portal->resowner);	}	portal->resowner = NULL;	/*	 * Delete tuplestore if present.  We should do this even under error	 * conditions; since the tuplestore would have been using cross-	 * transaction storage, its temp files need to be explicitly deleted.	 */	if (portal->holdStore)	{		MemoryContext oldcontext;		oldcontext = MemoryContextSwitchTo(portal->holdContext);		tuplestore_end(portal->holdStore);		MemoryContextSwitchTo(oldcontext);		portal->holdStore = NULL;	}	/* delete tuplestore storage, if any */	if (portal->holdContext)		MemoryContextDelete(portal->holdContext);	/* release subsidiary storage */	MemoryContextDelete(PortalGetHeapMemory(portal));	/* release portal struct (it's in PortalMemory) */	pfree(portal);}/* * Delete all declared cursors. * * Used by commands: CLOSE ALL, DISCARD ALL */voidPortalHashTableDeleteAll(void){	HASH_SEQ_STATUS status;	PortalHashEnt *hentry;	if (PortalHashTable == NULL)		return;	hash_seq_init(&status, PortalHashTable);	while ((hentry = hash_seq_search(&status)) != NULL)	{		Portal		portal = hentry->portal;		if (portal->status != PORTAL_ACTIVE)			PortalDrop(portal, false);	}}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?