/**
 * @file compatibility.h
 * @brief PostgreSQL cross-version compatibility shims for ProvSQL.
 *
 * ProvSQL supports a range of PostgreSQL major versions.  This header
 * centralises the small API differences between those versions so that
 * the rest of the codebase can call a single, stable interface.
 *
 * Currently handled differences:
 * - **List API (13+)**: @c list_delete_cell() and @c lnext() gained or
 *   lost a @p prev argument between PostgreSQL 12 and 13.  The
 *   @c my_list_delete_cell() and @c my_lnext() wrappers hide this.
 * - **list_insert_nth() (< 13)**: In PostgreSQL 12 and earlier the
 *   list implementation was a linked list and this helper did not exist;
 *   @c compatibility.c provides a backport.
 * - **Predefined function OIDs (< 14)**: The @c F_COUNT_ANY,
 *   @c F_COUNT_, and @c F_SUM_INT4 macros were introduced in PostgreSQL
 *   14.  Stable OID values for older releases are defined here.
 * - **list_make5() (13)**: existed pre-13, dropped by the PG13 list
 *   rewrite, re-added in PG14; backported for the PG13 gap as a macro
 *   over @c list_make4() + @c lappend().
 */
#ifndef COMPATIBILITY_H
#define COMPATIBILITY_H

#include "postgres.h"
#include "nodes/pg_list.h"

/**
 * @brief Version-agnostic wrapper around @c list_delete_cell().
 *
 * PostgreSQL 13 changed @c list_delete_cell() to no longer require the
 * previous cell pointer (because lists became arrays).  This inline
 * helper selects the correct call form at compile time.
 *
 * @param list  The list to modify.
 * @param cell  The cell to delete.
 * @param prev  The cell immediately before @p cell (ignored on PG ≥ 13).
 * @return      The modified list.
 */
static inline List *my_list_delete_cell(List *list, ListCell *cell, ListCell *prev) {
#if PG_VERSION_NUM >= 130000
  return list_delete_cell(list, cell);
#else
  return list_delete_cell(list, cell, prev);
#endif
}

/**
 * @brief Version-agnostic wrapper around @c lnext().
 *
 * PostgreSQL 13 added the list pointer parameter to @c lnext() to
 * support the array-based list implementation.  This inline helper
 * selects the correct call form at compile time.
 *
 * @param l  The list (ignored on PG < 13).
 * @param c  The current cell.
 * @return   The next cell, or @c NULL if @p c is the last element.
 */
static inline ListCell *my_lnext(const List *l, const ListCell *c)
{
#if PG_VERSION_NUM >= 130000
  return lnext(l, c);
#else
  return lnext(c);
#endif
}

#if PG_VERSION_NUM < 130000
/**
 * @brief Insert @p datum at position @p pos in @p list (PG < 13 backport).
 *
 * PostgreSQL 13 introduced @c list_insert_nth() when lists were
 * reimplemented as arrays.  This declaration provides the same function
 * for older PostgreSQL versions; the implementation lives in
 * @c compatibility.c.
 *
 * @param list   The list to insert into (may be @c NIL).
 * @param pos    Zero-based index at which to insert the new element.
 * @param datum  The value to insert.
 * @return       The (possibly reallocated) list.
 */
List *list_insert_nth(List *list, int pos, void *datum);
#endif

#if PG_VERSION_NUM < 140000
/** @brief OID of @c count(*) / @c count(any) aggregate function (pre-PG 14). */
#define F_COUNT_ANY 2147
/** @brief OID of @c count() aggregate function (pre-PG 14). */
#define F_COUNT_ 2803
/** @brief OID of @c sum(int4) aggregate function (pre-PG 14). */
#define F_SUM_INT4 2108
#endif

#if PG_VERSION_NUM >= 130000 && PG_VERSION_NUM < 140000
/** @brief Five-element list constructor.  The linked-list implementation
 * (pre-PG 13) had it as a macro; the PG13 array rewrite dropped it; PG14
 * brought it back.  Backport it for the PG13 gap only. */
#define list_make5(x1, x2, x3, x4, x5) lappend(list_make4(x1, x2, x3, x4), x5)
#endif

#if PG_VERSION_NUM < 130000
/** @brief Alignment codes for the array routines (@c construct_array /
 * @c deconstruct_array).  The @c TYPALIGN_* macros (in @c catalog/pg_type.h)
 * were introduced in PostgreSQL 13; on PG 10-12 the alignment is passed as
 * the older @c 'i' / @c 'c' characters. */
#define TYPALIGN_INT 'i'
#define TYPALIGN_CHAR 'c'
#endif

#if PG_VERSION_NUM < 140000
/** @brief OID of the @c array_agg(anynonarray) aggregate (pre-PG 14).
 * The @c F_ARRAY_AGG_ANYNONARRAY macro only exists since PostgreSQL 14,
 * when @c fmgroids.h gained overload-disambiguated names for aggregates;
 * before that, aggregates had no @c fmgroids.h entry at all.  Same
 * stable OID. */
#define F_ARRAY_AGG_ANYNONARRAY 2335
#endif

#endif /* COMPATIBILITY_H */
