#include <cstdlib>
#include <cstring>

#include <arpa/inet.h>

#include <iostream>
#include <string>

#include <libpq-fe.h>

namespace my_wrapper_library {

int ParseIntColumn(const PGresult* res, int tup_num,
                   // Having a 'std::string' here ensures that 'field_name' is
                   // null-terminated and safe to use with PQfnumber. Having
                   // 'std::string_view' here would be inherently unsafe.
                   const std::string& field_name) {
  const int field_num = PQfnumber(res, field_name.c_str());
  if (field_num == -1) {
    exit(1);
  }

  const char* value = PQgetvalue(res, tup_num, field_num);

  int result{};
  std::memcpy(&result, value, sizeof(result));
  return ntohl(result);
}

}  // namespace my_wrapper_library

static void exit_nicely(PGconn* conn) {
  PQfinish(conn);
  exit(1);
}

static uint64_t perform(PGconn* conn, const char* stmt_name) {
  PGresult* execution_res = execution_res =
      PQexecPrepared(conn, stmt_name, 0, NULL, NULL, NULL, 1);
  if (!execution_res || PQresultStatus(execution_res) != PGRES_TUPLES_OK)
    exit_nicely(conn);

  const int ntuples = PQntuples(execution_res);

  uint64_t meaningless_sum = 0;
  for (int i = 0; i < ntuples; ++i) {
    meaningless_sum += my_wrapper_library::ParseIntColumn(
        execution_res, i,
        // Here's the problem: this would be implicitly promoted to a
        // null-terminated string, and that results in an allocation. It could
        // be avoid by counted-string PQfnumber.
        "some_column_name");
  }

  PQclear(execution_res);
  return meaningless_sum;
}

static void perform_multiple(PGconn* conn) {
  PGresult* prepared_stmt = PQprepare(
      conn, "my_statement",
      "SELECT s as some_column_name FROM generate_series(1, 1000) s", 0, NULL);
  if (!prepared_stmt || PQresultStatus(prepared_stmt) != PGRES_COMMAND_OK)
    exit_nicely(conn);

  uint64_t meaningless_sum = 0;
  // Just run a bunch to get a perf-flame
  for (int i = 0; i < 10'000'000; ++i) {
    meaningless_sum += perform(conn, "my_statement");
  }

  PQclear(prepared_stmt);
  std::cout << "Meaningless_sum is: " << meaningless_sum << std::endl;
}

int main(int argc, char** argv) {
  const char* conninfo;
  PGconn* conn;

  if (argc > 1)
    conninfo = argv[1];
  else
    conninfo = "dbname = postgres";

  conn = PQconnectdb(conninfo);

  if (PQstatus(conn) != CONNECTION_OK) {
    std::cout << PQerrorMessage(conn);
    exit_nicely(conn);
  }

  perform_multiple(conn);

  PQfinish(conn);
  return 0;
}
