written function cleanup
[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
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      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      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18      Boston, MA 02110-1301, USA.
19 */
20 /**
21  * @file my/my.c
22  * @brief library to help with access to a MySQL database
23  * @author Christophe Genevey
24  * @author Christian Grothoff
25  */
26 #include "platform.h"
27 #include <mysql/mysql.h>
28 #include "gnunet_my_lib.h"
29
30 #define STRING_SIZE 50
31
32 /**
33  * Run a prepared SELECT statement.
34  *
35  * @param mc mysql context
36  * @param sh handle to SELECT statment
37  * @param params parameters to the statement
38  * @return
39       #GNUNET_YES if we can prepare all statement
40       #GNUNET_SYSERR if we can't prepare all statement
41  */
42 int
43 GNUNET_MY_exec_prepared (struct GNUNET_MYSQL_Context *mc,
44                          struct GNUNET_MYSQL_StatementHandle *sh,
45                          const struct GNUNET_MY_QueryParam *params)
46 {
47   const struct GNUNET_MY_QueryParam *p;
48   unsigned int num;
49   unsigned int i;
50   MYSQL_STMT *stmt;
51
52   num = 0;
53   for (i=0;NULL != params[i].conv;i++)
54     num += params[i].num_params;
55   {
56     MYSQL_BIND qbind[num];
57     unsigned int off;
58
59     memset(qbind, 0, sizeof(qbind));
60     off = 0;
61     for (i=0;NULL != (p = &params[i])->conv;i++)
62     {
63       if (GNUNET_OK !=
64           p->conv (p->conv_cls,
65                    p,
66                    &qbind[off]))
67       {
68         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
69                     "Conversion for MySQL query failed at offset %u\n",
70                     i);
71         GNUNET_MY_cleanup_query (params);
72         return GNUNET_SYSERR;
73       }
74       off += p->num_params;
75     }
76     stmt = GNUNET_MYSQL_statement_get_stmt (mc, sh);
77     if (mysql_stmt_bind_param (stmt,
78                                qbind))
79     {
80       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
81                        _("`%s' failed at %s:%d with error: %s\n"),
82                        "mysql_stmt_bind_param", __FILE__, __LINE__,
83                        mysql_stmt_error (stmt));
84       GNUNET_MYSQL_statements_invalidate (mc);
85       return GNUNET_SYSERR;
86     }
87
88     if (mysql_stmt_execute (stmt))
89     {
90       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
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       GNUNET_MY_cleanup_query (params);
96       return GNUNET_SYSERR;
97     }
98   }
99
100   return GNUNET_OK;
101 }
102
103
104 /**
105  * Free all memory that was allocated in @a qp during
106  * #GNUNET_MY_exect_prepared().
107  *
108  * @param qp query specification to clean up
109  */
110 void
111 GNUNET_MY_cleanup_query (struct GNUNET_MY_QueryParam *qp)
112 {
113   unsigned int i;
114
115   for (i=0;NULL != qp[i].cleaner;i++)
116     qp[i].cleaner (qp[i].conv_cls,
117                    &qp[i]);
118 }
119
120
121 /**
122  * Extract results from a query result according to the given
123  * specification.  Always fetches the next row.
124  *
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   unsigned int i;
139   int ret;
140   MYSQL_STMT *stmt;
141
142   stmt = GNUNET_MYSQL_statement_get_stmt (NULL /* FIXME */, sh);
143   if (NULL == stmt)
144   {
145     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
146                     ("`%s' failed at %s:%d with error: %s\n"),
147                        "mysql_stmt_bind_result", __FILE__, __LINE__,
148                        mysql_stmt_error (stmt));
149     return GNUNET_SYSERR;
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         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
181                     "Pre-conversion for MySQL result failed at offset %u\n",
182                     i);
183         GNUNET_MY_cleanup_result (rs);
184         return GNUNET_SYSERR;
185       }
186       field_off += rp->num_fields;
187     }
188     if (mysql_stmt_bind_result (stmt, result))
189     {
190       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
191                        "my",
192                        _("`%s' failed at %s:%d with error: %s\n"),
193                        "mysql_stmt_bind_result", __FILE__, __LINE__,
194                        mysql_stmt_error (stmt));
195       return GNUNET_SYSERR;
196     }
197
198     ret = mysql_stmt_fetch (stmt);
199
200     if (MYSQL_NO_DATA == ret)
201       return GNUNET_NO;
202     if ((0 != ret ) && (MYSQL_DATA_TRUNCATED != ret))
203     {
204       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
205                        "my",
206                        _("mysql_stmt_fetch failed at %s:%d with error: %s\n"),
207                        __FILE__, __LINE__,
208                        mysql_stmt_error (stmt));
209       return GNUNET_SYSERR;
210     }
211     field_off = 0;
212     for (i=0;NULL != rs[i].post_conv;i++)
213     {
214       struct GNUNET_MY_ResultSpec *rp = &rs[i];
215
216       if (NULL != rp->post_conv)
217         if (GNUNET_OK !=
218             rp->post_conv (rp->conv_cls,
219                            rp,
220                            stmt,
221                            field_off,
222                            &result[field_off]))
223         {
224           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
225                       "Post-conversion for MySQL result failed at offset %u\n",
226                       i);
227           GNUNET_MY_cleanup_result (rs);
228           return GNUNET_SYSERR;
229         }
230       field_off += rp->num_fields;
231     }
232   }
233   return GNUNET_OK;
234 }
235
236
237 /**
238  * Free all memory that was allocated in @a rs during
239  * #GNUNET_MY_extract_result().
240  *
241  * @param rs reult specification to clean up
242  */
243 void
244 GNUNET_MY_cleanup_result (struct GNUNET_MY_ResultSpec *rs)
245 {
246   unsigned int i;
247
248   for (i=0;NULL != rs[i].cleaner;i++)
249     rs[i].cleaner (rs[i].conv_cls,
250                    &rs[i]);
251 }
252
253
254 /* end of my.c */