/*-------------------------------------------------------------------------
 *
 * listutils.h
 *
 * Declarations for public utility functions related to lists.
 *
 * Copyright (c) Citus Data, Inc.
 *
 *-------------------------------------------------------------------------
 */

#ifndef CITUS_LISTUTILS_H
#define CITUS_LISTUTILS_H

#include "postgres.h"
#include "c.h"

#include "nodes/pg_list.h"

#include "utils/array.h"
#include "utils/hsearch.h"
#include "nodes/makefuncs.h"
#include "nodes/nodes.h"
#include "nodes/nodeFuncs.h"
#include "nodes/primnodes.h"

#include "pg_version_compat.h"


/*
 * ListCellAndListWrapper stores a list and list cell.
 * This struct is used for functionContext. When iterating a list
 * in separate function calls, we need both the list and the current cell.
 * Therefore this wrapper stores both of them.
 */
typedef struct ListCellAndListWrapper
{
	List *list;
	ListCell *listCell;
} ListCellAndListWrapper;

/*
 * foreach_ptr -
 *	  a convenience macro which loops through a pointer list without needing a
 *	  ListCell, just a declared pointer variable to store the pointer of the
 *	  cell in.
 *
 *   How it works:
 *	  - A ListCell is declared with the name {var}CellDoNotUse and used
 *	    throughout the for loop using ## to concat.
 *	  - To assign to var it needs to be done in the condition of the for loop,
 *	    because we cannot use the initializer since a ListCell* variable is
 *	    declared there.
 *	  - || true is used to always enter the loop when cell is not null even if
 *	    var is NULL.
 *
 * Note: PG17+ redefines foreach_ptr/foreach_int/foreach_oid with a different
 * 3-argument signature. We undef and redefine them to keep the 2-argument
 * form used throughout this codebase.
 */
