Andres Freund <and...@anarazel.de> writes:
> On 2019-07-31 19:40:09 -0400, Tom Lane wrote:
>> That makes the other idea (of a foreach-ish macro declaring the
>> listcell value variable) problematic, too :-(.

> Hm. One way partially around that would be using an anonymous struct
> inside the for(). Something like
> #define foreach_node(membertype, name, lst)   \
> for (struct {membertype *node; ListCell *lc; const List *l; int i;} name = 
> {...}; \
>      ...)
> which then would allow code like

> foreach_node(OpExpr, cur, list)
> {
>     do_something_with_node(cur.node);
>     foreach_delete_current(cur);
> }

I'm hesitant to change the look of our loops quite that much, mainly
because it'll be a pain for back-patching.  If you write some code
for HEAD like this, and then have to back-patch it, you'll need to
insert/change significantly more code than if it's just a matter
of whether there's a ListCell variable or not.

I experimented with the "aforeach" idea I suggested upthread,
to the extent of writing the macros and then converting
parse_clause.c (a file chosen more or less at random) to use
aforeach instead of foreach.  I was somewhat surprised to find
that every single foreach() did convert pleasantly.  (There are
several forboth's that I didn't try to do anything with, though.)

If we do go in this direction, I wouldn't suggest trying to
actually do wholesale conversion of existing code like this;
that seems more likely to create back-patching land mines than
do anything helpful.  I am slightly tempted to try to convert
everyplace using foreach_delete_current, though, since those
loops are different from v12 already.

Thoughts?

                        regards, tom lane

diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index 2a6b2ff..39d8d8e 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -117,8 +117,6 @@ static Node *transformFrameOffset(ParseState *pstate, int frameOptions,
 void
 transformFromClause(ParseState *pstate, List *frmList)
 {
-	ListCell   *fl;
-
 	/*
 	 * The grammar will have produced a list of RangeVars, RangeSubselects,
 	 * RangeFunctions, and/or JoinExprs. Transform each one (possibly adding
@@ -128,9 +126,9 @@ transformFromClause(ParseState *pstate, List *frmList)
 	 * Note we must process the items left-to-right for proper handling of
 	 * LATERAL references.
 	 */
-	foreach(fl, frmList)
+	aforeach(frmList)
 	{
-		Node	   *n = lfirst(fl);
+		Node	   *n = (Node *) aforeach_current();
 		RangeTblEntry *rte;
 		int			rtindex;
 		List	   *namespace;
@@ -267,11 +265,10 @@ extractRemainingColumns(List *common_colnames,
 	{
 		char	   *colname = strVal(lfirst(lnames));
 		bool		match = false;
-		ListCell   *cnames;
 
-		foreach(cnames, common_colnames)
+		aforeach(common_colnames)
 		{
-			char	   *ccolname = strVal(lfirst(cnames));
+			char	   *ccolname = strVal(aforeach_current());
 
 			if (strcmp(colname, ccolname) == 0)
 			{
@@ -475,7 +472,6 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 	List	   *coldeflists = NIL;
 	bool		is_lateral;
 	RangeTblEntry *rte;
-	ListCell   *lc;
 
 	/*
 	 * We make lateral_only names of this level visible, whether or not the
@@ -505,9 +501,9 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 	 * Likewise, collect column definition lists if there were any.  But
 	 * complain if we find one here and the RangeFunction has one too.
 	 */
-	foreach(lc, r->functions)
+	aforeach(r->functions)
 	{
-		List	   *pair = (List *) lfirst(lc);
+		List	   *pair = aforeach_current_node(List);
 		Node	   *fexpr;
 		List	   *coldeflist;
 		Node	   *newfexpr;
@@ -551,11 +547,9 @@ transformRangeFunction(ParseState *pstate, RangeFunction *r)
 				fc->over == NULL &&
 				coldeflist == NIL)
 			{
-				ListCell   *lc;
-
-				foreach(lc, fc->args)
+				aforeach(fc->args)
 				{
-					Node	   *arg = (Node *) lfirst(lc);
+					Node	   *arg = (Node *) aforeach_current();
 					FuncCall   *newfc;
 
 					last_srf = pstate->p_last_srf;
@@ -700,7 +694,6 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
 	Oid			docType;
 	RangeTblEntry *rte;
 	bool		is_lateral;
-	ListCell   *col;
 	char	  **names;
 	int			colno;
 
@@ -743,9 +736,9 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
 	names = palloc(sizeof(char *) * list_length(rtf->columns));
 
 	colno = 0;
-	foreach(col, rtf->columns)
+	aforeach(rtf->columns)
 	{
-		RangeTableFuncCol *rawc = (RangeTableFuncCol *) lfirst(col);
+		RangeTableFuncCol *rawc = aforeach_current_node(RangeTableFuncCol);
 		Oid			typid;
 		int32		typmod;
 		Node	   *colexpr;
@@ -837,15 +830,13 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
 	/* Namespaces, if any, also need to be transformed */
 	if (rtf->namespaces != NIL)
 	{
-		ListCell   *ns;
-		ListCell   *lc2;
 		List	   *ns_uris = NIL;
 		List	   *ns_names = NIL;
 		bool		default_ns_seen = false;
 
-		foreach(ns, rtf->namespaces)
+		aforeach(rtf->namespaces)
 		{
-			ResTarget  *r = (ResTarget *) lfirst(ns);
+			ResTarget  *r = aforeach_current_node(ResTarget);
 			Node	   *ns_uri;
 
 			Assert(IsA(r, ResTarget));
@@ -858,9 +849,9 @@ transformRangeTableFunc(ParseState *pstate, RangeTableFunc *rtf)
 			/* Verify consistency of name list: no dupes, only one DEFAULT */
 			if (r->name != NULL)
 			{
-				foreach(lc2, ns_names)
+				aforeach(ns_names)
 				{
-					Value	   *ns_node = (Value *) lfirst(lc2);
+					Value	   *ns_node = (Value *) aforeach_current();
 
 					if (ns_node == NULL)
 						continue;
@@ -1268,19 +1259,17 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 		if (j->isNatural)
 		{
 			List	   *rlist = NIL;
-			ListCell   *lx,
-					   *rx;
 
 			Assert(j->usingClause == NIL);	/* shouldn't have USING() too */
 
-			foreach(lx, l_colnames)
+			aforeach(l_colnames)
 			{
-				char	   *l_colname = strVal(lfirst(lx));
+				char	   *l_colname = strVal(aforeach_current());
 				Value	   *m_name = NULL;
 
-				foreach(rx, r_colnames)
+				aforeach(r_colnames)
 				{
-					char	   *r_colname = strVal(lfirst(rx));
+					char	   *r_colname = strVal(aforeach_current());
 
 					if (strcmp(l_colname, r_colname) == 0)
 					{
@@ -1313,24 +1302,21 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 			List	   *ucols = j->usingClause;
 			List	   *l_usingvars = NIL;
 			List	   *r_usingvars = NIL;
-			ListCell   *ucol;
 
 			Assert(j->quals == NULL);	/* shouldn't have ON() too */
 
-			foreach(ucol, ucols)
+			aforeach(ucols)
 			{
-				char	   *u_colname = strVal(lfirst(ucol));
-				ListCell   *col;
-				int			ndx;
+				char	   *u_colname = strVal(aforeach_current());
 				int			l_index = -1;
 				int			r_index = -1;
 				Var		   *l_colvar,
 						   *r_colvar;
 
 				/* Check for USING(foo,foo) */
-				foreach(col, res_colnames)
+				aforeach(res_colnames)
 				{
-					char	   *res_colname = strVal(lfirst(col));
+					char	   *res_colname = strVal(aforeach_current());
 
 					if (strcmp(res_colname, u_colname) == 0)
 						ereport(ERROR,
@@ -1340,10 +1326,9 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 				}
 
 				/* Find it in left input */
-				ndx = 0;
-				foreach(col, l_colnames)
+				aforeach(l_colnames)
 				{
-					char	   *l_colname = strVal(lfirst(col));
+					char	   *l_colname = strVal(aforeach_current());
 
 					if (strcmp(l_colname, u_colname) == 0)
 					{
@@ -1352,9 +1337,8 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 									(errcode(ERRCODE_AMBIGUOUS_COLUMN),
 									 errmsg("common column name \"%s\" appears more than once in left table",
 											u_colname)));
-						l_index = ndx;
+						l_index = aforeach_current_index();
 					}
-					ndx++;
 				}
 				if (l_index < 0)
 					ereport(ERROR,
@@ -1363,10 +1347,9 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 									u_colname)));
 
 				/* Find it in right input */
-				ndx = 0;
-				foreach(col, r_colnames)
+				aforeach(r_colnames)
 				{
-					char	   *r_colname = strVal(lfirst(col));
+					char	   *r_colname = strVal(aforeach_current());
 
 					if (strcmp(r_colname, u_colname) == 0)
 					{
@@ -1375,9 +1358,8 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 									(errcode(ERRCODE_AMBIGUOUS_COLUMN),
 									 errmsg("common column name \"%s\" appears more than once in right table",
 											u_colname)));
-						r_index = ndx;
+						r_index = aforeach_current_index();
 					}
-					ndx++;
 				}
 				if (r_index < 0)
 					ereport(ERROR,
@@ -1390,7 +1372,7 @@ transformFromClauseItem(ParseState *pstate, Node *n,
 				r_colvar = list_nth(r_colvars, r_index);
 				r_usingvars = lappend(r_usingvars, r_colvar);
 
-				res_colnames = lappend(res_colnames, lfirst(ucol));
+				res_colnames = lappend(res_colnames, aforeach_current());
 				res_colvars = lappend(res_colvars,
 									  buildMergedJoinVar(pstate,
 														 j->jointype,
@@ -1643,11 +1625,9 @@ makeNamespaceItem(RangeTblEntry *rte, bool rel_visible, bool cols_visible,
 static void
 setNamespaceColumnVisibility(List *namespace, bool cols_visible)
 {
-	ListCell   *lc;
-
-	foreach(lc, namespace)
+	aforeach(namespace)
 	{
-		ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
+		ParseNamespaceItem *nsitem = (ParseNamespaceItem *) aforeach_current();
 
 		nsitem->p_cols_visible = cols_visible;
 	}
@@ -1660,11 +1640,9 @@ setNamespaceColumnVisibility(List *namespace, bool cols_visible)
 static void
 setNamespaceLateralState(List *namespace, bool lateral_only, bool lateral_ok)
 {
-	ListCell   *lc;
-
-	foreach(lc, namespace)
+	aforeach(namespace)
 	{
-		ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
+		ParseNamespaceItem *nsitem = (ParseNamespaceItem *) aforeach_current();
 
 		nsitem->p_lateral_only = lateral_only;
 		nsitem->p_lateral_ok = lateral_ok;
@@ -1822,8 +1800,6 @@ static TargetEntry *
 findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist,
 						 ParseExprKind exprKind)
 {
-	ListCell   *tl;
-
 	/*----------
 	 * Handle two special cases as mandated by the SQL92 spec:
 	 *
@@ -1895,9 +1871,9 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist,
 		{
 			TargetEntry *target_result = NULL;
 
-			foreach(tl, *tlist)
+			aforeach(*tlist)
 			{
-				TargetEntry *tle = (TargetEntry *) lfirst(tl);
+				TargetEntry *tle = aforeach_current_node(TargetEntry);
 
 				if (!tle->resjunk &&
 					strcmp(tle->resname, name) == 0)
@@ -1944,9 +1920,9 @@ findTargetlistEntrySQL92(ParseState *pstate, Node *node, List **tlist,
 					 parser_errposition(pstate, location)));
 
 		target_pos = intVal(val);
-		foreach(tl, *tlist)
+		aforeach(*tlist)
 		{
-			TargetEntry *tle = (TargetEntry *) lfirst(tl);
+			TargetEntry *tle = aforeach_current_node(TargetEntry);
 
 			if (!tle->resjunk)
 			{
@@ -1990,7 +1966,6 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist,
 						 ParseExprKind exprKind)
 {
 	TargetEntry *target_result;
-	ListCell   *tl;
 	Node	   *expr;
 
 	/*
@@ -2002,9 +1977,9 @@ findTargetlistEntrySQL99(ParseState *pstate, Node *node, List **tlist,
 	 */
 	expr = transformExpr(pstate, node, exprKind);
 
-	foreach(tl, *tlist)
+	aforeach(*tlist)
 	{
-		TargetEntry *tle = (TargetEntry *) lfirst(tl);
+		TargetEntry *tle = aforeach_current_node(TargetEntry);
 		Node	   *texpr;
 
 		/*
@@ -2094,7 +2069,6 @@ flatten_grouping_sets(Node *expr, bool toplevel, bool *hasGroupingSets)
 		case T_GroupingSet:
 			{
 				GroupingSet *gset = (GroupingSet *) expr;
-				ListCell   *l2;
 				List	   *result_set = NIL;
 
 				if (hasGroupingSets)
@@ -2109,9 +2083,9 @@ flatten_grouping_sets(Node *expr, bool toplevel, bool *hasGroupingSets)
 				if (toplevel && gset->kind == GROUPING_SET_EMPTY)
 					return (Node *) NIL;
 
-				foreach(l2, gset->content)
+				aforeach(gset->content)
 				{
-					Node	   *n1 = lfirst(l2);
+					Node	   *n1 = (Node *) aforeach_current();
 					Node	   *n2 = flatten_grouping_sets(n1, false, NULL);
 
 					if (IsA(n1, GroupingSet) &&
@@ -2139,13 +2113,13 @@ flatten_grouping_sets(Node *expr, bool toplevel, bool *hasGroupingSets)
 		case T_List:
 			{
 				List	   *result = NIL;
-				ListCell   *l;
 
-				foreach(l, (List *) expr)
+				aforeach((List *) expr)
 				{
-					Node	   *n = flatten_grouping_sets(lfirst(l), toplevel, hasGroupingSets);
+					Node	   *n = (Node *) aforeach_current();
 
-					if (n != (Node *) NIL)
+					n = flatten_grouping_sets(n, toplevel, hasGroupingSets);
+					if (n)
 					{
 						if (IsA(n, List))
 							result = list_concat(result, (List *) n);
@@ -2200,8 +2174,6 @@ transformGroupClauseExpr(List **flatresult, Bitmapset *seen_local,
 
 	if (tle->ressortgroupref > 0)
 	{
-		ListCell   *sl;
-
 		/*
 		 * Eliminate duplicates (GROUP BY x, x) but only at local level.
 		 * (Duplicates in grouping sets can affect the number of returned
@@ -2239,10 +2211,9 @@ transformGroupClauseExpr(List **flatresult, Bitmapset *seen_local,
 		 * another sort step is going to be inevitable, but that's the
 		 * planner's problem.
 		 */
-
-		foreach(sl, sortClause)
+		aforeach(sortClause)
 		{
-			SortGroupClause *sc = (SortGroupClause *) lfirst(sl);
+			SortGroupClause *sc = aforeach_current_node(SortGroupClause);
 
 			if (sc->tleSortGroupRef == tle->ressortgroupref)
 			{
@@ -2298,11 +2269,10 @@ transformGroupClauseList(List **flatresult,
 {
 	Bitmapset  *seen_local = NULL;
 	List	   *result = NIL;
-	ListCell   *gl;
 
-	foreach(gl, list)
+	aforeach(list)
 	{
-		Node	   *gexpr = (Node *) lfirst(gl);
+		Node	   *gexpr = (Node *) aforeach_current();
 
 		Index		ref = transformGroupClauseExpr(flatresult,
 												   seen_local,
@@ -2349,14 +2319,13 @@ transformGroupingSet(List **flatresult,
 					 List **targetlist, List *sortClause,
 					 ParseExprKind exprKind, bool useSQL99, bool toplevel)
 {
-	ListCell   *gl;
 	List	   *content = NIL;
 
 	Assert(toplevel || gset->kind != GROUPING_SET_SETS);
 
-	foreach(gl, gset->content)
+	aforeach(gset->content)
 	{
-		Node	   *n = lfirst(gl);
+		Node	   *n = (Node *) aforeach_current();
 
 		if (IsA(n, List))
 		{
@@ -2371,7 +2340,7 @@ transformGroupingSet(List **flatresult,
 		}
 		else if (IsA(n, GroupingSet))
 		{
-			GroupingSet *gset2 = (GroupingSet *) lfirst(gl);
+			GroupingSet *gset2 = (GroupingSet *) n;
 
 			content = lappend(content, transformGroupingSet(flatresult,
 															pstate, gset2,
@@ -2455,7 +2424,6 @@ transformGroupClause(ParseState *pstate, List *grouplist, List **groupingSets,
 	List	   *result = NIL;
 	List	   *flat_grouplist;
 	List	   *gsets = NIL;
-	ListCell   *gl;
 	bool		hasGroupingSets = false;
 	Bitmapset  *seen_local = NULL;
 
@@ -2481,9 +2449,9 @@ transformGroupClause(ParseState *pstate, List *grouplist, List **groupingSets,
 													exprLocation((Node *) grouplist)));
 	}
 
-	foreach(gl, flat_grouplist)
+	aforeach(flat_grouplist)
 	{
-		Node	   *gexpr = (Node *) lfirst(gl);
+		Node	   *gexpr = (Node *) aforeach_current();
 
 		if (IsA(gexpr, GroupingSet))
 		{
@@ -2555,11 +2523,10 @@ transformSortClause(ParseState *pstate,
 					bool useSQL99)
 {
 	List	   *sortlist = NIL;
-	ListCell   *olitem;
 
-	foreach(olitem, orderlist)
+	aforeach(orderlist)
 	{
-		SortBy	   *sortby = (SortBy *) lfirst(olitem);
+		SortBy	   *sortby = aforeach_current_node(SortBy);
 		TargetEntry *tle;
 
 		if (useSQL99)
@@ -2587,11 +2554,10 @@ transformWindowDefinitions(ParseState *pstate,
 {
 	List	   *result = NIL;
 	Index		winref = 0;
-	ListCell   *lc;
 
-	foreach(lc, windowdefs)
+	aforeach(windowdefs)
 	{
-		WindowDef  *windef = (WindowDef *) lfirst(lc);
+		WindowDef  *windef = aforeach_current_node(WindowDef);
 		WindowClause *refwc = NULL;
 		List	   *partitionClause;
 		List	   *orderClause;
@@ -2805,8 +2771,6 @@ transformDistinctClause(ParseState *pstate,
 						List **targetlist, List *sortClause, bool is_agg)
 {
 	List	   *result = NIL;
-	ListCell   *slitem;
-	ListCell   *tlitem;
 
 	/*
 	 * The distinctClause should consist of all ORDER BY items followed by all
@@ -2823,9 +2787,9 @@ transformDistinctClause(ParseState *pstate,
 	 * effect will be that the TLE value will be made unique according to both
 	 * sortops.
 	 */
-	foreach(slitem, sortClause)
+	aforeach(sortClause)
 	{
-		SortGroupClause *scl = (SortGroupClause *) lfirst(slitem);
+		SortGroupClause *scl = aforeach_current_node(SortGroupClause);
 		TargetEntry *tle = get_sortgroupclause_tle(scl, *targetlist);
 
 		if (tle->resjunk)
@@ -2843,9 +2807,9 @@ transformDistinctClause(ParseState *pstate,
 	 * Now add any remaining non-resjunk tlist items, using default sort/group
 	 * semantics for their data types.
 	 */
-	foreach(tlitem, *targetlist)
+	aforeach(*targetlist)
 	{
-		TargetEntry *tle = (TargetEntry *) lfirst(tlitem);
+		TargetEntry *tle = aforeach_current_node(TargetEntry);
 
 		if (tle->resjunk)
 			continue;			/* ignore junk */
@@ -2902,9 +2866,9 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist,
 	 * Also notice that we could have duplicate DISTINCT ON expressions and
 	 * hence duplicate entries in sortgrouprefs.)
 	 */
-	foreach(lc, distinctlist)
+	aforeach(distinctlist)
 	{
-		Node	   *dexpr = (Node *) lfirst(lc);
+		Node	   *dexpr = (Node *) aforeach_current();
 		int			sortgroupref;
 		TargetEntry *tle;
 
@@ -2923,9 +2887,9 @@ transformDistinctOnClause(ParseState *pstate, List *distinctlist,
 	 * in DISTINCT ON.
 	 */
 	skipped_sortitem = false;
-	foreach(lc, sortClause)
+	aforeach(sortClause)
 	{
-		SortGroupClause *scl = (SortGroupClause *) lfirst(lc);
+		SortGroupClause *scl = aforeach_current_node(SortGroupClause);
 
 		if (list_member_int(sortgrouprefs, scl->tleSortGroupRef))
 		{
@@ -3021,11 +2985,10 @@ resolve_unique_index_expr(ParseState *pstate, InferClause *infer,
 						  Relation heapRel)
 {
 	List	   *result = NIL;
-	ListCell   *l;
 
-	foreach(l, infer->indexElems)
+	aforeach(infer->indexElems)
 	{
-		IndexElem  *ielem = (IndexElem *) lfirst(l);
+		IndexElem  *ielem = aforeach_current_node(IndexElem);
 		InferenceElem *pInfer = makeNode(InferenceElem);
 		Node	   *parse;
 
@@ -3422,16 +3385,15 @@ Index
 assignSortGroupRef(TargetEntry *tle, List *tlist)
 {
 	Index		maxRef;
-	ListCell   *l;
 
 	if (tle->ressortgroupref)	/* already has one? */
 		return tle->ressortgroupref;
 
 	/* easiest way to pick an unused refnumber: max used + 1 */
 	maxRef = 0;
-	foreach(l, tlist)
+	aforeach(tlist)
 	{
-		Index		ref = ((TargetEntry *) lfirst(l))->ressortgroupref;
+		Index		ref = aforeach_current_node(TargetEntry)->ressortgroupref;
 
 		if (ref > maxRef)
 			maxRef = ref;
@@ -3463,15 +3425,14 @@ bool
 targetIsInSortList(TargetEntry *tle, Oid sortop, List *sortList)
 {
 	Index		ref = tle->ressortgroupref;
-	ListCell   *l;
 
 	/* no need to scan list if tle has no marker */
 	if (ref == 0)
 		return false;
 
-	foreach(l, sortList)
+	aforeach(sortList)
 	{
-		SortGroupClause *scl = (SortGroupClause *) lfirst(l);
+		SortGroupClause *scl = aforeach_current_node(SortGroupClause);
 
 		if (scl->tleSortGroupRef == ref &&
 			(sortop == InvalidOid ||
@@ -3489,11 +3450,9 @@ targetIsInSortList(TargetEntry *tle, Oid sortop, List *sortList)
 static WindowClause *
 findWindowClause(List *wclist, const char *name)
 {
-	ListCell   *l;
-
-	foreach(l, wclist)
+	aforeach(wclist)
 	{
-		WindowClause *wc = (WindowClause *) lfirst(l);
+		WindowClause *wc = aforeach_current_node(WindowClause);
 
 		if (wc->name && strcmp(wc->name, name) == 0)
 			return wc;
diff --git a/src/include/nodes/pg_list.h b/src/include/nodes/pg_list.h
index 1463408..21d0a67 100644
--- a/src/include/nodes/pg_list.h
+++ b/src/include/nodes/pg_list.h
@@ -381,6 +381,59 @@ lnext(const List *l, const ListCell *c)
 #define foreach_current_index(cell)  (cell##__state.i)
 
 /*
+ * aforeach (anonymous foreach) -
+ *	  another convenience macro for looping through a list
+ *
+ * This is the same as foreach() except that no ListCell variable is needed.
+ * Instead, reference the current list element with the appropriate variant
+ * of aforeach_current().
+ */
+#define aforeach(lst)	\
+	for (ForEachState aforeach__state = {(lst), 0}; \
+		 aforeach__state.l != NIL && \
+		 aforeach__state.i < aforeach__state.l->length; \
+		 aforeach__state.i++)
+
+/*
+ * aforeach_current... -
+ *
+ * Access the current element of the most closely nested aforeach() loop.
+ * These exist in the same variants that lfirst...() has.
+ */
+#define aforeach_current()	\
+	(aforeach__state.l->elements[aforeach__state.i].ptr_value)
+#define aforeach_current_int()	\
+	(aforeach__state.l->elements[aforeach__state.i].int_value)
+#define aforeach_current_oid()	\
+	(aforeach__state.l->elements[aforeach__state.i].oid_value)
+#define aforeach_current_node(type)	\
+	castNode(type, aforeach_current())
+
+/*
+ * aforeach_delete_current -
+ *	  delete the current list element from the List associated with the most
+ *	  closely nested aforeach() loop, returning the new List pointer.
+ *
+ * This is equivalent to list_delete_nth_cell(), but it also adjusts the
+ * aforeach loop's state so that no list elements will be missed.  Do not
+ * delete elements from an active aforeach loop's list in any other way!
+ */
+#define aforeach_delete_current()	\
+	((List *) (aforeach__state.l = list_delete_nth_cell(aforeach__state.l, \
+														aforeach__state.i--)))
+
+/*
+ * aforeach_current_index -
+ *	  get the zero-based list index of the most closely nested aforeach()
+ *	  loop's current element.
+ *
+ * Beware of using this after aforeach_delete_current(); the value will be
+ * out of sync for the rest of the current loop iteration.  Anyway, since
+ * you just deleted the current element, the value is pretty meaningless.
+ */
+#define aforeach_current_index()  (aforeach__state.i)
+
+/*
  * for_each_cell -
  *	  a convenience macro which loops through a list starting from a
  *	  specified cell

Reply via email to