TransactionId xid; /* xid that might need to see the row */
int vfd; /* fd of mappings file */
off_t off; /* how far have we written yet */
- uint32 num_mappings; /* number of in-memory mappings */
- dlist_head mappings; /* list of in-memory mappings */
+ dclist_head mappings; /* list of in-memory mappings */
char path[MAXPGPATH]; /* path, for error messages */
} RewriteMappingFile;
Oid dboid;
uint32 len;
int written;
+ uint32 num_mappings = dclist_count(&src->mappings);
/* this file hasn't got any new mappings */
- if (src->num_mappings == 0)
+ if (num_mappings == 0)
continue;
if (state->rs_old_rel->rd_rel->relisshared)
else
dboid = MyDatabaseId;
- xlrec.num_mappings = src->num_mappings;
+ xlrec.num_mappings = num_mappings;
xlrec.mapped_rel = RelationGetRelid(state->rs_old_rel);
xlrec.mapped_xid = src->xid;
xlrec.mapped_db = dboid;
xlrec.start_lsn = state->rs_begin_lsn;
/* write all mappings consecutively */
- len = src->num_mappings * sizeof(LogicalRewriteMappingData);
+ len = num_mappings * sizeof(LogicalRewriteMappingData);
waldata_start = waldata = palloc(len);
/*
* collect data we need to write out, but don't modify ondisk data yet
*/
- dlist_foreach_modify(iter, &src->mappings)
+ dclist_foreach_modify(iter, &src->mappings)
{
RewriteMappingDataEntry *pmap;
- pmap = dlist_container(RewriteMappingDataEntry, node, iter.cur);
+ pmap = dclist_container(RewriteMappingDataEntry, node, iter.cur);
memcpy(waldata, &pmap->map, sizeof(pmap->map));
waldata += sizeof(pmap->map);
/* remove from the list and free */
- dlist_delete(&pmap->node);
+ dclist_delete_from(&src->mappings, &pmap->node);
pfree(pmap);
/* update bookkeeping */
state->rs_num_rewrite_mappings--;
- src->num_mappings--;
}
- Assert(src->num_mappings == 0);
+ Assert(dclist_count(&src->mappings) == 0);
Assert(waldata == waldata_start + len);
/*
LSN_FORMAT_ARGS(state->rs_begin_lsn),
xid, GetCurrentTransactionId());
- dlist_init(&src->mappings);
- src->num_mappings = 0;
+ dclist_init(&src->mappings);
src->off = 0;
memcpy(src->path, path, sizeof(path));
src->vfd = PathNameOpenFile(path,
pmap = MemoryContextAlloc(state->rs_cxt,
sizeof(RewriteMappingDataEntry));
memcpy(&pmap->map, map, sizeof(LogicalRewriteMappingData));
- dlist_push_tail(&src->mappings, &pmap->node);
- src->num_mappings++;
+ dclist_push_tail(&src->mappings, &pmap->node);
state->rs_num_rewrite_mappings++;
/*
} mXactCacheEnt;
#define MAX_CACHE_ENTRIES 256
-static dlist_head MXactCache = DLIST_STATIC_INIT(MXactCache);
-static int MXactCacheMembers = 0;
+static dclist_head MXactCache = DCLIST_STATIC_INIT(MXactCache);
static MemoryContext MXactContext = NULL;
#ifdef MULTIXACT_DEBUG
/* sort the array so comparison is easy */
qsort(members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
- dlist_foreach(iter, &MXactCache)
+ dclist_foreach(iter, &MXactCache)
{
- mXactCacheEnt *entry = dlist_container(mXactCacheEnt, node, iter.cur);
+ mXactCacheEnt *entry = dclist_container(mXactCacheEnt, node,
+ iter.cur);
if (entry->nmembers != nmembers)
continue;
if (memcmp(members, entry->members, nmembers * sizeof(MultiXactMember)) == 0)
{
debug_elog3(DEBUG2, "CacheGet: found %u", entry->multi);
- dlist_move_head(&MXactCache, iter.cur);
+ dclist_move_head(&MXactCache, iter.cur);
return entry->multi;
}
}
debug_elog3(DEBUG2, "CacheGet: looking for %u", multi);
- dlist_foreach(iter, &MXactCache)
+ dclist_foreach(iter, &MXactCache)
{
- mXactCacheEnt *entry = dlist_container(mXactCacheEnt, node, iter.cur);
+ mXactCacheEnt *entry = dclist_container(mXactCacheEnt, node,
+ iter.cur);
if (entry->multi == multi)
{
* This is acceptable only because we exit the iteration
* immediately afterwards.
*/
- dlist_move_head(&MXactCache, iter.cur);
+ dclist_move_head(&MXactCache, iter.cur);
*members = ptr;
return entry->nmembers;
/* mXactCacheGetBySet assumes the entries are sorted, so sort them */
qsort(entry->members, nmembers, sizeof(MultiXactMember), mxactMemberComparator);
- dlist_push_head(&MXactCache, &entry->node);
- if (MXactCacheMembers++ >= MAX_CACHE_ENTRIES)
+ dclist_push_head(&MXactCache, &entry->node);
+ if (dclist_count(&MXactCache) > MAX_CACHE_ENTRIES)
{
dlist_node *node;
- node = dlist_tail_node(&MXactCache);
- dlist_delete(node);
- MXactCacheMembers--;
+ node = dclist_tail_node(&MXactCache);
+ dclist_delete_from(&MXactCache, node);
- entry = dlist_container(mXactCacheEnt, node, node);
+ entry = dclist_container(mXactCacheEnt, node, node);
debug_elog3(DEBUG2, "CachePut: pruning cached multi %u",
entry->multi);
* a child of TopTransactionContext, we needn't delete it explicitly.
*/
MXactContext = NULL;
- dlist_init(&MXactCache);
- MXactCacheMembers = 0;
+ dclist_init(&MXactCache);
}
/*
* Discard the local MultiXactId cache like in AtEOXact_MultiXact.
*/
MXactContext = NULL;
- dlist_init(&MXactCache);
- MXactCacheMembers = 0;
+ dclist_init(&MXactCache);
}
/*
}
#ifdef ILIST_DEBUG
+/*
+ * dlist_member_check
+ * Validate that 'node' is a member of 'head'
+ */
+void
+dlist_member_check(dlist_head *head, dlist_node *node)
+{
+ dlist_iter iter;
+
+ dlist_foreach(iter, head)
+ {
+ if (iter.cur == node)
+ return;
+ }
+ elog(ERROR, "double linked list member check failure");
+}
+
/*
* Verify integrity of a doubly linked list
*/
buffer->by_txn_last_xid = InvalidTransactionId;
buffer->by_txn_last_txn = NULL;
- buffer->catchange_ntxns = 0;
-
buffer->outbuf = NULL;
buffer->outbufsize = 0;
buffer->size = 0;
dlist_init(&buffer->toplevel_by_lsn);
dlist_init(&buffer->txns_by_base_snapshot_lsn);
- dlist_init(&buffer->catchange_txns);
+ dclist_init(&buffer->catchange_txns);
/*
* Ensure there's no stale data from prior uses of this slot, in case some
*/
dlist_delete(&txn->node);
if (rbtxn_has_catalog_changes(txn))
- {
- dlist_delete(&txn->catchange_node);
- rb->catchange_ntxns--;
-
- Assert(rb->catchange_ntxns >= 0);
- }
+ dclist_delete_from(&rb->catchange_txns, &txn->catchange_node);
/* now remove reference from buffer */
hash_search(rb->by_txn,
if (!rbtxn_has_catalog_changes(txn))
{
txn->txn_flags |= RBTXN_HAS_CATALOG_CHANGES;
- dlist_push_tail(&rb->catchange_txns, &txn->catchange_node);
- rb->catchange_ntxns++;
+ dclist_push_tail(&rb->catchange_txns, &txn->catchange_node);
}
/*
if (toptxn != NULL && !rbtxn_has_catalog_changes(toptxn))
{
toptxn->txn_flags |= RBTXN_HAS_CATALOG_CHANGES;
- dlist_push_tail(&rb->catchange_txns, &toptxn->catchange_node);
- rb->catchange_ntxns++;
+ dclist_push_tail(&rb->catchange_txns, &toptxn->catchange_node);
}
}
size_t xcnt = 0;
/* Quick return if the list is empty */
- if (rb->catchange_ntxns == 0)
- {
- Assert(dlist_is_empty(&rb->catchange_txns));
+ if (dclist_count(&rb->catchange_txns) == 0)
return NULL;
- }
/* Initialize XID array */
- xids = (TransactionId *) palloc(sizeof(TransactionId) * rb->catchange_ntxns);
- dlist_foreach(iter, &rb->catchange_txns)
+ xids = (TransactionId *) palloc(sizeof(TransactionId) *
+ dclist_count(&rb->catchange_txns));
+ dclist_foreach(iter, &rb->catchange_txns)
{
- ReorderBufferTXN *txn = dlist_container(ReorderBufferTXN,
- catchange_node,
- iter.cur);
+ ReorderBufferTXN *txn = dclist_container(ReorderBufferTXN,
+ catchange_node,
+ iter.cur);
Assert(rbtxn_has_catalog_changes(txn));
qsort(xids, xcnt, sizeof(TransactionId), xidComparator);
- Assert(xcnt == rb->catchange_ntxns);
+ Assert(xcnt == dclist_count(&rb->catchange_txns));
return xids;
}
/* Get the catalog modifying transactions that are yet not committed */
catchange_xip = ReorderBufferGetCatalogChangesXacts(builder->reorder);
- catchange_xcnt = builder->reorder->catchange_ntxns;
+ catchange_xcnt = dclist_count(&builder->reorder->catchange_txns);
needed_length = sizeof(SnapBuildOnDisk) +
sizeof(TransactionId) * (builder->committed.xcnt + catchange_xcnt);
dlist_mutable_iter iter;
int not_freed_count = 0;
- if (xact_state->pending_drops_count == 0)
- {
- Assert(dlist_is_empty(&xact_state->pending_drops));
+ if (dclist_count(&xact_state->pending_drops) == 0)
return;
- }
- dlist_foreach_modify(iter, &xact_state->pending_drops)
+ dclist_foreach_modify(iter, &xact_state->pending_drops)
{
PgStat_PendingDroppedStatsItem *pending =
- dlist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
+ dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
xl_xact_stats_item *it = &pending->item;
if (isCommit && !pending->is_create)
not_freed_count++;
}
- dlist_delete(&pending->node);
- xact_state->pending_drops_count--;
+ dclist_delete_from(&xact_state->pending_drops, &pending->node);
pfree(pending);
}
dlist_mutable_iter iter;
int not_freed_count = 0;
- if (xact_state->pending_drops_count == 0)
+ if (dclist_count(&xact_state->pending_drops) == 0)
return;
parent_xact_state = pgstat_get_xact_stack_level(nestDepth - 1);
- dlist_foreach_modify(iter, &xact_state->pending_drops)
+ dclist_foreach_modify(iter, &xact_state->pending_drops)
{
PgStat_PendingDroppedStatsItem *pending =
- dlist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
+ dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
xl_xact_stats_item *it = &pending->item;
- dlist_delete(&pending->node);
- xact_state->pending_drops_count--;
+ dclist_delete_from(&xact_state->pending_drops, &pending->node);
if (!isCommit && pending->is_create)
{
* remove the stats object, the surrounding transaction might
* still abort. Pass it on to the parent.
*/
- dlist_push_tail(&parent_xact_state->pending_drops, &pending->node);
- parent_xact_state->pending_drops_count++;
+ dclist_push_tail(&parent_xact_state->pending_drops, &pending->node);
}
else
{
}
}
- Assert(xact_state->pending_drops_count == 0);
+ Assert(dclist_count(&xact_state->pending_drops) == 0);
if (not_freed_count > 0)
pgstat_request_entry_refs_gc();
}
xact_state = (PgStat_SubXactStatus *)
MemoryContextAlloc(TopTransactionContext,
sizeof(PgStat_SubXactStatus));
- dlist_init(&xact_state->pending_drops);
- xact_state->pending_drops_count = 0;
+ dclist_init(&xact_state->pending_drops);
xact_state->nest_level = nest_level;
xact_state->prev = pgStatXactStack;
xact_state->first = NULL;
Assert(!isCommit || xact_state->nest_level == 1);
Assert(!isCommit || xact_state->prev == NULL);
- *items = palloc(xact_state->pending_drops_count
+ *items = palloc(dclist_count(&xact_state->pending_drops)
* sizeof(xl_xact_stats_item));
- dlist_foreach(iter, &xact_state->pending_drops)
+ dclist_foreach(iter, &xact_state->pending_drops)
{
PgStat_PendingDroppedStatsItem *pending =
- dlist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
+ dclist_container(PgStat_PendingDroppedStatsItem, node, iter.cur);
if (isCommit && pending->is_create)
continue;
if (!isCommit && !pending->is_create)
continue;
- Assert(nitems < xact_state->pending_drops_count);
+ Assert(nitems < dclist_count(&xact_state->pending_drops));
(*items)[nitems++] = pending->item;
}
drop->item.dboid = dboid;
drop->item.objoid = objoid;
- dlist_push_tail(&xact_state->pending_drops, &drop->node);
- xact_state->pending_drops_count++;
+ dclist_push_tail(&xact_state->pending_drops, &drop->node);
}
/*
static HTAB *ri_constraint_cache = NULL;
static HTAB *ri_query_cache = NULL;
static HTAB *ri_compare_cache = NULL;
-static dlist_head ri_constraint_cache_valid_list;
-static int ri_constraint_cache_valid_count = 0;
+static dclist_head ri_constraint_cache_valid_list;
/*
/*
* For efficient processing of invalidation messages below, we keep a
- * doubly-linked list, and a count, of all currently valid entries.
+ * doubly-linked count list of all currently valid entries.
*/
- dlist_push_tail(&ri_constraint_cache_valid_list, &riinfo->valid_link);
- ri_constraint_cache_valid_count++;
+ dclist_push_tail(&ri_constraint_cache_valid_list, &riinfo->valid_link);
riinfo->valid = true;
* O(N^2) behavior in situations where a session touches many foreign keys
* and also does many ALTER TABLEs, such as a restore from pg_dump.
*/
- if (ri_constraint_cache_valid_count > 1000)
+ if (dclist_count(&ri_constraint_cache_valid_list) > 1000)
hashvalue = 0; /* pretend it's a cache reset */
- dlist_foreach_modify(iter, &ri_constraint_cache_valid_list)
+ dclist_foreach_modify(iter, &ri_constraint_cache_valid_list)
{
- RI_ConstraintInfo *riinfo = dlist_container(RI_ConstraintInfo,
- valid_link, iter.cur);
+ RI_ConstraintInfo *riinfo = dclist_container(RI_ConstraintInfo,
+ valid_link, iter.cur);
/*
* We must invalidate not only entries directly matching the given
{
riinfo->valid = false;
/* Remove invalidated entries from the list, too */
- dlist_delete(iter.cur);
- ri_constraint_cache_valid_count--;
+ dclist_delete_from(&ri_constraint_cache_valid_list, iter.cur);
}
}
}
* the link fields in the remainder would be wasted space. But usually,
* it saves space to not have separately-allocated list nodes.)
*
+ * The doubly-linked list comes in 2 forms. dlist_head defines a head of a
+ * doubly-linked list of dlist_nodes, whereas dclist_head defines the head of
+ * a doubly-linked list of dlist_nodes with an additional 'count' field to
+ * keep track of how many items are contained within the given list. For
+ * simplicity, dlist_head and dclist_head share the same node and iterator
+ * types. The functions to manipulate a dlist_head always have a name
+ * starting with "dlist", whereas functions to manipulate a dclist_head have a
+ * name starting with "dclist". dclist_head comes with an additional function
+ * (dclist_count) to return the number of entries in the list. dclists are
+ * able to store a maximum of PG_UINT32_MAX elements. It is up to the caller
+ * to ensure no more than this many items are added to a dclist.
+ *
* None of the functions here allocate any memory; they just manipulate
- * externally managed memory. The APIs for singly and doubly linked lists
- * are identical as far as capabilities of both allow.
+ * externally managed memory. With the exception doubly-linked count lists
+ * providing the ability to obtain the number of items in the list, the APIs
+ * for singly and both doubly linked lists are identical as far as
+ * capabilities of both allow.
*
* Each list has a list header, which exists even when the list is empty.
* An empty singly-linked list has a NULL pointer in its header.
- * There are two kinds of empty doubly linked lists: those that have been
- * initialized to NULL, and those that have been initialized to circularity.
+ *
+ * For both doubly-linked list types, there are two valid ways to represent an
+ * empty list. The head's 'next' pointer can either be NULL or the head's
+ * 'next' and 'prev' links can both point back to the list head (circular).
* (If a dlist is modified and then all its elements are deleted, it will be
- * in the circular state.) We prefer circular dlists because there are some
+ * in the circular state.). We prefer circular dlists because there are some
* operations that can be done without branches (and thus faster) on lists
* that use circular representation. However, it is often convenient to
* initialize list headers to zeroes rather than setting them up with an
- * explicit initialization function, so we also allow the other case.
+ * explicit initialization function, so we also allow the NULL initalization.
*
* EXAMPLES
*
/*
- * Doubly linked list iterator.
+ * Doubly linked list iterator type for dlist_head and and dclist_head types.
+ *
+ * Used as state in dlist_foreach() and dlist_reverse_foreach() (and the
+ * dclist variant thereof).
*
- * Used as state in dlist_foreach() and dlist_reverse_foreach(). To get the
- * current element of the iteration use the 'cur' member.
+ * To get the current element of the iteration use the 'cur' member.
*
* Iterations using this are *not* allowed to change the list while iterating!
*
* NB: We use an extra "end" field here to avoid multiple evaluations of
- * arguments in the dlist_foreach() macro.
+ * arguments in the dlist_foreach() and dclist_foreach() macros.
*/
typedef struct dlist_iter
{
} dlist_iter;
/*
- * Doubly linked list iterator allowing some modifications while iterating.
+ * Doubly linked list iterator for both dlist_head and dclist_head types.
+ * This iterator type allows some modifications while iterating.
*
- * Used as state in dlist_foreach_modify(). To get the current element of the
- * iteration use the 'cur' member.
+ * Used as state in dlist_foreach_modify() and dclist_foreach_modify().
+ *
+ * To get the current element of the iteration use the 'cur' member.
*
* Iterations using this are only allowed to change the list at the current
* point of iteration. It is fine to delete the current node, but it is *not*
dlist_node *end; /* last node we'll iterate to */
} dlist_mutable_iter;
+/*
+ * Head of a doubly linked list with a count of the number of items
+ *
+ * This internally makes use of a dlist to implement the actual list. When
+ * items are added or removed from the list the count is updated to reflect
+ * the current number of items in the list.
+ */
+typedef struct dclist_head
+{
+ dlist_head dlist; /* the actual list header */
+ uint32 count; /* the number of items in the list */
+} dclist_head;
+
/*
* Node of a singly linked list.
*
/* Static initializers */
#define DLIST_STATIC_INIT(name) {{&(name).head, &(name).head}}
+#define DCLIST_STATIC_INIT(name) {{{&(name).dlist.head, &(name).dlist.head}}, 0}
#define SLIST_STATIC_INIT(name) {{NULL}}
extern void slist_delete(slist_head *head, slist_node *node);
#ifdef ILIST_DEBUG
+extern void dlist_member_check(dlist_head *head, dlist_node *node);
extern void dlist_check(dlist_head *head);
extern void slist_check(slist_head *head);
#else
* in which functions the only point of passing the list head pointer is to be
* able to run these checks.
*/
+#define dlist_member_check(head, node) ((void) (head))
#define dlist_check(head) ((void) (head))
#define slist_check(head) ((void) (head))
#endif /* ILIST_DEBUG */
node->next->prev = node->prev;
}
+/*
+ * Same as dlist_delete, but performs checks in ILIST_DEBUG builds to ensure
+ * that 'node' belongs to 'head'.
+ */
+static inline void
+dlist_delete_from(dlist_head *head, dlist_node *node)
+{
+ dlist_member_check(head, node);
+ dlist_delete(node);
+}
+
/*
* Remove and return the first node from a list (there must be one).
*/
(iter).cur != (iter).end; \
(iter).cur = (iter).cur->prev)
+/* doubly-linked count list implementation */
+
+/*
+ * dclist_init
+ * Initialize a doubly linked count list.
+ *
+ * Previous state will be thrown away without any cleanup.
+ */
+static inline void
+dclist_init(dclist_head *head)
+{
+ dlist_init(&head->dlist);
+ head->count = 0;
+}
+
+/*
+ * dclist_is_empty
+ * Returns true if the list is empty, otherwise false.
+ */
+static inline bool
+dclist_is_empty(dclist_head *head)
+{
+ Assert(dlist_is_empty(&head->dlist) == (head->count == 0));
+ return (head->count == 0);
+}
+
+/*
+ * dclist_push_head
+ * Insert a node at the beginning of the list.
+ */
+static inline void
+dclist_push_head(dclist_head *head, dlist_node *node)
+{
+ if (head->dlist.head.next == NULL) /* convert NULL header to circular */
+ dclist_init(head);
+
+ dlist_push_head(&head->dlist, node);
+ head->count++;
+
+ Assert(head->count > 0); /* count overflow check */
+}
+
+/*
+ * dclist_push_tail
+ * Insert a node at the end of the list.
+ */
+static inline void
+dclist_push_tail(dclist_head *head, dlist_node *node)
+{
+ if (head->dlist.head.next == NULL) /* convert NULL header to circular */
+ dclist_init(head);
+
+ dlist_push_tail(&head->dlist, node);
+ head->count++;
+
+ Assert(head->count > 0); /* count overflow check */
+}
+
+/*
+ * dclist_insert_after
+ * Insert a node after another *in the same list*
+ *
+ * Caution: 'after' must be a member of 'head'.
+ */
+static inline void
+dclist_insert_after(dclist_head *head, dlist_node *after, dlist_node *node)
+{
+ dlist_member_check(&head->dlist, after);
+ Assert(head->count > 0); /* must be at least 1 already */
+
+ dlist_insert_after(after, node);
+ head->count++;
+
+ Assert(head->count > 0); /* count overflow check */
+}
+
+/*
+ * dclist_insert_before
+ * Insert a node before another *in the same list*
+ *
+ * Caution: 'before' must be a member of 'head'.
+ */
+static inline void
+dclist_insert_before(dclist_head *head, dlist_node *before, dlist_node *node)
+{
+ dlist_member_check(&head->dlist, before);
+ Assert(head->count > 0); /* must be at least 1 already */
+
+ dlist_insert_before(before, node);
+ head->count++;
+
+ Assert(head->count > 0); /* count overflow check */
+}
+
+/*
+ * dclist_delete_from
+ * Deletes 'node' from 'head'.
+ *
+ * Caution: 'node' must be a member of 'head'.
+ */
+static inline void
+dclist_delete_from(dclist_head *head, dlist_node *node)
+{
+ Assert(head->count > 0);
+
+ dlist_delete_from(&head->dlist, node);
+ head->count--;
+}
+
+/*
+ * dclist_pop_head_node
+ * Remove and return the first node from a list (there must be one).
+ */
+static inline dlist_node *
+dclist_pop_head_node(dclist_head *head)
+{
+ dlist_node *node;
+
+ Assert(head->count > 0);
+
+ node = dlist_pop_head_node(&head->dlist);
+ head->count--;
+ return node;
+}
+
+/*
+ * dclist_move_head
+ * Move 'node' from its current position in the list to the head position
+ * in 'head'.
+ *
+ * Caution: 'node' must be a member of 'head'.
+ */
+static inline void
+dclist_move_head(dclist_head *head, dlist_node *node)
+{
+ dlist_member_check(&head->dlist, node);
+ Assert(head->count > 0);
+
+ dlist_move_head(&head->dlist, node);
+}
+
+/*
+ * dclist_move_tail
+ * Move 'node' from its current position in the list to the tail position
+ * in 'head'.
+ *
+ * Caution: 'node' must be a member of 'head'.
+ */
+static inline void
+dclist_move_tail(dclist_head *head, dlist_node *node)
+{
+ dlist_member_check(&head->dlist, node);
+ Assert(head->count > 0);
+
+ dlist_move_tail(&head->dlist, node);
+}
+
+/*
+ * dclist_has_next
+ * Check whether 'node' has a following node.
+ *
+ * Caution: 'node' must be a member of 'head'.
+ */
+static inline bool
+dclist_has_next(dclist_head *head, dlist_node *node)
+{
+ dlist_member_check(&head->dlist, node);
+ Assert(head->count > 0);
+
+ return dlist_has_next(&head->dlist, node);
+}
+
+/*
+ * dclist_has_prev
+ * Check whether 'node' has a preceding node.
+ *
+ * Caution: 'node' must be a member of 'head'.
+ */
+static inline bool
+dclist_has_prev(dclist_head *head, dlist_node *node)
+{
+ dlist_member_check(&head->dlist, node);
+ Assert(head->count > 0);
+
+ return dlist_has_prev(&head->dlist, node);
+}
+
+/*
+ * dclist_next_node
+ * Return the next node in the list (there must be one).
+ */
+static inline dlist_node *
+dclist_next_node(dclist_head *head, dlist_node *node)
+{
+ Assert(head->count > 0);
+
+ return dlist_next_node(&head->dlist, node);
+}
+
+/*
+ * dclist_prev_node
+ * Return the prev node in the list (there must be one).
+ */
+static inline dlist_node *
+dclist_prev_node(dclist_head *head, dlist_node *node)
+{
+ Assert(head->count > 0);
+
+ return dlist_prev_node(&head->dlist, node);
+}
+
+/* internal support function to get address of head element's struct */
+static inline void *
+dclist_head_element_off(dclist_head *head, size_t off)
+{
+ Assert(!dclist_is_empty(head));
+
+ return (char *) head->dlist.head.next - off;
+}
+
+/*
+ * dclist_head_node
+ * Return the first node in the list (there must be one).
+ */
+static inline dlist_node *
+dclist_head_node(dclist_head *head)
+{
+ Assert(head->count > 0);
+
+ return (dlist_node *) dlist_head_element_off(&head->dlist, 0);
+}
+
+/* internal support function to get address of tail element's struct */
+static inline void *
+dclist_tail_element_off(dclist_head *head, size_t off)
+{
+ Assert(!dclist_is_empty(head));
+
+ return (char *) head->dlist.head.prev - off;
+}
+
+/*
+ * Return the last node in the list (there must be one).
+ */
+static inline dlist_node *
+dclist_tail_node(dclist_head *head)
+{
+ Assert(head->count > 0);
+
+ return (dlist_node *) dlist_tail_element_off(&head->dlist, 0);
+}
+
+/*
+ * dclist_count
+ * Returns the stored number of entries in 'head'
+ */
+static inline uint32
+dclist_count(dclist_head *head)
+{
+ Assert(dlist_is_empty(&head->dlist) == (head->count == 0));
+
+ return head->count;
+}
+
+/*
+ * Return the containing struct of 'type' where 'membername' is the dlist_node
+ * pointed at by 'ptr'.
+ *
+ * This is used to convert a dlist_node * back to its containing struct.
+ *
+ * Note: This is effectively just the same as dlist_container, so reuse that.
+ */
+#define dclist_container(type, membername, ptr) \
+ dlist_container(type, membername, ptr)
+
+ /*
+ * Return the address of the first element in the list.
+ *
+ * The list must not be empty.
+ */
+#define dclist_head_element(type, membername, lhead) \
+ (AssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node), \
+ (type *) dclist_head_element_off(lhead, offsetof(type, membername)))
+
+ /*
+ * Return the address of the last element in the list.
+ *
+ * The list must not be empty.
+ */
+#define dclist_tail_element(type, membername, lhead) \
+ (AssertVariableIsOfTypeMacro(((type *) NULL)->membername, dlist_node), \
+ ((type *) dclist_tail_element_off(lhead, offsetof(type, membername))))
+
+
+/* Iterators for dclists */
+#define dclist_foreach(iter, lhead) \
+ dlist_foreach(iter, &((lhead)->dlist))
+
+#define dclist_foreach_modify(iter, lhead) \
+ dlist_foreach_modify(iter, &((lhead)->dlist))
+
+#define dclist_reverse_foreach(iter, lhead) \
+ dlist_reverse_foreach(iter, &((lhead)->dlist))
/* singly linked list implementation */
/*
* Transactions and subtransactions that have modified system catalogs.
*/
- dlist_head catchange_txns;
- int catchange_ntxns;
+ dclist_head catchange_txns;
/*
* one-entry sized cache for by_txn. Very frequently the same txn gets
* if the transaction commits/aborts. To handle replicas and crashes,
* stats drops are included in commit / abort records.
*/
- dlist_head pending_drops;
- int pending_drops_count;
+ dclist_head pending_drops;
/*
* Tuple insertion/deletion counts for an open transaction can't be
MemoryContextCallbackFunction
MemoryContextCounters
MemoryContextData
-MemoryContextMethods
MemoryContextMethodID
+MemoryContextMethods
MemoryStatsPrintFunc
MergeAction
MergeActionState
dateKEY
datetkn
dce_uuid_t
+dclist_head
decimal
deparse_columns
deparse_context