refactoring my API
[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
43 int
44 GNUNET_MY_exec_prepared (struct GNUNET_MYSQL_Context *mc,
45                          struct GNUNET_MYSQL_StatementHandle *sh,
46                          const struct GNUNET_MY_QueryParam *params)
47 {
48   const struct GNUNET_MY_QueryParam *p;
49   unsigned int num;
50   unsigned int i;
51   MYSQL_STMT *stmt;
52
53   num = 0;
54   for (i=0;NULL != params[i].conv;i++)
55     num += params[i].num_params;
56   {
57     MYSQL_BIND qbind[num];
58     unsigned int off;
59
60     memset(qbind, 0, sizeof(qbind));
61     off = 0;
62     for (i=0;NULL != (p = &params[i])->conv;i++)
63     {
64       if (GNUNET_OK !=
65           p->conv (p->conv_cls,
66                    p,
67                    &qbind[off]))
68       {
69         return GNUNET_SYSERR;
70       }
71       off += p->num_params;
72     }
73     stmt = GNUNET_MYSQL_statement_get_stmt (mc, sh);
74     if (mysql_stmt_bind_param (stmt,
75                                qbind))
76     {
77       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
78                        _("`%s' failed at %s:%d with error: %s\n"),
79                        "mysql_stmt_bind_param", __FILE__, __LINE__,
80                        mysql_stmt_error (stmt));
81       GNUNET_MYSQL_statements_invalidate (mc);
82       return GNUNET_SYSERR;
83     }
84
85   }
86   if (mysql_stmt_execute (stmt))
87   {
88     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
89                      _("`%s' failed at %s:%d with error: %s\n"),
90                      "mysql_stmt_execute", __FILE__, __LINE__,
91                      mysql_stmt_error (stmt));
92     GNUNET_MYSQL_statements_invalidate (mc);
93     return GNUNET_SYSERR;
94   }
95
96   return GNUNET_OK;
97 }
98
99
100 /**
101  * Extract results from a query result according to the given
102  * specification.  Always fetches the next row.
103  *
104  *
105  * @param sh statement that returned results
106  * @param rs specification to extract for
107  * @return
108  *  #GNUNET_YES if all results could be extracted
109  *  #GNUNET_NO if there is no more data in the result set
110  *  #GNUNET_SYSERR if a result was invalid
111  */
112 int
113 GNUNET_MY_extract_result (struct GNUNET_MYSQL_StatementHandle *sh,
114                           struct GNUNET_MY_ResultSpec *rs)
115 {
116   unsigned int num_fields;
117   unsigned int i;
118   int ret;
119   MYSQL_STMT *stmt;
120
121   stmt = GNUNET_MYSQL_statement_get_stmt (NULL /* FIXME */, sh);
122   if (NULL == stmt)
123   {
124     GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR, "mysql",
125                     ("`%s' failed at %s:%d with error: %s\n"),
126                        "mysql_stmt_bind_result", __FILE__, __LINE__,
127                        mysql_stmt_error (stmt));
128     return GNUNET_SYSERR;
129   }
130
131   num_fields = 0;
132   for (i=0;NULL != rs[i].conv;i++)
133     num_fields += rs[i].num_fields;
134
135   if (mysql_stmt_field_count (stmt) != num_fields)
136   {
137     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
138                 "Number of fields missmatch between SQL result and result specification\n");
139     return GNUNET_SYSERR;
140   }
141
142   {
143     MYSQL_BIND result[num_fields];
144     unsigned int field_off;
145
146     memset (result, 0, sizeof (MYSQL_BIND) * num_fields);
147     field_off = 0;
148     for (i=0;NULL != rs[i].conv;i++)
149     {
150       struct GNUNET_MY_ResultSpec *rp = &rs[i];
151
152       if (GNUNET_OK !=
153           rp->pre_conv (rp->cls,
154                         rp,
155                         stmt,
156                         field_off,
157                         &result[field_off]))
158       {
159         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
160                     "Pre-conversion for MySQL result failed at offset %u\n",
161                     i);
162         GNUNET_MY_cleanup_result (rs);
163         return GNUNET_SYSERR;
164       }
165       field_off += rp->num_fields;
166     }
167     if (mysql_stmt_bind_result (stmt, result))
168     {
169       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
170                        "my",
171                        _("`%s' failed at %s:%d with error: %s\n"),
172                        "mysql_stmt_bind_result", __FILE__, __LINE__,
173                        mysql_stmt_error (stmt));
174       return GNUNET_SYSERR;
175     }
176     ret = mysql_stmt_fetch (stmt);
177     if (MYSQL_NO_DATA == ret)
178       return GNUNET_NO;
179     if (0 != ret)
180     {
181       GNUNET_log_from (GNUNET_ERROR_TYPE_ERROR,
182                        "my",
183                        _("mysql_stmt_fetch failed at %s:%d with error: %s\n"),
184                        __FILE__, __LINE__,
185                        mysql_stmt_error (stmt));
186       return GNUNET_SYSERR;
187     }
188     field_off = 0;
189     for (i=0;NULL != rs[i].conv;i++)
190     {
191       struct GNUNET_MY_ResultSpec *rp = &rs[i];
192
193       if (NULL != rp->post_conv)
194         if (GNUNET_OK !=
195             rp->post_conv (rp->cls,
196                            rp,
197                            stmt,
198                            field_off,
199                            &result[field_off]))
200         {
201           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
202                       "Post-conversion for MySQL result failed at offset %u\n",
203                       i);
204           GNUNET_MY_cleanup_result (rs);
205           return GNUNET_SYSERR;
206         }
207       field_off += rp->num_fields;
208     }
209   }
210   return GNUNET_OK;
211 }
212
213
214 /**
215  * Free all memory that was allocated in @a rs during
216  * #GNUNET_MY_extract_result().
217  *
218  * @param rs reult specification to clean up
219  */
220 void
221 GNUNET_MY_cleanup_result (struct GNUNET_PQ_ResultSpec *rs)
222 {
223   unsigned int i;
224
225   for (i=0;NULL != rs[i].conv;i++)
226     rs[i].cleaner (rs[i].cls,
227                    &rs[i]);
228 }
229
230
231 /* end of my.c */