/**
 * @file safe_query.h
 * @brief Public surface of the safe-query (hierarchical-CQ) rewriter.
 *
 * The implementation lives in @c safe_query.c.  ProvSQL's main planner
 * hook (@c process_query in @c provsql.c) calls @c try_safe_query_rewrite
 * between the AGG-DISTINCT rewrite and @c get_provenance_attributes,
 * but only when the @c provsql.boolean_provenance GUC is on.  Returning
 * a non-NULL @c Query feeds the rewriter's output back through
 * @c process_query from the top; returning @c NULL makes the caller
 * fall through to the existing pipeline.
 */
#ifndef SAFE_QUERY_H
#define SAFE_QUERY_H

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

#include "provsql_utils.h"

/** @brief GUC: opt-in safe-query optimisation, declared in @c provsql.c. */
extern bool provsql_boolean_provenance;

/**
 * @brief Top-level entry point for the hierarchical-CQ rewriter.
 *
 * Runs the shape gate, then the hierarchy detector.  When both accept,
 * applies the rewrite and returns the rewritten @c Query (the caller
 * is expected to re-enter @c process_query on the result).  Returns
 * @c NULL when @p q is outside the safe-query scope.
 *
 * @param constants  Cached extension OIDs (from @c get_constants).
 * @param q          Input @c Query, modified in place by side-effect-free
 *                   helpers but @em not consumed; the rewriter
 *                   @c copyObject's it before mutating.
 * @return A fresh rewritten @c Query, or @c NULL to fall through.
 */
extern Query *try_safe_query_rewrite(const constants_t *constants, Query *q);

/**
 * @brief Per-atom marker spec for the inversion-free path.
 *
 * One entry per range-table atom (indexed by @c relid-1).  When @c valid, the
 * planner wraps that atom's provenance token in
 * @c annotate(prov, inversion_free_key(root_col, sec_col, factor)): a
 * per-input order key built from the tuple's root- and secondary-class column
 * values and its factor (@c SAFE_CERT_GUARD_FACTOR for the shared self-join
 * guard, else a per-factor id).
 */
typedef struct InvFreeMarker {
  bool       valid;
  AttrNumber root_col;   /* 1-based root-class column */
  AttrNumber sec_col;    /* 1-based secondary-class column */
  int        factor;     /* SAFE_CERT_GUARD_FACTOR, or a factor id */
} InvFreeMarker;

/**
 * @brief Per-query marker context for the inversion-free path, threaded through
 *        the recursive query rewrite to reach base inputs nested in subqueries.
 *
 * The detector runs on a *flattened* copy of the lineage query (single-base SPJ
 * subqueries / views inlined to their base relation in place, so range-table
 * positions are preserved).  The resulting per-atom markers are organised into
 * a context tree mirroring the *original* (un-flattened) query: for range-table
 * position @c i (0-based), @c markers[i] is the order marker for a base relation
 * sitting directly at @c i, while @c sub[i] is the child context for a flattened
 * subquery at @c i (whose own base atom carries the marker).  Exactly one of
 * @c markers[i].valid and @c sub[i]!=NULL holds for a certified atom.  A query
 * rewritten with a non-NULL context applies @c markers to its own base prov
 * Vars and passes @c sub[i] down when recursing into the subquery at @c i.
 */
typedef struct InvFreeMarkerCtx InvFreeMarkerCtx;
struct InvFreeMarkerCtx {
  int                natoms;    /* this query's range-table length */
  InvFreeMarker     *markers;   /* size natoms: direct base-atom markers */
  InvFreeMarkerCtx **sub;       /* size natoms: child ctx per flattened subquery, or NULL */
};

/**
 * @brief Inversion-free analysis of the lineage query @p q.
 *
 * Runs the detector on @p q itself (the query whose provenance lineage is being
 * built), so the certificate and the per-atom marker specs align with the
 * lineage by construction, independent of any read-once pre-pass.  On success
 * sets @p cert_out to a palloc'd @c C-prefixed serialised @c SafeCert recipe
 * (for the per-row root) and, when the marker model applies, @p markers_out to
 * a palloc'd array of @c *natoms_out @c InvFreeMarker (one per atom); otherwise
 * leaves them @c NULL.
 *
 * @return @c true if @p q is certified inversion-free (cert produced).
 */
extern bool inversion_free_analyze(const constants_t *constants, Query *q,
                                   char **cert_out, InvFreeMarker **markers_out,
                                   int *natoms_out);

/**
 * @brief PG 18 helper: strip the synthetic @c RTE_GROUP entry from
 *        @p q in place, resolving every grouped @c Var back to its
 *        base-table expression.  Defined in @c provsql.c.
 *
 * Idempotent: a no-op on earlier PostgreSQL versions and when
 * @c q->hasGroupRTE is already false.  The safe-query rewriter
 * applies it before the union-find pass so the range table is flat.
 */
extern void strip_group_rte_pg18(Query *q);

#endif /* SAFE_QUERY_H */
