error handling
[oweals/gnunet.git] / src / pq / pq.c
1 /*
2    This file is part of GNUnet
3    Copyright (C) 2014, 2015, 2016, 2017, 2019 GNUnet e.V.
4
5    GNUnet is free software: you can redistribute it and/or modify it
6    under the terms of the GNU Affero General Public License as published
7    by the Free Software Foundation, either version 3 of the License,
8    or (at your option) any later version.
9
10    GNUnet is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Affero General Public License for more details.
14
15    You should have received a copy of the GNU Affero General Public License
16    along with this program.  If not, see <http://www.gnu.org/licenses/>.
17
18      SPDX-License-Identifier: AGPL3.0-or-later
19  */
20 /**
21  * @file pq/pq.c
22  * @brief helper functions for libpq (PostGres) interactions
23  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
24  * @author Florian Dold
25  * @author Christian Grothoff
26  */
27 #include "platform.h"
28 #include "pq.h"
29
30 /**
31  * Execute a prepared statement.
32  *
33  * @param db database handle
34  * @param name name of the prepared statement
35  * @param params parameters to the statement
36  * @return postgres result
37  */
38 PGresult *
39 GNUNET_PQ_exec_prepared (struct GNUNET_PQ_Context *db,
40                          const char *name,
41                          const struct GNUNET_PQ_QueryParam *params)
42 {
43   unsigned int len;
44
45   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
46               "Running prepared statement `%s' on %p\n",
47               name,
48               db);
49   /* count the number of parameters */
50   len = 0;
51   for (unsigned int i = 0; 0 != params[i].num_params; i++)
52     len += params[i].num_params;
53
54   /* new scope to allow stack allocation without alloca */
55   {
56     /* Scratch buffer for temporary storage */
57     void *scratch[len];
58     /* Parameter array we are building for the query */
59     void *param_values[len];
60     int param_lengths[len];
61     int param_formats[len];
62     unsigned int off;
63     /* How many entries in the scratch buffer are in use? */
64     unsigned int soff;
65     PGresult *res;
66     int ret;
67     ConnStatusType status;
68
69     off = 0;
70     soff = 0;
71     for (unsigned int i = 0; 0 != params[i].num_params; i++)
72     {
73       const struct GNUNET_PQ_QueryParam *x = &params[i];
74
75       ret = x->conv (x->conv_cls,
76                      x->data,
77                      x->size,
78                      &param_values[off],
79                      &param_lengths[off],
80                      &param_formats[off],
81                      x->num_params,
82                      &scratch[soff],
83                      len - soff);
84       if (ret < 0)
85       {
86         for (off = 0; off < soff; off++)
87           GNUNET_free (scratch[off]);
88         return NULL;
89       }
90       soff += ret;
91       off += x->num_params;
92     }
93     GNUNET_assert (off == len);
94     GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
95                      "pq",
96                      "Executing prepared SQL statement `%s'\n",
97                      name);
98     res = PQexecPrepared (db->conn,
99                           name,
100                           len,
101                           (const char **) param_values,
102                           param_lengths,
103                           param_formats,
104                           1);
105     if ( (PGRES_COMMAND_OK != PQresultStatus (res)) &&
106          (CONNECTION_OK != (status = PQstatus (db->conn))) )
107     {
108       GNUNET_log_from (GNUNET_ERROR_TYPE_DEBUG,
109                        "pq",
110                        "Database disconnected on SQL statement `%s' (reconnecting)\n",
111                        name);
112       GNUNET_PQ_reconnect (db);
113       res = NULL;
114     }
115
116     for (off = 0; off < soff; off++)
117       GNUNET_free (scratch[off]);
118     return res;
119   }
120 }
121
122
123 /**
124  * Free all memory that was allocated in @a rs during
125  * #GNUNET_PQ_extract_result().
126  *
127  * @param rs reult specification to clean up
128  */
129 void
130 GNUNET_PQ_cleanup_result (struct GNUNET_PQ_ResultSpec *rs)
131 {
132   for (unsigned int i = 0; NULL != rs[i].conv; i++)
133     if (NULL != rs[i].cleaner)
134       rs[i].cleaner (rs[i].cls,
135                      rs[i].dst);
136 }
137
138
139 /**
140  * Extract results from a query result according to the given
141  * specification.
142  *
143  * @param result result to process
144  * @param[in,out] rs result specification to extract for
145  * @param row row from the result to extract
146  * @return
147  *   #GNUNET_YES if all results could be extracted
148  *   #GNUNET_SYSERR if a result was invalid (non-existing field)
149  */
150 int
151 GNUNET_PQ_extract_result (PGresult *result,
152                           struct GNUNET_PQ_ResultSpec *rs,
153                           int row)
154 {
155   if (NULL == result)
156     return GNUNET_SYSERR;
157   for (unsigned int i = 0; NULL != rs[i].conv; i++)
158   {
159     struct GNUNET_PQ_ResultSpec *spec;
160     int ret;
161
162     spec = &rs[i];
163     ret = spec->conv (spec->cls,
164                       result,
165                       row,
166                       spec->fname,
167                       &spec->dst_size,
168                       spec->dst);
169     if (GNUNET_OK != ret)
170     {
171       for (unsigned int j = 0; j < i; j++)
172         if (NULL != rs[j].cleaner)
173           rs[j].cleaner (rs[j].cls,
174                          rs[j].dst);
175       return GNUNET_SYSERR;
176     }
177     if (NULL != spec->result_size)
178       *spec->result_size = spec->dst_size;
179   }
180   return GNUNET_OK;
181 }
182
183
184 /* end of pq/pq.c */