From 1291a7a2c3a78fa72117d0a0dc24bace620ecac8 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 v6 1/3] 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 | 51 ++++++++++++++++++++++++++++++++-----
 1 file changed, 45 insertions(+), 6 deletions(-)

diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 529a382d284..d3403c63aa9 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,44 @@ for_each_cell_setup(const List *lst, const ListCell *initcell)
 	return r;
 }
 
+#define foreach_internal(var, lst, func) \
+	for (ForEachState var##__state = {(lst), 0}; \
+		 (var##__state.l != NIL && \
+		  var##__state.i < var##__state.l->length && \
+		 (var = func(&var##__state.l->elements[var##__state.i]), true)); \
+		 var##__state.i++)
+
+/*
+ * Convenience macros that loop through a list of pointers/ints/oids/xids
+ * without needing a "ListCell *", just a declared variable to store of the
+ * respective type to store the current value 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
+ * track if a break occured by using a boolean flag variable called e.g.
+ * "found".
+ *
+ * The caveats for foreach() apply equally here.
+ */
+#define foreach_ptr(var, lst)   foreach_internal(var, lst, lfirst)
+#define foreach_int(var, lst)   foreach_internal(var, lst, lfirst_int)
+#define foreach_oid(var, lst)   foreach_internal(var, lst, lfirst_oid)
+#define foreach_xid(var, lst)   foreach_internal(var, lst, lfirst_xid)
+
+
+/*
+ * foreach_node -
+ *	  The same as foreach_ptr, but when assertions are enabled it verifies that
+ *	  the element is of the specified node type (using its nodeTag()).
+ */
+#define foreach_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: 0e917508b89dd21c5bcd9183e77585f01055a20d
-- 
2.34.1

