/*
This file is part of GNUnet
Copyright (C) 2014, 2015, 2016, 2017, 2019, 2020 GNUnet e.V.
GNUnet is free software: you can redistribute it and/or modify it
under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.
GNUnet 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
Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
SPDX-License-Identifier: AGPL3.0-or-later
*/
/**
* @file pq/pq.c
* @brief helper functions for libpq (PostGres) interactions
* @author Sree Harsha Totakura
* @author Florian Dold
* @author Christian Grothoff
*/
#include "platform.h"
#include "pq.h"
PGresult *
GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db,
const char *name,
const struct GNUNET_PQ_QueryParam *params)
{
unsigned int len;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Running prepared statement `%s' on %p\n",
name,
db);
/* count the number of parameters */
len = 0;
for (unsigned int i = 0; 0 != params[i].num_params; i++)
len += params[i].num_params;
/* new scope to allow stack allocation without alloca */
{
/* Scratch buffer for temporary storage */
void *scratch[len];
/* Parameter array we are building for the query */
void *param_values[len];
int param_lengths[len];
int param_formats[len];
unsigned int off;
/* How many entries in the scratch buffer are in use? */
unsigned int soff;
PGresult *res;
int ret;
ConnStatusType status;
off = 0;
soff = 0;
for (unsigned int i = 0; 0 != params[i].num_params; i++)
{
const struct GNUNET_PQ_QueryParam *x = ¶ms[i];
ret = x->conv (x->conv_cls,
x->data,
x->size,
¶m_values[off],
¶m_lengths[off],
¶m_formats[off],
x->num_params,
&scratch[soff],
len - soff);
if (ret < 0)
{
for (off = 0; off < soff; off++)
GNUNET_free (scratch[off]);
return NULL;
}
soff += ret;
off += x->num_params;
}
GNUNET_assert (off == len);
GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
"pq",
"Executing prepared SQL statement `%s'\n",
name);
res = PQexecPrepared (db->conn,
name,
len,
(const char **) param_values,
param_lengths,
param_formats,
1);
GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
"pq",
"Execution of prepared SQL statement `%s' finished (%d)\n",
name,
PGRES_COMMAND_OK == PQresultStatus (res));
if ( (PGRES_COMMAND_OK != PQresultStatus (res)) &&
(CONNECTION_OK != (status = PQstatus (db->conn))) )
{
GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
"pq",
"Database disconnected on SQL statement `%s' (reconnecting)\n",
name);
GNUNET_PQ_reconnect (db);
res = NULL;
}
for (off = 0; off < soff; off++)
GNUNET_free (scratch[off]);
return res;
}
}
void
GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs)
{
for (unsigned int i = 0; NULL != rs[i].conv; i++)
if (NULL != rs[i].cleaner)
rs[i].cleaner (rs[i].cls,
rs[i].dst);
}
enum GNUNET_GenericReturnValue
GNUNET_PQ_extract_result (PGresult *result,
struct GNUNET_PQ_ResultSpec *rs,
int row)
{
unsigned int i;
if (NULL == result)
return GNUNET_SYSERR;
for (i = 0; NULL != rs[i].conv; i++)
{
struct GNUNET_PQ_ResultSpec *spec;
enum GNUNET_GenericReturnValue ret;
spec = &rs[i];
ret = spec->conv (spec->cls,
result,
row,
spec->fname,
&spec->dst_size,
spec->dst);
switch (ret)
{
case GNUNET_OK:
/* canonical case, continue below */
if (NULL != spec->is_null)
*spec->is_null = false;
break;
case GNUNET_NO:
if (spec->is_nullable)
{
if (NULL != spec->is_null)
*spec->is_null = true;
continue;
}
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"NULL field encountered for `%s' where non-NULL was required\n",
spec->fname);
goto cleanup;
case GNUNET_SYSERR:
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to extract field `%s'\n",
spec->fname);
GNUNET_break (0);
goto cleanup;
}
if (NULL != spec->result_size)
*spec->result_size = spec->dst_size;
}
return GNUNET_OK;
cleanup:
for (unsigned int j = 0; j < i; j++)
if (NULL != rs[j].cleaner)
rs[j].cleaner (rs[j].cls,
rs[j].dst);
return GNUNET_SYSERR;
}
/* end of pq/pq.c */