#ifdef foreach_ptr
#undef foreach_ptr
#endif
#define foreach_ptr(var, l) \
	for (ListCell *(var ## CellDoNotUse) = list_head(l); \
		 (var ## CellDoNotUse) != NULL && \
		 (((var) = lfirst(var ## CellDoNotUse)) || true); \
		 var ## CellDoNotUse = lnext_compat(l, var ## CellDoNotUse))


/*
 * foreach_int -
 *	  a convenience macro which loops through an int list without needing a
 *	  ListCell, just a declared int variable to store the int of the cell in.
 *	  For explanation of how it works see foreach_ptr.
 */
#ifdef foreach_int
#undef foreach_int
#endif
#define foreach_int(var, l) \
	for (ListCell *(var ## CellDoNotUse) = list_head(l); \
		 (var ## CellDoNotUse) != NULL && \
		 (((var) = lfirst_int(var ## CellDoNotUse)) || true); \
		 var ## CellDoNotUse = lnext_compat(l, var ## CellDoNotUse))


/*
 * foreach_oid -
 *	  a convenience macro which loops through an oid list without needing a
 *	  ListCell, just a declared Oid variable to store the oid of the cell in.
 *	  For explanation of how it works see foreach_ptr.
 */
#ifdef foreach_oid
#undef foreach_oid
#endif
#define foreach_oid(var, l) \
	for (ListCell *(var ## CellDoNotUse) = list_head(l); \
		 (var ## CellDoNotUse) != NULL && \
		 (((var) = lfirst_oid(var ## CellDoNotUse)) || true); \
		 var ## CellDoNotUse = lnext_compat(l, var ## CellDoNotUse))

/*
 * forboth_ptr -
 *	  a convenience macro which loops through two lists of pointers at the same
 *	  time, without needing a ListCell. It only needs two declared pointer
 *	  variables to store the pointer of each of the two cells in.
 */
#define forboth_ptr(var1, l1, var2, l2) \
	for (ListCell *(var1 ## CellDoNotUse) = list_head(l1), \
		 *(var2 ## CellDoNotUse) = list_head(l2); \
		 (var1 ## CellDoNotUse) != NULL && \
		 (var2 ## CellDoNotUse) != NULL && \
		 (((var1) = lfirst(var1 ## CellDoNotUse)) || true) && \
		 (((var2) = lfirst(var2 ## CellDoNotUse)) || true); \
		 var1 ## CellDoNotUse = lnext_compat(l1, var1 ## CellDoNotUse), \
		 var2 ## CellDoNotUse = lnext_compat(l2, var2 ## CellDoNotUse) \
		 )

/*
 * forboth_ptr_oid -
 *	  a convenience macro which loops through two lists at the same time. The
 *	  first list should contain pointers and the second list should contain
 *	  Oids. It does not need a ListCell to do this. It only needs two declared
 *	  variables to store the pointer and the Oid of each of the two cells in.
 */
#define forboth_ptr_oid(var1, l1, var2, l2) \
	for (ListCell *(var1 ## CellDoNotUse) = list_head(l1), \
		 *(var2 ## CellDoNotUse) = list_head(l2); \
		 (var1 ## CellDoNotUse) != NULL && \
		 (var2 ## CellDoNotUse) != NULL && \
		 (((var1) = lfirst(var1 ## CellDoNotUse)) || true) && \
		 (((var2) = lfirst_oid(var2 ## CellDoNotUse)) || true); \
		 var1 ## CellDoNotUse = lnext_compat(l1, var1 ## CellDoNotUse), \
		 var2 ## CellDoNotUse = lnext_compat(l2, var2 ## CellDoNotUse) \
		 )

/*
 * forboth_int_oid -
 *	  a convenience macro which loops through two lists at the same time. The
 *	  first list should contain integers and the second list should contain
 *	  Oids. It does not need a ListCell to do this. It only needs two declared
 *	  variables to store the int and the Oid of each of the two cells in.
 */
#define forboth_int_oid(var1, l1, var2, l2) \
	for (ListCell *(var1 ## CellDoNotUse) = list_head(l1), \
		 *(var2 ## CellDoNotUse) = list_head(l2); \
		 (var1 ## CellDoNotUse) != NULL && \
		 (var2 ## CellDoNotUse) != NULL && \
		 (((var1) = lfirst_int(var1 ## CellDoNotUse)) || true) && \
		 (((var2) = lfirst_oid(var2 ## CellDoNotUse)) || true); \
		 var1 ## CellDoNotUse = lnext_compat(l1, var1 ## CellDoNotUse), \
		 var2 ## CellDoNotUse = lnext_compat(l2, var2 ## CellDoNotUse) \
		 )

/*
 * foreach_ptr_append -
 *	  a convenience macro which loops through a pointer List and can append list
 *	  elements without needing a ListCell or and index variable, just a declared
 *	  pointer variable to store the iterated values.
 *
 *	  PostgreSQL 13 changed the representation of Lists to expansible arrays,
 *	  not chains of cons-cells. This changes the costs for accessing and
 *	  mutating List contents. Therefore different implementations are provided.
 *
 *	  For more information, see postgres commit with sha
 *	  1cff1b95ab6ddae32faa3efe0d95a820dbfdc164
 */
#if PG_VERSION_NUM >= PG_VERSION_13

/*
 *	  How it works:
 *	  - An index is declared with the name {var}PositionDoNotUse and used
 *	    throughout the for loop using ## to concat.
 *	  - To assign to var it needs to be done in the condition of the for loop,
 *	    because we cannot use the initializer since the index variable is
 *	    declared there.
 *	  - || true is used to always enter the loop even if var is NULL.
 */
#define foreach_ptr_append(var, l) \
	for (int var ## PositionDoNotUse = 0; \
		 (var ## PositionDoNotUse) < list_length(l) && \
		 (((var) = list_nth(l, var ## PositionDoNotUse)) || true); \
		 var ## PositionDoNotUse ++)
#else
#define foreach_ptr_append(var, l) foreach_ptr(var, l)
#endif

static inline List*
CustomBuildTargetList(List* tlist, Index varNo)
{
	List		*result_tlist = NIL;
	ListCell	*lc;

	foreach (lc, tlist)
	{
		TargetEntry	*tle = (TargetEntry *) lfirst(lc);

		Var *var = makeVar(varNo,
						   tle->resno,
						   exprType((Node *) tle->expr),
						   exprTypmod((Node *) tle->expr),
						   exprCollation((Node *) tle->expr),
						   0);

		TargetEntry *newtle = makeTargetEntry((Expr *) var,
											  tle->resno,
											  tle->resname,
											  tle->resjunk);

		result_tlist = lappend(result_tlist, newtle);
	}

	return result_tlist;
}

#endif /* CITUS_LISTUTILS_H */
