From 4f5bbe8865a420d635d0d4357388d1c3632a9821 Mon Sep 17 00:00:00 2001
From: Jelte Fennema-Nio <jelte.fennema@microsoft.com>
Date: Tue, 24 Oct 2023 16:46:08 +0200
Subject: [PATCH v4 1/2] Add macros for looping through a list without needing
 a ListCell

Many usages of the foreach macro only use the ListCell variable to get
its contents. This adds macros that simplify iteration code for that
common use case.
---
 src/include/nodes/pg_list.h | 111 ++++++++++++++++++++++++++++++++++--
 1 file changed, 105 insertions(+), 6 deletions(-)

diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 529a382d284..e09116e8fdb 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -383,13 +383,14 @@ lnext(const List *l, const ListCell *c)
  *	  delete the current list element from the List associated with a
  *	  surrounding foreach() loop, returning the new List pointer.
  *
- * This is equivalent to list_delete_cell(), but it also adjusts the foreach
- * loop's state so that no list elements will be missed.  Do not delete
- * elements from an active foreach loop's list in any other way!
+ * This is similar to list_delete_cell(), but it also works when using
+ * for_each_xyz macros that don't require passing in a "ListCell *".
+ * Furthermore it adjusts the foreach loop's state so that no list elements
+ * will be missed. Do not delete elements from an active foreach loop's list in
+ * any other way!
  */
-#define foreach_delete_current(lst, cell)	\
-	(cell##__state.i--, \
-	 (List *) (cell##__state.l = list_delete_cell(lst, cell)))
+#define foreach_delete_current(lst, var)	\
+	((List *) (var##__state.l = list_delete_cell(lst, &var##__state.l->elements[var##__state.i--])))
 
 /*
  * foreach_current_index -
@@ -452,6 +453,104 @@ for_each_cell_setup(const List *lst, const ListCell *initcell)
 	return r;
 }
 
+/*
+ * for_each_ptr -
+ *	  a convenience macro which loops through a list of pointers without
+ *	  needing a "ListCell *", just a declared pointer variable to store the
+ *	  current pointer int;
+ *
+ * Unlike with foreach() it's not possible to detect if an early "break"
+ * occured by checking the value of the loop variable at the end of the loop.
+ * If you need this, it's recommended to use foreach() instead or manually keep
+ * track of a break occured using a boolean flag variable called e.g. "found".
+ *
+ * The caveats for foreach() apply equally here.
+ */
+#define for_each_ptr(var, lst) \
+	for (ForEachState var##__state = {(lst), 0}; \
+		 (var##__state.l != NIL && \
+		  var##__state.i < var##__state.l->length && \
+		  (var = lfirst(&var##__state.l->elements[var##__state.i]), true));\
+		 var##__state.i++)
+
+/*
+ * for_each_int -
+ *	  a convenience macro which loops through a list of ints without needing a
+ *	  "ListCell *", just a declared int variable to store the current int in.
+ *
+ * Unlike with foreach() it's not possible to detect if an early "break"
+ * occured by checking the value of the loop variable at the end of the loop.
+ * If you need this, it's recommended to use foreach() instead or manually keep
+ * track of a break occured using a boolean flag variable called e.g. "found".
+ *
+ * The caveats for foreach() apply equally here.
+ */
+#define for_each_int(var, lst) \
+	for (ForEachState var##__state = {(lst), 0}; \
+		 (var##__state.l != NIL && \
+		  var##__state.i < var##__state.l->length && \
+		  (var = lfirst_int(&var##__state.l->elements[var##__state.i]), true));\
+		 var##__state.i++)
+
+/*
+ * foreach_oid -
+ *	  a convenience macro which loops through an oid list without needing a
+ *	  "ListCell *", just a declared Oid variable to store the current oid in.
+ *
+ * Unlike with foreach() it's not possible to detect if an early "break"
+ * occured by checking the value of the loop variable at the end of the loop.
+ * If you need this, it's recommended to use foreach() instead or manually keep
+ * track of a break occured using a boolean flag variable called e.g. "found".
+ *
+ * The caveats for foreach() apply equally here.
+ */
+#define for_each_oid(var, lst) \
+	for (ForEachState var##__state = {(lst), 0}; \
+		 (var##__state.l != NIL && \
+		  var##__state.i < var##__state.l->length && \
+		  (var = lfirst_oid(&var##__state.l->elements[var##__state.i]), true));\
+		 var##__state.i++)
+
+/*
+ * foreach_xid -
+ *	  a convenience macro which loops through an xid list without needing a
+ *	  "ListCell *", just a declared TransactionId variable to store the current
+ *	  xid in.
+ *
+ * Unlike with foreach() it's not possible to detect if an early "break"
+ * occured by checking the value of the loop variable at the end of the loop.
+ * If you need this, it's recommended to use foreach() instead or manually keep
+ * track of a break occured using a boolean flag variable called e.g. "found".
+ *
+ * The caveats for foreach() apply equally here.
+ */
+#define for_each_xid(var, lst) \
+	for (ForEachState var##__state = {(lst), 0}; \
+		 (var##__state.l != NIL && \
+		  var##__state.i < var##__state.l->length && \
+		  (var = lfirst_xid(&var##__state.l->elements[var##__state.i]), true));\
+		 var##__state.i++)
+
+/*
+ * for_each_node -
+ *	  a convenience macro which loops through a node list without needing a
+ *	  "ListCell *", just a declared pointer variable to store the pointer of
+ *	  the current node in.
+ *
+ * Unlike with foreach() it's not possible to detect if an early "break"
+ * occured by checking the value of the loop variable at the end of the loop.
+ * If you need this, it's recommended to use foreach() instead or manually keep
+ * track of a break occured using a boolean flag variable called e.g. "found".
+ *
+ * The caveats for foreach() apply equally here.
+ */
+#define for_each_node(type, var, lst) \
+	for (ForEachState var##__state = {(lst), 0}; \
+		 (var##__state.l != NIL && \
+		  var##__state.i < var##__state.l->length && \
+		  (var = lfirst_node(type, &var##__state.l->elements[var##__state.i]), true));\
+		 var##__state.i++)
+
 /*
  * forboth -
  *	  a convenience macro for advancing through two linked lists

base-commit: 8f0fd47fa33720dd09ad0ae74a8a583b9780e328
-- 
2.34.1

