finish to fix memory leak
[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                          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         return GNUNET_SYSERR;
72       }
73       off += p->num_params;
74     }
75     stmt = GNUNET_MYSQL_statement_get_stmt (mc, sh);
76     if (mysql_stmt_bind_param (stmt,
77                                qbind))
78     {
79       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
80                        _("`%s' failed at %s:%d with error: %s\n"),
81                        "mysql_stmt_bind_param", __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, "mysql",
90                        _("`%s' failed at %s:%d with error: %s\n"),
91                        "mysql_stmt_execute", __FILE__, __LINE__,
92                        mysql_stmt_error (stmt));
93       GNUNET_MYSQL_statements_invalidate (mc);
94       return GNUNET_SYSERR;
95     }
96
97       GNUNET_MY_cleanup_query (params,
98                             qbind);
99   }
100
101   return GNUNET_OK;
102 }
103
104
105 /**
106  * Free all memory that was allocated in @a qp during
107  * #GNUNET_MY_exect_prepared().
108  *
109  * @param qp query specification to clean up
110  * @param qbind array of parameter to clean up
111  */
112 void
113 GNUNET_MY_cleanup_query (struct GNUNET_MY_QueryParam *qp,
114                         MYSQL_BIND * qbind)
115 {
116   unsigned int i;
117
118   for (i=0; NULL != qp[i].conv ;i++)
119     if(NULL != qp[i].cleaner)
120       qp[i].cleaner (qp[i].conv_cls,
121                     &qbind[i]);
122 }
123
124
125 /**
126  * Extract results from a query result according to the given
127  * specification.  Always fetches the next row.
128  *
129  *
130  * @param sh statement that returned results
131  * @param rs specification to extract for
132  * @return
133  *  #GNUNET_YES if all results could be extracted
134  *  #GNUNET_NO if there is no more data in the result set
135  *  #GNUNET_SYSERR if a result was invalid
136  */
137 int
138 GNUNET_MY_extract_result (struct GNUNET_MYSQL_StatementHandle *sh,
139                           struct GNUNET_MY_ResultSpec *rs)
140 {
141   unsigned int num_fields;
142   unsigned int i;
143   int ret;
144   MYSQL_STMT *stmt;
145
146   stmt = GNUNET_MYSQL_statement_get_stmt (NULL, sh);
147   if (NULL == stmt)
148   {
149     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
150                     ("`%s' failed at %s:%d with error: %s\n"),
151                        "mysql_stmt_bind_result", __FILE__, __LINE__,
152                        mysql_stmt_error (stmt));
153     return GNUNET_SYSERR;
154   }
155
156   num_fields = 0;
157   for (i=0;NULL != rs[i].pre_conv;i++)
158     num_fields += rs[i].num_fields;
159
160   if (mysql_stmt_field_count (stmt) != num_fields)
161   {
162     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
163                 "Number of fields missmatch between SQL result and result specification\n");
164     return GNUNET_SYSERR;
165   }
166
167   {
168     MYSQL_BIND result[num_fields];
169     unsigned int field_off;
170
171     memset (result, 0, sizeof (MYSQL_BIND) * num_fields);
172     field_off = 0;
173     for (i=0;NULL != rs[i].pre_conv;i++)
174     {
175       struct GNUNET_MY_ResultSpec *rp = &rs[i];
176
177       if (GNUNET_OK !=
178           rp->pre_conv (rp->conv_cls,
179                         rp,
180                         stmt,
181                         field_off,
182                         &result[field_off]))
183       {
184         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
185                     "Pre-conversion for MySQL result failed at offset %u\n",
186                     i);
187         return GNUNET_SYSERR;
188       }
189       field_off += rp->num_fields;
190     }
191     if (mysql_stmt_bind_result (stmt, result))
192     {
193       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
194                        "my",
195                        _("`%s' failed at %s:%d with error: %s\n"),
196                        "mysql_stmt_bind_result", __FILE__, __LINE__,
197                        mysql_stmt_error (stmt));
198       return GNUNET_SYSERR;
199     }
200
201     ret = mysql_stmt_fetch (stmt);
202
203     if (MYSQL_NO_DATA == ret)
204       return GNUNET_NO;
205     if ((0 != ret ) && (MYSQL_DATA_TRUNCATED != ret))
206     {
207       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
208                        "my",
209                        _("mysql_stmt_fetch failed at %s:%d with error: %s\n"),
210                        __FILE__, __LINE__,
211                        mysql_stmt_error (stmt));
212       GNUNET_MY_cleanup_result (rs);
213       return GNUNET_SYSERR;
214     }
215     field_off = 0;
216     for (i=0;NULL != rs[i].post_conv;i++)
217     {
218       struct GNUNET_MY_ResultSpec *rp = &rs[i];
219
220       if (NULL != rp->post_conv)
221         if (GNUNET_OK !=
222             rp->post_conv (rp->conv_cls,
223                            rp,
224                            stmt,
225                            field_off,
226                            &result[field_off]))
227         {
228           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
229                       "Post-conversion for MySQL result failed at offset %u\n",
230                       i);
231           GNUNET_MY_cleanup_result (rs);
232           return GNUNET_SYSERR;
233         }
234       field_off += rp->num_fields;
235     }
236   }
237   return GNUNET_OK;
238 }
239
240
241 /**
242  * Free all memory that was allocated in @a rs during
243  * #GNUNET_MY_extract_result().
244  *
245  * @param rs result specification to clean up
246  */
247 void
248 GNUNET_MY_cleanup_result (struct GNUNET_MY_ResultSpec *rs)
249 {
250   unsigned int i;
251
252   for (i=0;NULL != rs[i].post_conv;i++)
253     if (NULL != rs[i].cleaner)
254       rs[i].cleaner (rs[i].conv_cls,
255                      &rs[i]);
256 }
257
258
259 /* end of my.c */