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