/*
   This file is part of TALER
   Copyright (C) 2023 Taler Systems SA

   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
   Foundation; either version 3, or (at your option) any later version.

   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

   You should have received a copy of the GNU General Public License along with
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
/**
 * @file src/backenddb/helper.h
 * @brief shared internal definitions for postgres logic
 * @author Christian Grothoff
 */
#ifndef HELPER_H
#define HELPER_H

#include <gnunet/gnunet_util_lib.h>
#include <gnunet/gnunet_db_lib.h>
#include <gnunet/gnunet_time_lib.h>
#include <taler/taler_util.h>

/**
 * Type of the "cls" argument given to each of the functions in
 * our API.
 */
struct TALER_MERCHANTDB_PostgresContext
{

  /**
   * Postgres connection handle.
   */
  struct GNUNET_PQ_Context *conn;

  /**
   * Underlying configuration.
   */
  const struct GNUNET_CONFIGURATION_Handle *cfg;

  /**
   * Name of the currently active transaction, NULL if none is active.
   */
  const char *transaction_name;

  /**
   * Instance id ("merchant_id") that the search_path is currently
   * pointing at, or NULL if no per-instance schema is selected.
   * Owned by this struct; set by TALER_MERCHANTDB_set_instance().
   */
  char *current_merchant_id;

  /**
   * merchant_serial corresponding to @e current_merchant_id, or 0
   * if no per-instance schema is selected.  Used as the suffix in
   * per-instance prepared-statement names.
   */
  uint64_t current_merchant_serial;

  /**
   * Public key of the currently selected instance.  Populated by
   * TALER_MERCHANTDB_set_instance() together with @e current_merchant_id
   * and @e current_merchant_serial.  Used by call sites that emit
   * cross-process events (e.g. order-pay notifications) which carry the
   * instance public key in their payload.
   */
  struct TALER_MerchantPublicKeyP current_merchant_pub;

};


/**
 * How many times have we connected to the DB.
 */
extern uint64_t TMH_PG_prep_gen_;


/**
 * Convert the signed @a limit of a paginated listing into the row
 * limit to pass to SQL.  The sign selects the iteration direction and
 * the magnitude the number of rows, but computing `-limit` directly is
 * undefined behaviour for `INT64_MIN`, and the resulting value would
 * reach Postgres as `LIMIT -9223372036854775808`, which errors out.
 * `INT64_MIN` is therefore saturated to `INT64_MAX`.
 *
 * @param limit signed limit as given by the caller
 * @return absolute value of @a limit, without signed overflow
 */
static inline uint64_t
TALER_MERCHANTDB_abs_limit (int64_t limit)
{
  /* Postgres binds INT8 as a *signed* 64-bit integer, so a magnitude
     above INT64_MAX would reach LIMIT as a negative number. */
  if (INT64_MIN == limit)
    return (uint64_t) INT64_MAX;
  if (limit < 0)
    return (uint64_t) -limit;
  return (uint64_t) limit;
}


/**
 * Prepares SQL statement @a sql under @a name for
 * connection @a pg once.
 * Returns with #GNUNET_DB_STATUS_HARD_ERROR on failure.
 *
 * @param pg a `struct TALER_MERCHANTDB_PostgresContext`
 * @param name name to prepare the statement under
 * @param sql actual SQL text
 */
#define PREPARE(pg,name,sql)                      \
        do {                                            \
          static unsigned long long gen;                \
                                                        \
          if (gen < TMH_PG_prep_gen_)                   \
          {                                             \
            struct GNUNET_PQ_PreparedStatement ps[] = { \
              GNUNET_PQ_make_prepare (name, sql),       \
              GNUNET_PQ_PREPARED_STATEMENT_END          \
            };                                          \
                                                        \
            if (GNUNET_OK !=                            \
                GNUNET_PQ_prepare_statements (pg->conn, \
                                              ps))      \
            {                                           \
              GNUNET_break (0);                         \
              return GNUNET_DB_STATUS_HARD_ERROR;       \
            }                                           \
            gen = TMH_PG_prep_gen_;                     \
          }                                             \
        } while (0)


/**
 * Prepares SQL statement @a sql under no name ("") for
 * connection @a pg.  Useful for prepared statements that
 * should not be cached.
 * Returns with #GNUNET_DB_STATUS_HARD_ERROR on failure.
 *
 * @param pg a `struct TALER_MERCHANTDB_PostgresContext`
 * @param sql actual SQL text
 */
#define TMH_PQ_prepare_anon(pg,sql)              \
        do {                                     \
          if (GNUNET_OK !=                       \
              GNUNET_PQ_prepare_anon (pg->conn,  \
                                      sql)) {    \
            GNUNET_break (0);                    \
            return GNUNET_DB_STATUS_HARD_ERROR;  \
          } } while (0)


#endif
