2f4cd3ba383591c10c75e4136886aff5a30926e9
[oweals/gnunet.git] / src / my / my.c
1 /*
2      This file is part of GNUnet
3      Copyright (C) 2016, 2018 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 /**
19  * @file my/my.c
20  * @brief library to help with access to a MySQL database
21  * @author Christophe Genevey
22  * @author Christian Grothoff
23  */
24 #include "platform.h"
25 #include <mysql/mysql.h>
26 #include "gnunet_my_lib.h"
27
28
29 /**
30  * Run a prepared SELECT statement.
31  *
32  * @param mc mysql context
33  * @param sh handle to SELECT statment
34  * @param params parameters to the statement
35  * @return
36       #GNUNET_YES if we can prepare all statement
37       #GNUNET_SYSERR if we can't prepare all statement
38  */
39 int
40 GNUNET_MY_exec_prepared (struct GNUNET_MYSQL_Context *mc,
41                          struct GNUNET_MYSQL_StatementHandle *sh,
42                          struct GNUNET_MY_QueryParam *params)
43 {
44   const struct GNUNET_MY_QueryParam *p;
45   unsigned int num;
46   MYSQL_STMT *stmt;
47
48   num = 0;
49   for (unsigned int i=0;NULL != params[i].conv;i++)
50     num += params[i].num_params;
51   {
52     MYSQL_BIND qbind[num];
53     unsigned int off;
54
55     memset (qbind,
56             0,
57             sizeof(qbind));
58     off = 0;
59     for (unsigned int i=0;NULL != (p = &params[i])->conv;i++)
60     {
61       if (GNUNET_OK !=
62           p->conv (p->conv_cls,
63                    p,
64                    &qbind[off]))
65       {
66         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
67                     "Conversion for MySQL query failed at offset %u\n",
68                     i);
69         return GNUNET_SYSERR;
70       }
71       off += p->num_params;
72     }
73     stmt = GNUNET_MYSQL_statement_get_stmt (sh);
74     if (mysql_stmt_bind_param (stmt,
75                                qbind))
76     {
77       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
78                        "my",
79                        _("`%s' failed at %s:%d with error: %s\n"),
80                        "mysql_stmt_bind_param",
81                        __FILE__, __LINE__,
82                        mysql_stmt_error (stmt));
83       GNUNET_MYSQL_statements_invalidate (mc);
84       return GNUNET_SYSERR;
85     }
86
87     if (mysql_stmt_execute (stmt))
88     {
89       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
90                        "my",
91                        _("`%s' failed at %s:%d with error: %s\n"),
92                        "mysql_stmt_execute", __FILE__, __LINE__,
93                        mysql_stmt_error (stmt));
94       GNUNET_MYSQL_statements_invalidate (mc);
95       return GNUNET_SYSERR;
96     }
97     GNUNET_MY_cleanup_query (params,
98                              qbind);
99   }
100   return GNUNET_OK;
101 }
102
103
104 /**
105  * Free all memory that was allocated in @a qp during
106  * #GNUNET_MY_exec_prepared().
107  *
108  * @param qp query specification to clean up
109  * @param qbind array of parameter to clean up
110  */
111 void
112 GNUNET_MY_cleanup_query (struct GNUNET_MY_QueryParam *qp,
113                          MYSQL_BIND *qbind)
114 {
115   for (unsigned int i=0; NULL != qp[i].conv ;i++)
116     if (NULL != qp[i].cleaner)
117       qp[i].cleaner (qp[i].conv_cls,
118                      &qbind[i]);
119 }
120
121
122 /**
123  * Extract results from a query result according to the given
124  * specification.  Always fetches the next row.
125  *
126  * @param sh statement that returned results
127  * @param rs specification to extract for
128  * @return
129  *  #GNUNET_YES if all results could be extracted
130  *  #GNUNET_NO if there is no more data in the result set
131  *  #GNUNET_SYSERR if a result was invalid
132  */
133 int
134 GNUNET_MY_extract_result (struct GNUNET_MYSQL_StatementHandle *sh,
135                           struct GNUNET_MY_ResultSpec *rs)
136 {
137   unsigned int num_fields;
138   int ret;
139   MYSQL_STMT *stmt;
140
141   stmt = GNUNET_MYSQL_statement_get_stmt (sh);
142   if (NULL == stmt)
143   {
144     GNUNET_break (0);
145     return GNUNET_SYSERR;
146   }
147   if (NULL == rs)
148   {
149     mysql_stmt_free_result (stmt);
150     return GNUNET_NO;
151   }
152
153   num_fields = 0;
154   for (unsigned int i=0;NULL != rs[i].pre_conv;i++)
155     num_fields += rs[i].num_fields;
156
157   if (mysql_stmt_field_count (stmt) != num_fields)
158   {
159     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
160                 "Number of fields missmatch between SQL result and result specification\n");
161     return GNUNET_SYSERR;
162   }
163
164   {
165     MYSQL_BIND result[num_fields];
166     unsigned int field_off;
167
168     memset (result, 0, sizeof (MYSQL_BIND) * num_fields);
169     field_off = 0;
170     for (unsigned int i=0;NULL != rs[i].pre_conv;i++)
171     {
172       struct GNUNET_MY_ResultSpec *rp = &rs[i];
173
174       if (GNUNET_OK !=
175           rp->pre_conv (rp->conv_cls,
176                         rp,
177                         stmt,
178                         field_off,
179                         &result[field_off]))
180
181       {
182         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
183                     "Pre-conversion for MySQL result failed at offset %u\n",
184                     i);
185         return GNUNET_SYSERR;
186       }
187       field_off += rp->num_fields;
188     }
189
190     if (mysql_stmt_bind_result (stmt, result))
191     {
192       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
193                        "my",
194                        _("%s failed at %s:%d with error: %s\n"),
195                        "mysql_stmt_bind_result",
196                        __FILE__, __LINE__,
197                        mysql_stmt_error (stmt));
198       return GNUNET_SYSERR;
199     }
200 #if TEST_OPTIMIZATION
201     (void) mysql_stmt_store_result (stmt);
202 #endif
203     ret = mysql_stmt_fetch (stmt);
204     if (MYSQL_NO_DATA == ret)
205     {
206       mysql_stmt_free_result (stmt);
207       return GNUNET_NO;
208     }
209     if (1 == ret)
210     {
211       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
212                        "my",
213                        _("%s failed at %s:%d with error: %s\n"),
214                        "mysql_stmt_fetch",
215                        __FILE__, __LINE__,
216                        mysql_stmt_error (stmt));
217       GNUNET_MY_cleanup_result (rs);
218       mysql_stmt_free_result (stmt);
219       return GNUNET_SYSERR;
220     }
221     field_off = 0;
222     for (unsigned int i=0;NULL != rs[i].post_conv;i++)
223     {
224       struct GNUNET_MY_ResultSpec *rp = &rs[i];
225
226       if (NULL != rp->post_conv)
227         if (GNUNET_OK !=
228             rp->post_conv (rp->conv_cls,
229                            rp,
230                            stmt,
231                            field_off,
232                            &result[field_off]))
233         {
234           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
235                       "Post-conversion for MySQL result failed at offset %u\n",
236                       i);
237           mysql_stmt_free_result (stmt);
238           GNUNET_MY_cleanup_result (rs);
239           return GNUNET_SYSERR;
240         }
241       field_off += rp->num_fields;
242     }
243   }
244   return GNUNET_OK;
245 }
246
247
248 /**
249  * Free all memory that was allocated in @a rs during
250  * #GNUNET_MY_extract_result().
251  *
252  * @param rs result specification to clean up
253  */
254 void
255 GNUNET_MY_cleanup_result (struct GNUNET_MY_ResultSpec *rs)
256 {
257   for (unsigned int i=0;NULL != rs[i].post_conv;i++)
258     if (NULL != rs[i].cleaner)
259       rs[i].cleaner (rs[i].conv_cls,
260                      &rs[i]);
261 }
262
263
264 /* end of my.c */