]> git.pld-linux.org Git - packages/apr-util.git/blame - apr_dbd_mysql.c
- updated to 1.2.6, removed obsolete db4.4 patch
[packages/apr-util.git] / apr_dbd_mysql.c
CommitLineData
249639b3
AM
1/*
2 Copyright (c) 2003-4, WebThing Ltd
3 Author: Nick Kew <nick@webthing.com>
4
5This program is free software; you can redistribute it and/or modify
6it under the terms of the GNU General Public License as published by
7the Free Software Foundation; either version 2 of the License, or
8(at your option) any later version.
9
10This program is distributed in the hope that it will be useful,
11but WITHOUT ANY WARRANTY; without even the implied warranty of
12MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13GNU General Public License for more details.
14
15You should have received a copy of the GNU General Public License
16along with this program; if not, write to the Free Software
17Foundation, 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
44struct apr_dbd_prepared_t {
45 MYSQL_STMT* stmt;
46};
47
48struct apr_dbd_transaction_t {
49 int errnum;
50 apr_dbd_t *handle;
51};
52
53struct apr_dbd_t {
54 MYSQL* conn ;
55 apr_dbd_transaction_t* trans ;
56};
57
58struct apr_dbd_results_t {
59 int random;
60 MYSQL_RES *res;
61 MYSQL_STMT *statement;
62 MYSQL_BIND *bind;
63};
64struct apr_dbd_row_t {
65 MYSQL_ROW row;
66 apr_dbd_results_t *res;
67};
68
69static 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}
102static 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
142static 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
168static 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
189static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
190{
191 return mysql_error(sql->conn);
192}
193static 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}
206static 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}
214static 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}
249static 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}
288static 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}
327static 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}
403static 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}
480static 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 */
499static 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}
514static 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);
c09ca3c5 566 return sql->conn ? sql : NULL;
249639b3
AM
567}
568static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
569{
570 mysql_close(handle->conn);
571 return APR_SUCCESS;
572}
573static 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}
578static 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}
583static void *dbd_mysql_native(apr_dbd_t *handle)
584{
585 return handle->conn;
586}
587static 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}
596static 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}
610static 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}
617APU_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.121428 seconds and 4 git commands to generate.