/* This file is part of GNUnet Copyright (C) 2014, 2015, 2016, 2017, 2019 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" /** * Execute a prepared statement. * * @param db database handle * @param name name of the prepared statement * @param params parameters to the statement * @return postgres result */ 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); 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; } } /** * Free all memory that was allocated in @a rs during * #GNUNET_PQ_extract_result(). * * @param rs reult specification to clean up */ 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); } /** * Extract results from a query result according to the given * specification. * * @param result result to process * @param[in,out] rs result specification to extract for * @param row row from the result to extract * @return * #GNUNET_YES if all results could be extracted * #GNUNET_SYSERR if a result was invalid (non-existing field) */ int GNUNET_PQ_extract_result (PGresult *result, struct GNUNET_PQ_ResultSpec *rs, int row) { if (NULL == result) return GNUNET_SYSERR; for (unsigned int i = 0; NULL != rs[i].conv; i++) { struct GNUNET_PQ_ResultSpec *spec; int ret; spec = &rs[i]; ret = spec->conv (spec->cls, result, row, spec->fname, &spec->dst_size, spec->dst); if (GNUNET_OK != ret) { 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; } if (NULL != spec->result_size) *spec->result_size = spec->dst_size; } return GNUNET_OK; } /* end of pq/pq.c */