]> git.pld-linux.org Git - packages/apr-util.git/blob - apr_dbd_mysql.c
- updated to 1.2.10
[packages/apr-util.git] / apr_dbd_mysql.c
1 /*
2          Copyright (c) 2003-4, WebThing Ltd
3          Author: Nick Kew <nick@webthing.com>
4  
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9  
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14  
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  
19 */
20
21 /*
22         The current GPL satisfies MySQL licensing terms without
23         invoking any exceptions.
24
25         This code is untested, and probably mostly won't work.
26         It's a demo of concept.
27 */
28
29 #include "apu.h"
30
31 #if APU_HAVE_MYSQL
32
33 #include <ctype.h>
34 #include <stdlib.h>
35
36 #include <mysql/mysql.h>
37 #include <mysql/errmsg.h>
38
39 #include "apr_strings.h"
40
41 #include "apr_dbd_internal.h"
42
43
44 struct apr_dbd_prepared_t {
45     MYSQL_STMT* stmt;
46 };
47
48 struct apr_dbd_transaction_t {
49     int errnum;
50     apr_dbd_t *handle;
51 };
52
53 struct apr_dbd_t {
54     MYSQL* conn ;
55     apr_dbd_transaction_t* trans ;
56 };
57
58 struct apr_dbd_results_t {
59     int random;
60     MYSQL_RES *res;
61     MYSQL_STMT *statement;
62     MYSQL_BIND *bind;
63 };
64 struct apr_dbd_row_t {
65     MYSQL_ROW row;
66     apr_dbd_results_t *res;
67 };
68
69 static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql,
70                             apr_dbd_results_t **results,
71                             const char *query, int seek)
72 {
73     int sz;
74     int ret;
75     if (sql->trans && sql->trans->errnum) {
76         return sql->trans->errnum;
77     }
78     ret = mysql_query(sql->conn, query);
79     if (!ret) {
80         if (sz = mysql_field_count(sql->conn), sz > 0) {
81             if (!*results) {
82                 *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
83             }
84             (*results)->random = seek;
85             (*results)->statement = NULL;
86             if (seek) {
87                 (*results)->res = mysql_store_result(sql->conn);
88             }
89             else {
90                 (*results)->res = mysql_use_result(sql->conn);
91             }
92             apr_pool_cleanup_register(pool, (*results)->res,
93                                       (void*)mysql_free_result,
94                                       apr_pool_cleanup_null);
95         }
96     }
97     if (sql->trans) {
98         sql->trans->errnum = ret;
99     }
100     return ret;
101 }
102 static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
103                              apr_dbd_row_t **row, int rownum)
104 {
105     MYSQL_ROW r;
106     int ret = 0;
107
108     if (res->statement) {
109         if (res->random) {
110             if (rownum >= 0) {
111                 mysql_stmt_data_seek(res->statement, (my_ulonglong)rownum);
112             }
113         }
114         ret = mysql_stmt_fetch(res->statement);
115     }
116     else {
117         if (res->random) {
118             if (rownum >= 0) {
119                 mysql_data_seek(res->res, (my_ulonglong) rownum);
120             }
121         }
122         r = mysql_fetch_row(res->res);
123         if (r == NULL) {
124             ret = 1;
125         }
126     }
127     if (ret == 0) {
128         if (!*row) {
129             *row = apr_palloc(pool, sizeof(apr_dbd_row_t));
130         }
131         (*row)->row = r;
132         (*row)->res = res;
133     }
134     else {
135         mysql_free_result(res->res);
136         apr_pool_cleanup_kill(pool, res->res, (void*)mysql_free_result);
137         ret = -1;
138     }
139     return ret;
140 }
141 #if 0
142 static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n,
143                                apr_dbd_datum_t *val)
144 {
145     MYSQL_BIND *bind;
146     if (row->res->statement) {
147         bind = &row->res->bind[n];
148         if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
149             val->type = APR_DBD_VALUE_NULL;
150             return -1;
151         }
152         if (*bind->is_null) {
153             val->type = APR_DBD_VALUE_NULL;
154             return -1;
155         }
156         else {
157             val->type = APR_DBD_VALUE_STRING;
158             val->value.stringval = bind->buffer;
159         }
160     }
161     else {
162         val->type = APR_DBD_VALUE_STRING;
163         val->value.stringval = row->row[n];
164     }
165     return 0;
166 }
167 #else
168 static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
169 {
170     MYSQL_BIND *bind;
171     if (row->res->statement) {
172         bind = &row->res->bind[n];
173         if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
174             return NULL;
175         }
176         if (*bind->is_null) {
177             return NULL;
178         }
179         else {
180             return bind->buffer;
181         }
182     }
183     else {
184         return row->row[n];
185     }
186     return 0;
187 }
188 #endif
189 static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
190 {
191     return mysql_error(sql->conn);
192 }
193 static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
194 {
195     int ret;
196     if (sql->trans && sql->trans->errnum) {
197         return sql->trans->errnum;
198     }
199     ret = mysql_query(sql->conn, query);
200     *nrows = mysql_affected_rows(sql->conn);
201     if (sql->trans) {
202         sql->trans->errnum = ret;
203     }
204     return ret;
205 }
206 static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
207                                     apr_dbd_t *sql)
208 {
209     unsigned long len = strlen(arg);
210     char *ret = apr_palloc(pool, 2*len + 1);
211     mysql_real_escape_string(sql->conn, ret, arg, len);
212     return ret;
213 }
214 static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
215                              const char *query, const char *label,
216                              apr_dbd_prepared_t **statement)
217 {
218     /* Translate from apr_dbd to native query format */
219     char *myquery = apr_pstrdup(pool, query);
220     char *p = myquery;
221     const char *q;
222     for (q = query; *q; ++q) {
223         if (q[0] == '%') {
224             if (isalpha(q[1])) {
225                 *p++ = '?';
226                 ++q;
227             }
228             else if (q[1] == '%') {
229                 /* reduce %% to % */
230                 *p++ = *q++;
231             }
232             else {
233                 *p++ = *q;
234             }
235         }
236         else {
237             *p++ = *q;
238         }
239     } 
240     *p = 0;
241     if (!*statement) {
242         *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
243     }
244     (*statement)->stmt = mysql_stmt_init(sql->conn);
245     apr_pool_cleanup_register(pool, *statement, (void*)mysql_stmt_close,
246                               apr_pool_cleanup_null);
247     return mysql_stmt_prepare((*statement)->stmt, myquery, strlen(myquery));
248 }
249 static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
250                             int *nrows, apr_dbd_prepared_t *statement,
251                             int nargs, const char **values)
252 {
253     MYSQL_BIND *bind;
254     char *arg;
255     int ret;
256     int i;
257     my_bool is_null = FALSE;
258
259     if (sql->trans && sql->trans->errnum) {
260         return sql->trans->errnum;
261     }
262     nargs = mysql_stmt_param_count(statement->stmt);
263
264     bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
265     for (i=0; i < nargs; ++i) {
266         arg = (char*)values[i];
267         bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
268         bind[i].buffer = arg;
269         bind[i].buffer_length = strlen(arg);
270         bind[i].length = &bind[i].buffer_length;
271         bind[i].is_null = &is_null;
272         bind[i].is_unsigned = 0;
273     }
274
275     ret = mysql_stmt_bind_param(statement->stmt, bind);
276     if (ret != 0) {
277         *nrows = 0;
278     }
279     else {
280         ret = mysql_stmt_execute(statement->stmt);
281         *nrows = mysql_stmt_affected_rows(statement->stmt);
282     }
283     if (sql->trans) {
284         sql->trans->errnum = ret;
285     }
286     return ret;
287 }
288 static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
289                              apr_dbd_prepared_t *statement, va_list args)
290 {
291     MYSQL_BIND *bind;
292     char *arg;
293     int ret;
294     int nargs = 0;
295     int i;
296     my_bool is_null = FALSE;
297
298     if (sql->trans && sql->trans->errnum) {
299         return sql->trans->errnum;
300     }
301     nargs = mysql_stmt_param_count(statement->stmt);
302
303     bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
304     for (i=0; i < nargs; ++i) {
305         arg = va_arg(args, char*);
306         bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
307         bind[i].buffer = arg;
308         bind[i].buffer_length = strlen(arg);
309         bind[i].length = &bind[i].buffer_length;
310         bind[i].is_null = &is_null;
311         bind[i].is_unsigned = 0;
312     }
313
314     ret = mysql_stmt_bind_param(statement->stmt, bind);
315     if (ret != 0) {
316         *nrows = 0;
317     }
318     else {
319         ret = mysql_stmt_execute(statement->stmt);
320         *nrows = mysql_stmt_affected_rows(statement->stmt);
321     }
322     if (sql->trans) {
323         sql->trans->errnum = ret;
324     }
325     return ret;
326 }
327 static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
328                              apr_dbd_results_t **res,
329                              apr_dbd_prepared_t *statement, int random,
330                              int nargs, const char **args)
331 {
332     int i;
333     int nfields;
334     char *arg;
335     my_bool is_null = FALSE;
336     my_bool *is_nullr;
337     int ret;
338     const int FIELDSIZE = 255;
339     unsigned long *length;
340     char **data;
341     MYSQL_BIND *bind;
342
343     if (sql->trans && sql->trans->errnum) {
344         return sql->trans->errnum;
345     }
346
347     nargs = mysql_stmt_param_count(statement->stmt);
348     bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
349
350     for (i=0; i < nargs; ++i) {
351         arg = (char*)args[i];
352         bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
353         bind[i].buffer = arg;
354         bind[i].buffer_length = strlen(arg);
355         bind[i].length = &bind[i].buffer_length;
356         bind[i].is_null = &is_null;
357         bind[i].is_unsigned = 0;
358     }
359
360     ret = mysql_stmt_bind_param(statement->stmt, bind);
361     if (ret == 0) {
362         ret = mysql_stmt_execute(statement->stmt);
363         if (!ret) {
364             if (!*res) {
365                 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
366                 if (!*res) {
367                     while (!mysql_stmt_fetch(statement->stmt));
368                     return -1;
369                 }
370             }
371             (*res)->random = random;
372             (*res)->statement = statement->stmt;
373             (*res)->res = mysql_stmt_result_metadata(statement->stmt);
374             apr_pool_cleanup_register(pool, (*res)->res,
375                 (void*)mysql_free_result, apr_pool_cleanup_null);
376             nfields = mysql_num_fields((*res)->res);
377             if (!(*res)->bind) {
378                 (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
379                 length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
380                 data = apr_palloc(pool, nfields*sizeof(char*));
381                 is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
382                 length = apr_pcalloc(pool, nfields);
383                 for ( i = 0; i < nfields; ++i ) {
384                     (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
385                     (*res)->bind[i].buffer_length = FIELDSIZE;
386                     (*res)->bind[i].length = &length[i];
387                     data[i] = apr_palloc(pool, FIELDSIZE*sizeof(char));
388                     (*res)->bind[i].buffer = data[i];
389                     (*res)->bind[i].is_null = is_nullr+i;
390                 }
391             }
392             ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
393             if (!ret) {
394                 ret = mysql_stmt_store_result(statement->stmt);
395             }
396         }
397     }
398     if (sql->trans) {
399         sql->trans->errnum = ret;
400     }
401     return ret;
402 }
403 static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
404                               apr_dbd_results_t **res,
405                               apr_dbd_prepared_t *statement, int random,
406                               va_list args)
407 {
408     int i;
409     int nfields;
410     char *arg;
411     my_bool is_null = FALSE;
412     my_bool *is_nullr;
413     int ret;
414     const int FIELDSIZE = 255;
415     unsigned long *length;
416     char **data;
417     int nargs;
418     MYSQL_BIND *bind;
419
420     if (sql->trans && sql->trans->errnum) {
421         return sql->trans->errnum;
422     }
423
424     nargs = mysql_stmt_param_count(statement->stmt);
425     bind = apr_palloc(pool, nargs*sizeof(MYSQL_BIND));
426
427     for (i=0; i < nargs; ++i) {
428         arg = va_arg(args, char*);
429         bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
430         bind[i].buffer = arg;
431         bind[i].buffer_length = strlen(arg);
432         bind[i].length = &bind[i].buffer_length;
433         bind[i].is_null = &is_null;
434         bind[i].is_unsigned = 0;
435     }
436
437     ret = mysql_stmt_bind_param(statement->stmt, bind);
438     if (ret == 0) {
439         ret = mysql_stmt_execute(statement->stmt);
440         if (!ret) {
441             if (!*res) {
442                 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
443                 if (!*res) {
444                     while (!mysql_stmt_fetch(statement->stmt));
445                     return -1;
446                 }
447             }
448             (*res)->random = random;
449             (*res)->statement = statement->stmt;
450             (*res)->res = mysql_stmt_result_metadata(statement->stmt);
451             apr_pool_cleanup_register(pool, (*res)->res,
452                 (void*)mysql_free_result, apr_pool_cleanup_null);
453             nfields = mysql_num_fields((*res)->res);
454             if (!(*res)->bind) {
455                 (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
456                 length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
457                 data = apr_palloc(pool, nfields*sizeof(char*));
458                 is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
459                 length = apr_pcalloc(pool, nfields);
460                 for ( i = 0; i < nfields; ++i ) {
461                     (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
462                     (*res)->bind[i].buffer_length = FIELDSIZE;
463                     (*res)->bind[i].length = &length[i];
464                     data[i] = apr_palloc(pool, FIELDSIZE*sizeof(char));
465                     (*res)->bind[i].buffer = data[i];
466                     (*res)->bind[i].is_null = is_nullr+i;
467                 }
468             }
469             ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
470             if (!ret) {
471                 ret = mysql_stmt_store_result(statement->stmt);
472             }
473         }
474     }
475     if (sql->trans) {
476         sql->trans->errnum = ret;
477     }
478     return ret;
479 }
480 static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
481 {
482     int ret = -1;
483     if (trans) {
484         if (trans->errnum) {
485             trans->errnum = 0;
486             ret = mysql_rollback(trans->handle->conn);
487         }
488         else {
489             ret = mysql_commit(trans->handle->conn);
490         }
491     }
492     ret |= mysql_autocommit(trans->handle->conn, 1);
493     return ret;
494 }
495 /* Whether or not transactions work depends on whether the
496  * underlying DB supports them within MySQL.  Unfortunately
497  * it fails silently with the default InnoDB.
498  */
499 static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
500                                  apr_dbd_transaction_t **trans)
501 {
502     /* Don't try recursive transactions here */
503     if (handle->trans) {
504         dbd_mysql_end_transaction(handle->trans) ;
505     }
506     if (!*trans) {
507         *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
508     }
509     (*trans)->errnum = mysql_autocommit(handle->conn, 0);
510     (*trans)->handle = handle;
511     handle->trans = *trans;
512     return (*trans)->errnum;
513 }
514 static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params)
515 {
516     static const char *const delims = " \r\n\t;|,";
517     const char *ptr;
518     int i;
519     const char *key;
520     size_t klen;
521     const char *value;
522     size_t vlen;
523     struct {
524         const char *field;
525         const char *value;
526     } fields[] = {
527         {"host", NULL},
528         {"user", NULL},
529         {"pass", NULL},
530         {"dbname", NULL},
531         {"port", NULL},
532         {"sock", NULL},
533         {NULL, NULL}
534     };
535     unsigned int port = 0;
536     apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t));
537     sql->conn = mysql_init(sql->conn);
538     if ( sql->conn == NULL ) {
539         return NULL;
540     }
541     for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
542         for (key = ptr-1; isspace(*key); --key);
543         klen = 0;
544         while (isalpha(*key)) {
545             --key;
546             ++klen;
547         }
548         ++key;
549         for (value = ptr+1; isspace(*value); ++value);
550         vlen = strcspn(value, delims);
551         for (i=0; fields[i].field != NULL; ++i) {
552             if (!strncasecmp(fields[i].field, key, klen)) {
553                 fields[i].value = apr_pstrndup(pool, value, vlen);
554                 break;
555             }
556         }
557         ptr = value+vlen;
558     }
559     if (fields[4].value != NULL) {
560         port = atoi(fields[4].value);
561     }
562     sql->conn = mysql_real_connect(sql->conn, fields[0].value,
563                                    fields[1].value, fields[2].value,
564                                    fields[3].value, port,
565                                    fields[5].value, 0);
566     return sql->conn ? sql : NULL;
567 }
568 static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
569 {
570     mysql_close(handle->conn);
571     return APR_SUCCESS;
572 }
573 static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
574                                          apr_dbd_t *handle)
575 {
576     return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
577 }
578 static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
579                                const char* name)
580 {
581     return mysql_select_db(handle->conn, name);
582 }
583 static void *dbd_mysql_native(apr_dbd_t *handle)
584 {
585     return handle->conn;
586 }
587 static int dbd_mysql_num_cols(apr_dbd_results_t *res)
588 {
589     if (res->statement) {
590         return mysql_stmt_field_count(res->statement);
591     }
592     else {
593         return mysql_num_fields(res->res);
594     }
595 }
596 static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
597 {
598     if (res->random) {
599         if (res->statement) {
600             return (int) mysql_stmt_num_rows(res->statement);
601         }
602         else {
603             return (int) mysql_num_rows(res->res);
604         }
605     }
606     else {
607         return -1;
608     }
609 }
610 static void dbd_mysql_init(apr_pool_t *pool)
611 {
612     my_init();
613     /* FIXME: this is a guess; find out what it really does */ 
614     apr_pool_cleanup_register(pool, NULL, apr_pool_cleanup_null,
615                               (void*)mysql_thread_end);
616 }
617 APU_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = {
618     "mysql",
619     dbd_mysql_init,
620     dbd_mysql_native,
621     dbd_mysql_open,
622     dbd_mysql_check_conn,
623     dbd_mysql_close,
624     dbd_mysql_select_db,
625     dbd_mysql_transaction,
626     dbd_mysql_end_transaction,
627     dbd_mysql_query,
628     dbd_mysql_select,
629     dbd_mysql_num_cols,
630     dbd_mysql_num_tuples,
631     dbd_mysql_get_row,
632     dbd_mysql_get_entry,
633     dbd_mysql_error,
634     dbd_mysql_escape,
635     dbd_mysql_prepare,
636     dbd_mysql_pvquery,
637     dbd_mysql_pvselect,
638     dbd_mysql_pquery,
639     dbd_mysql_pselect,
640 };
641
642 #endif
This page took 0.441541 seconds and 3 git commands to generate.