Deleted Added
full compact
apr_dbd_mysql.c (253734) apr_dbd_mysql.c (272076)
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apu.h"
18
19#if APU_HAVE_MYSQL
20
21#include "apu_version.h"
22#include "apu_config.h"
23
24#include <ctype.h>
25#include <stdlib.h>
26
27#if defined(HAVE_MYSQL_MYSQL_H)
28#if defined(HAVE_MYSQL_MY_GLOBAL_H)
29#include <mysql/my_global.h>
30#if defined(HAVE_MYSQL_MY_SYS_H)
31#include <mysql/my_sys.h>
32#endif
33#endif
34#include <mysql/mysql.h>
35#include <mysql/errmsg.h>
36#else /* !defined(HAVE_MYSQL_MYSQL_H) */
37#if defined(HAVE_MY_GLOBAL_H)
38#include <my_global.h>
39#if defined(HAVE_MY_SYS_H)
40#include <my_sys.h>
41#endif
42#endif
43#include <mysql.h>
44#include <errmsg.h>
45#endif
46
47#include "apr_strings.h"
48#include "apr_lib.h"
49#include "apr_buckets.h"
50
51#include "apr_dbd_internal.h"
52
53/* default maximum field size 1 MB */
54#define FIELDSIZE 1048575
55
56struct apr_dbd_prepared_t {
57 MYSQL_STMT* stmt;
58 int nargs;
59 int nvals;
60 apr_dbd_type_e *types;
61};
62
63struct apr_dbd_transaction_t {
64 int mode;
65 int errnum;
66 apr_dbd_t *handle;
67};
68
69struct apr_dbd_t {
70 MYSQL* conn ;
71 apr_dbd_transaction_t* trans ;
72 unsigned long fldsz;
73};
74
75struct apr_dbd_results_t {
76 int random;
77 MYSQL_RES *res;
78 MYSQL_STMT *statement;
79 MYSQL_BIND *bind;
80 apr_pool_t *pool;
81};
82struct apr_dbd_row_t {
83 MYSQL_ROW row;
84 apr_dbd_results_t *res;
85 unsigned long *len;
86};
87
88/* MySQL specific bucket for BLOB types */
89typedef struct apr_bucket_lob apr_bucket_lob;
90/**
91 * A bucket referring to a MySQL BLOB
92 */
93struct apr_bucket_lob {
94 /** Number of buckets using this memory */
95 apr_bucket_refcount refcount;
96 /** The row this bucket refers to */
97 const apr_dbd_row_t *row;
98 /** The column this bucket refers to */
99 int col;
100 /** The pool into which any needed structures should
101 * be created while reading from this bucket */
102 apr_pool_t *readpool;
103};
104
105static void lob_bucket_destroy(void *data);
106static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
107 apr_size_t *len, apr_read_type_e block);
108static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
109 const apr_dbd_row_t *row, int col,
110 apr_off_t offset, apr_size_t len,
111 apr_pool_t *p);
112static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
113 apr_off_t offset,
114 apr_size_t len, apr_pool_t *p,
115 apr_bucket_alloc_t *list);
116static int dbd_mysql_num_cols(apr_dbd_results_t *res);
117
118static const apr_bucket_type_t apr_bucket_type_lob = {
119 "LOB", 5, APR_BUCKET_DATA,
120 lob_bucket_destroy,
121 lob_bucket_read,
122 apr_bucket_setaside_notimpl,
123 apr_bucket_shared_split,
124 apr_bucket_shared_copy
125};
126
127static void lob_bucket_destroy(void *data)
128{
129 apr_bucket_lob *f = data;
130
131 if (apr_bucket_shared_destroy(f)) {
132 /* no need to destroy database objects here; it will get
133 * done automatically when the pool gets cleaned up */
134 apr_bucket_free(f);
135 }
136}
137
138static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
139 apr_size_t *len, apr_read_type_e block)
140{
141 apr_bucket_lob *a = e->data;
142 const apr_dbd_row_t *row = a->row;
143 apr_dbd_results_t *res = row->res;
144 int col = a->col;
145 apr_bucket *b = NULL;
146 int rv;
147 apr_size_t blength = e->length; /* bytes remaining in file past offset */
148 apr_off_t boffset = e->start;
149 MYSQL_BIND *bind = &res->bind[col];
150
151 *str = NULL; /* in case we die prematurely */
152
153 /* fetch from offset if not at the beginning */
154 if (boffset > 0) {
155 rv = mysql_stmt_fetch_column(res->statement, bind, col,
156 (unsigned long) boffset);
157 if (rv != 0) {
158 return APR_EGENERAL;
159 }
160 }
161 blength -= blength > bind->buffer_length ? bind->buffer_length : blength;
162 *len = e->length - blength;
163 *str = bind->buffer;
164
165 /* allocate new buffer, since we used this one for the bucket */
166 bind->buffer = apr_palloc(res->pool, bind->buffer_length);
167
168 /*
169 * Change the current bucket to refer to what we read,
170 * even if we read nothing because we hit EOF.
171 */
172 apr_bucket_pool_make(e, *str, *len, res->pool);
173
174 /* If we have more to read from the field, then create another bucket */
175 if (blength > 0) {
176 /* for efficiency, we can just build a new apr_bucket struct
177 * to wrap around the existing LOB bucket */
178 b = apr_bucket_alloc(sizeof(*b), e->list);
179 b->start = boffset + *len;
180 b->length = blength;
181 b->data = a;
182 b->type = &apr_bucket_type_lob;
183 b->free = apr_bucket_free;
184 b->list = e->list;
185 APR_BUCKET_INSERT_AFTER(e, b);
186 }
187 else {
188 lob_bucket_destroy(a);
189 }
190
191 return APR_SUCCESS;
192}
193
194static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
195 const apr_dbd_row_t *row, int col,
196 apr_off_t offset, apr_size_t len,
197 apr_pool_t *p)
198{
199 apr_bucket_lob *f;
200
201 f = apr_bucket_alloc(sizeof(*f), b->list);
202 f->row = row;
203 f->col = col;
204 f->readpool = p;
205
206 b = apr_bucket_shared_make(b, f, offset, len);
207 b->type = &apr_bucket_type_lob;
208
209 return b;
210}
211
212static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
213 apr_off_t offset,
214 apr_size_t len, apr_pool_t *p,
215 apr_bucket_alloc_t *list)
216{
217 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
218
219 APR_BUCKET_INIT(b);
220 b->free = apr_bucket_free;
221 b->list = list;
222 return apr_bucket_lob_make(b, row, col, offset, len, p);
223}
224
225static apr_status_t free_result(void *data)
226{
227 mysql_free_result(data);
228 return APR_SUCCESS;
229}
230
231static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql,
232 apr_dbd_results_t **results,
233 const char *query, int seek)
234{
235 int sz;
236 int ret;
237 if (sql->trans && sql->trans->errnum) {
238 return sql->trans->errnum;
239 }
240 ret = mysql_query(sql->conn, query);
241 if (!ret) {
242 if (sz = mysql_field_count(sql->conn), sz > 0) {
243 if (!*results) {
244 *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
245 }
246 (*results)->random = seek;
247 (*results)->statement = NULL;
248 (*results)->pool = pool;
249 if (seek) {
250 (*results)->res = mysql_store_result(sql->conn);
251 }
252 else {
253 (*results)->res = mysql_use_result(sql->conn);
254 }
255 apr_pool_cleanup_register(pool, (*results)->res,
256 free_result,apr_pool_cleanup_null);
257 }
258 } else {
259 ret = mysql_errno(sql->conn);
260 }
261
262 if (TXN_NOTICE_ERRORS(sql->trans)) {
263 sql->trans->errnum = ret;
264 }
265 return ret;
266}
267
268static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n)
269{
270 if ((n < 0) || (n >= (int) mysql_num_fields(res->res))) {
271 return NULL;
272 }
273
274 return mysql_fetch_fields(res->res)[n].name;
275}
276
277static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
278 apr_dbd_row_t **row, int rownum)
279{
280 MYSQL_ROW r = NULL;
281 int ret = 0;
282
283 if (res->statement) {
284 if (res->random) {
285 if (rownum > 0) {
286 mysql_stmt_data_seek(res->statement, (my_ulonglong) --rownum);
287 }
288 else {
289 return -1; /* invalid row */
290 }
291 }
292 ret = mysql_stmt_fetch(res->statement);
293 switch (ret) {
294 case 1:
295 ret = mysql_stmt_errno(res->statement);
296 break;
297 case MYSQL_NO_DATA:
298 ret = -1;
299 break;
300 default:
301 ret = 0; /* bad luck - get_entry will deal with this */
302 break;
303 }
304 }
305 else {
306 if (res->random) {
307 if (rownum > 0) {
308 mysql_data_seek(res->res, (my_ulonglong) --rownum);
309 }
310 else {
311 return -1; /* invalid row */
312 }
313 }
314 r = mysql_fetch_row(res->res);
315 if (r == NULL) {
316 ret = -1;
317 }
318 }
319 if (ret == 0) {
320 if (!*row) {
321 *row = apr_palloc(pool, sizeof(apr_dbd_row_t));
322 }
323 (*row)->row = r;
324 (*row)->res = res;
325 (*row)->len = mysql_fetch_lengths(res->res);
326 }
327 else {
328 apr_pool_cleanup_run(res->pool, res->res, free_result);
329 }
330 return ret;
331}
332#if 0
333/* An improved API that was proposed but not followed up */
334static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n,
335 apr_dbd_datum_t *val)
336{
337 MYSQL_BIND *bind;
338 if (dbd_mysql_num_cols(row->res) <= n) {
339 return NULL;
340 }
341 if (row->res->statement) {
342 bind = &row->res->bind[n];
343 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
344 val->type = APR_DBD_VALUE_NULL;
345 return -1;
346 }
347 if (*bind->is_null) {
348 val->type = APR_DBD_VALUE_NULL;
349 return -1;
350 }
351 else {
352 val->type = APR_DBD_VALUE_STRING;
353 val->value.stringval = bind->buffer;
354 }
355 }
356 else {
357 val->type = APR_DBD_VALUE_STRING;
358 val->value.stringval = row->row[n];
359 }
360 return 0;
361}
362#else
363
364static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
365{
366 MYSQL_BIND *bind;
367 if (dbd_mysql_num_cols(row->res) <= n) {
368 return NULL;
369 }
370 if (row->res->statement) {
371 bind = &row->res->bind[n];
372 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
373 return NULL;
374 }
375 if (*bind->is_null) {
376 return NULL;
377 }
378 else {
379 return bind->buffer;
380 }
381 }
382 else {
383 return row->row[n];
384 }
385 return NULL;
386}
387#endif
388
389static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n,
390 apr_dbd_type_e type, void *data)
391{
392 if (row->res->statement) {
393 MYSQL_BIND *bind = &row->res->bind[n];
394 unsigned long len = *bind->length;
395
396 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
397 return APR_EGENERAL;
398 }
399
400 if (*bind->is_null) {
401 return APR_ENOENT;
402 }
403
404 switch (type) {
405 case APR_DBD_TYPE_TINY:
406 *(char*)data = atoi(bind->buffer);
407 break;
408 case APR_DBD_TYPE_UTINY:
409 *(unsigned char*)data = atoi(bind->buffer);
410 break;
411 case APR_DBD_TYPE_SHORT:
412 *(short*)data = atoi(bind->buffer);
413 break;
414 case APR_DBD_TYPE_USHORT:
415 *(unsigned short*)data = atoi(bind->buffer);
416 break;
417 case APR_DBD_TYPE_INT:
418 *(int*)data = atoi(bind->buffer);
419 break;
420 case APR_DBD_TYPE_UINT:
421 *(unsigned int*)data = atoi(bind->buffer);
422 break;
423 case APR_DBD_TYPE_LONG:
424 *(long*)data = atol(bind->buffer);
425 break;
426 case APR_DBD_TYPE_ULONG:
427 *(unsigned long*)data = atol(bind->buffer);
428 break;
429 case APR_DBD_TYPE_LONGLONG:
430 *(apr_int64_t*)data = apr_atoi64(bind->buffer);
431 break;
432 case APR_DBD_TYPE_ULONGLONG:
433 *(apr_uint64_t*)data = apr_atoi64(bind->buffer);
434 break;
435 case APR_DBD_TYPE_FLOAT:
436 *(float*)data = (float) atof(bind->buffer);
437 break;
438 case APR_DBD_TYPE_DOUBLE:
439 *(double*)data = atof(bind->buffer);
440 break;
441 case APR_DBD_TYPE_STRING:
442 case APR_DBD_TYPE_TEXT:
443 case APR_DBD_TYPE_TIME:
444 case APR_DBD_TYPE_DATE:
445 case APR_DBD_TYPE_DATETIME:
446 case APR_DBD_TYPE_TIMESTAMP:
447 case APR_DBD_TYPE_ZTIMESTAMP:
448 *((char*)bind->buffer+bind->buffer_length-1) = '\0';
449 *(char**)data = bind->buffer;
450 break;
451 case APR_DBD_TYPE_BLOB:
452 case APR_DBD_TYPE_CLOB:
453 {
454 apr_bucket *e;
455 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
456
457 e = apr_bucket_lob_create(row, n, 0, len,
458 row->res->pool, b->bucket_alloc);
459 APR_BRIGADE_INSERT_TAIL(b, e);
460 }
461 break;
462 case APR_DBD_TYPE_NULL:
463 *(void**)data = NULL;
464 break;
465 default:
466 return APR_EGENERAL;
467 }
468 }
469 else {
470 if (row->row[n] == NULL) {
471 return APR_ENOENT;
472 }
473
474 switch (type) {
475 case APR_DBD_TYPE_TINY:
476 *(char*)data = atoi(row->row[n]);
477 break;
478 case APR_DBD_TYPE_UTINY:
479 *(unsigned char*)data = atoi(row->row[n]);
480 break;
481 case APR_DBD_TYPE_SHORT:
482 *(short*)data = atoi(row->row[n]);
483 break;
484 case APR_DBD_TYPE_USHORT:
485 *(unsigned short*)data = atoi(row->row[n]);
486 break;
487 case APR_DBD_TYPE_INT:
488 *(int*)data = atoi(row->row[n]);
489 break;
490 case APR_DBD_TYPE_UINT:
491 *(unsigned int*)data = atoi(row->row[n]);
492 break;
493 case APR_DBD_TYPE_LONG:
494 *(long*)data = atol(row->row[n]);
495 break;
496 case APR_DBD_TYPE_ULONG:
497 *(unsigned long*)data = atol(row->row[n]);
498 break;
499 case APR_DBD_TYPE_LONGLONG:
500 *(apr_int64_t*)data = apr_atoi64(row->row[n]);
501 break;
502 case APR_DBD_TYPE_ULONGLONG:
503 *(apr_uint64_t*)data = apr_atoi64(row->row[n]);
504 break;
505 case APR_DBD_TYPE_FLOAT:
506 *(float*)data = (float) atof(row->row[n]);
507 break;
508 case APR_DBD_TYPE_DOUBLE:
509 *(double*)data = atof(row->row[n]);
510 break;
511 case APR_DBD_TYPE_STRING:
512 case APR_DBD_TYPE_TEXT:
513 case APR_DBD_TYPE_TIME:
514 case APR_DBD_TYPE_DATE:
515 case APR_DBD_TYPE_DATETIME:
516 case APR_DBD_TYPE_TIMESTAMP:
517 case APR_DBD_TYPE_ZTIMESTAMP:
518 *(char**)data = row->row[n];
519 break;
520 case APR_DBD_TYPE_BLOB:
521 case APR_DBD_TYPE_CLOB:
522 {
523 apr_bucket *e;
524 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
525
526 e = apr_bucket_pool_create(row->row[n], row->len[n],
527 row->res->pool, b->bucket_alloc);
528 APR_BRIGADE_INSERT_TAIL(b, e);
529 }
530 break;
531 case APR_DBD_TYPE_NULL:
532 *(void**)data = NULL;
533 break;
534 default:
535 return APR_EGENERAL;
536 }
537 }
538 return 0;
539}
540
541static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
542{
543 return mysql_error(sql->conn);
544}
545
546static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
547{
548 int ret;
549 if (sql->trans && sql->trans->errnum) {
550 return sql->trans->errnum;
551 }
552 ret = mysql_query(sql->conn, query);
553 if (ret != 0) {
554 ret = mysql_errno(sql->conn);
555 }
556 *nrows = (int) mysql_affected_rows(sql->conn);
557 if (TXN_NOTICE_ERRORS(sql->trans)) {
558 sql->trans->errnum = ret;
559 }
560 return ret;
561}
562
563static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
564 apr_dbd_t *sql)
565{
566 unsigned long len = strlen(arg);
567 char *ret = apr_palloc(pool, 2*len + 1);
568 mysql_real_escape_string(sql->conn, ret, arg, len);
569 return ret;
570}
571
572static apr_status_t stmt_close(void *data)
573{
574 mysql_stmt_close(data);
575 return APR_SUCCESS;
576}
577
578static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
579 const char *query, const char *label,
580 int nargs, int nvals, apr_dbd_type_e *types,
581 apr_dbd_prepared_t **statement)
582{
583 /* Translate from apr_dbd to native query format */
584 int ret;
585
586 if (!*statement) {
587 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
588 }
589 (*statement)->stmt = mysql_stmt_init(sql->conn);
590
591 if ((*statement)->stmt) {
592 apr_pool_cleanup_register(pool, (*statement)->stmt,
593 stmt_close, apr_pool_cleanup_null);
594 ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query));
595
596 if (ret != 0) {
597 ret = mysql_stmt_errno((*statement)->stmt);
598 }
599
600 (*statement)->nargs = nargs;
601 (*statement)->nvals = nvals;
602 (*statement)->types = types;
603
604 return ret;
605 }
606
607 return CR_OUT_OF_MEMORY;
608}
609
610static void dbd_mysql_bind(apr_dbd_prepared_t *statement,
611 const char **values, MYSQL_BIND *bind)
612{
613 int i, j;
614
615 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
616 bind[i].length = &bind[i].buffer_length;
617 bind[i].is_unsigned = 0;
618 bind[i].is_null = NULL;
619
620 if (values[j] == NULL) {
621 bind[i].buffer_type = MYSQL_TYPE_NULL;
622 }
623 else {
624 switch (statement->types[i]) {
625 case APR_DBD_TYPE_BLOB:
626 case APR_DBD_TYPE_CLOB:
627 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
628 bind[i].buffer = (void*)values[j];
629 bind[i].buffer_length = atol(values[++j]);
630
631 /* skip table and column */
632 j += 2;
633 break;
634 default:
635 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
636 bind[i].buffer = (void*)values[j];
637 bind[i].buffer_length = strlen(values[j]);
638 break;
639 }
640 }
641 }
642
643 return;
644}
645
646static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
647 int *nrows, apr_dbd_prepared_t *statement,
648 MYSQL_BIND *bind)
649{
650 int ret;
651
652 ret = mysql_stmt_bind_param(statement->stmt, bind);
653 if (ret != 0) {
654 *nrows = 0;
655 ret = mysql_stmt_errno(statement->stmt);
656 }
657 else {
658 ret = mysql_stmt_execute(statement->stmt);
659 if (ret != 0) {
660 ret = mysql_stmt_errno(statement->stmt);
661 }
662 *nrows = (int) mysql_stmt_affected_rows(statement->stmt);
663 }
664
665 return ret;
666}
667
668static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
669 int *nrows, apr_dbd_prepared_t *statement,
670 const char **values)
671{
672 MYSQL_BIND *bind;
673 int ret;
674
675 if (sql->trans && sql->trans->errnum) {
676 return sql->trans->errnum;
677 }
678
679 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
680
681 dbd_mysql_bind(statement, values, bind);
682
683 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
684
685 if (TXN_NOTICE_ERRORS(sql->trans)) {
686 sql->trans->errnum = ret;
687 }
688 return ret;
689}
690
691static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
692 apr_dbd_prepared_t *statement, va_list args)
693{
694 const char **values;
695 int i;
696
697 if (sql->trans && sql->trans->errnum) {
698 return sql->trans->errnum;
699 }
700
701 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
702
703 for (i = 0; i < statement->nvals; i++) {
704 values[i] = va_arg(args, const char*);
705 }
706
707 return dbd_mysql_pquery(pool, sql, nrows, statement, values);
708}
709
710static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
711 apr_dbd_results_t **res,
712 apr_dbd_prepared_t *statement,
713 int random, MYSQL_BIND *bind)
714{
715 int nfields, i;
716 my_bool *is_nullr;
717#if MYSQL_VERSION_ID >= 50000
718 my_bool *error;
719#endif
720 int ret;
721 unsigned long *length, maxlen;
722
723 ret = mysql_stmt_bind_param(statement->stmt, bind);
724 if (ret == 0) {
725 ret = mysql_stmt_execute(statement->stmt);
726 if (!ret) {
727 if (!*res) {
728 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
729 }
730 (*res)->random = random;
731 (*res)->statement = statement->stmt;
732 (*res)->res = mysql_stmt_result_metadata(statement->stmt);
733 (*res)->pool = pool;
734 apr_pool_cleanup_register(pool, (*res)->res,
735 free_result, apr_pool_cleanup_null);
736 nfields = mysql_num_fields((*res)->res);
737 if (!(*res)->bind) {
738 (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
739 length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
740#if MYSQL_VERSION_ID >= 50000
741 error = apr_palloc(pool, nfields*sizeof(my_bool));
742#endif
743 is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
744 for ( i = 0; i < nfields; ++i ) {
745 maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
746 (*res)->res->fields[i].length : sql->fldsz) + 1;
747 if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) {
748 (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
749 }
750 else {
751 (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
752 }
753 (*res)->bind[i].buffer_length = maxlen;
754 (*res)->bind[i].length = &length[i];
755 (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
756 (*res)->bind[i].is_null = is_nullr+i;
757#if MYSQL_VERSION_ID >= 50000
758 (*res)->bind[i].error = error+i;
759#endif
760 }
761 }
762 ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
763 if (!ret) {
764 ret = mysql_stmt_store_result(statement->stmt);
765 }
766 }
767 }
768 if (ret != 0) {
769 ret = mysql_stmt_errno(statement->stmt);
770 }
771
772 return ret;
773}
774
775static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
776 apr_dbd_results_t **res,
777 apr_dbd_prepared_t *statement, int random,
778 const char **args)
779{
780 int ret;
781 MYSQL_BIND *bind;
782
783 if (sql->trans && sql->trans->errnum) {
784 return sql->trans->errnum;
785 }
786
787 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
788
789 dbd_mysql_bind(statement, args, bind);
790
791 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
792
793 if (TXN_NOTICE_ERRORS(sql->trans)) {
794 sql->trans->errnum = ret;
795 }
796 return ret;
797}
798
799static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
800 apr_dbd_results_t **res,
801 apr_dbd_prepared_t *statement, int random,
802 va_list args)
803{
804 const char **values;
805 int i;
806
807 if (sql->trans && sql->trans->errnum) {
808 return sql->trans->errnum;
809 }
810
811 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
812
813 for (i = 0; i < statement->nvals; i++) {
814 values[i] = va_arg(args, const char*);
815 }
816
817 return dbd_mysql_pselect(pool, sql, res, statement, random, values);
818}
819
820static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement,
821 const void **values, MYSQL_BIND *bind)
822{
823 void *arg;
824 int i, j;
825 apr_dbd_type_e type;
826
827 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
828 arg = (void *)values[j];
829
830 bind[i].length = &bind[i].buffer_length;
831 bind[i].is_null = NULL;
832
833 type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
834 switch (type) {
835 case APR_DBD_TYPE_TINY:
836 bind[i].buffer = arg;
837 bind[i].buffer_type = MYSQL_TYPE_TINY;
838 bind[i].is_unsigned = 0;
839 break;
840 case APR_DBD_TYPE_UTINY:
841 bind[i].buffer = arg;
842 bind[i].buffer_type = MYSQL_TYPE_TINY;
843 bind[i].is_unsigned = 1;
844 break;
845 case APR_DBD_TYPE_SHORT:
846 bind[i].buffer = arg;
847 bind[i].buffer_type = MYSQL_TYPE_SHORT;
848 bind[i].is_unsigned = 0;
849 break;
850 case APR_DBD_TYPE_USHORT:
851 bind[i].buffer = arg;
852 bind[i].buffer_type = MYSQL_TYPE_SHORT;
853 bind[i].is_unsigned = 1;
854 break;
855 case APR_DBD_TYPE_INT:
856 bind[i].buffer = arg;
857 bind[i].buffer_type = MYSQL_TYPE_LONG;
858 bind[i].is_unsigned = 0;
859 break;
860 case APR_DBD_TYPE_UINT:
861 bind[i].buffer = arg;
862 bind[i].buffer_type = MYSQL_TYPE_LONG;
863 bind[i].is_unsigned = 1;
864 break;
865 case APR_DBD_TYPE_LONG:
866 if (sizeof(int) == sizeof(long)) {
867 bind[i].buffer = arg;
868 }
869 else {
870 bind[i].buffer = apr_palloc(pool, sizeof(int));
871 *(int*)bind[i].buffer = *(long*)arg;
872 }
873 bind[i].buffer_type = MYSQL_TYPE_LONG;
874 bind[i].is_unsigned = 0;
875 break;
876 case APR_DBD_TYPE_ULONG:
877 if (sizeof(unsigned int) == sizeof(unsigned long)) {
878 bind[i].buffer = arg;
879 }
880 else {
881 bind[i].buffer = apr_palloc(pool, sizeof(unsigned int));
882 *(unsigned int*)bind[i].buffer = *(unsigned long*)arg;
883 }
884 bind[i].buffer_type = MYSQL_TYPE_LONG;
885 bind[i].is_unsigned = 1;
886 break;
887 case APR_DBD_TYPE_LONGLONG:
888 if (sizeof(my_ulonglong) == sizeof(apr_int64_t)) {
889 bind[i].buffer = arg;
890 bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
891 }
892 else { /* have to downsize, long long is not portable */
893 bind[i].buffer = apr_palloc(pool, sizeof(long));
894 *(long*)bind[i].buffer = (long) *(apr_int64_t*)arg;
895 bind[i].buffer_type = MYSQL_TYPE_LONG;
896 }
897 bind[i].is_unsigned = 0;
898 break;
899 case APR_DBD_TYPE_ULONGLONG:
900 if (sizeof(my_ulonglong) == sizeof(apr_uint64_t)) {
901 bind[i].buffer = arg;
902 bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
903 }
904 else { /* have to downsize, long long is not portable */
905 bind[i].buffer = apr_palloc(pool, sizeof(long));
906 *(unsigned long*)bind[i].buffer =
907 (unsigned long) *(apr_uint64_t*)arg;
908 bind[i].buffer_type = MYSQL_TYPE_LONG;
909 }
910 bind[i].is_unsigned = 1;
911 break;
912 case APR_DBD_TYPE_FLOAT:
913 bind[i].buffer = arg;
914 bind[i].buffer_type = MYSQL_TYPE_FLOAT;
915 bind[i].is_unsigned = 0;
916 break;
917 case APR_DBD_TYPE_DOUBLE:
918 bind[i].buffer = arg;
919 bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
920 bind[i].is_unsigned = 0;
921 break;
922 case APR_DBD_TYPE_STRING:
923 case APR_DBD_TYPE_TEXT:
924 case APR_DBD_TYPE_TIME:
925 case APR_DBD_TYPE_DATE:
926 case APR_DBD_TYPE_DATETIME:
927 case APR_DBD_TYPE_TIMESTAMP:
928 case APR_DBD_TYPE_ZTIMESTAMP:
929 bind[i].buffer = arg;
930 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
931 bind[i].is_unsigned = 0;
932 bind[i].buffer_length = strlen((const char *)arg);
933 break;
934 case APR_DBD_TYPE_BLOB:
935 case APR_DBD_TYPE_CLOB:
936 bind[i].buffer = (void *)arg;
937 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
938 bind[i].is_unsigned = 0;
939 bind[i].buffer_length = *(apr_size_t*)values[++j];
940
941 /* skip table and column */
942 j += 2;
943 break;
944 case APR_DBD_TYPE_NULL:
945 default:
946 bind[i].buffer_type = MYSQL_TYPE_NULL;
947 break;
948 }
949 }
950
951 return;
952}
953
954static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql,
955 int *nrows, apr_dbd_prepared_t *statement,
956 const void **values)
957{
958 MYSQL_BIND *bind;
959 int ret;
960
961 if (sql->trans && sql->trans->errnum) {
962 return sql->trans->errnum;
963 }
964
965 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
966
967 dbd_mysql_bbind(pool, statement, values, bind);
968
969 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
970
971 if (TXN_NOTICE_ERRORS(sql->trans)) {
972 sql->trans->errnum = ret;
973 }
974 return ret;
975}
976
977static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
978 apr_dbd_prepared_t *statement, va_list args)
979{
980 const void **values;
981 int i;
982
983 if (sql->trans && sql->trans->errnum) {
984 return sql->trans->errnum;
985 }
986
987 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
988
989 for (i = 0; i < statement->nvals; i++) {
990 values[i] = va_arg(args, const void*);
991 }
992
993 return dbd_mysql_pbquery(pool, sql, nrows, statement, values);
994}
995
996static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
997 apr_dbd_results_t **res,
998 apr_dbd_prepared_t *statement, int random,
999 const void **args)
1000{
1001 int ret;
1002 MYSQL_BIND *bind;
1003
1004 if (sql->trans && sql->trans->errnum) {
1005 return sql->trans->errnum;
1006 }
1007
1008 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
1009
1010 dbd_mysql_bbind(pool, statement, args, bind);
1011
1012 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
1013
1014 if (TXN_NOTICE_ERRORS(sql->trans)) {
1015 sql->trans->errnum = ret;
1016 }
1017 return ret;
1018}
1019
1020static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
1021 apr_dbd_results_t **res,
1022 apr_dbd_prepared_t *statement, int random,
1023 va_list args)
1024{
1025 const void **values;
1026 int i;
1027
1028 if (sql->trans && sql->trans->errnum) {
1029 return sql->trans->errnum;
1030 }
1031
1032 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1033
1034 for (i = 0; i < statement->nvals; i++) {
1035 values[i] = va_arg(args, const void*);
1036 }
1037
1038 return dbd_mysql_pbselect(pool, sql, res, statement, random, values);
1039}
1040
1041static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
1042{
1043 int ret = -1;
1044 if (trans) {
1045 /* rollback on error or explicit rollback request */
1046 if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1047 trans->errnum = 0;
1048 ret = mysql_rollback(trans->handle->conn);
1049 }
1050 else {
1051 ret = mysql_commit(trans->handle->conn);
1052 }
1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements. See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "apu.h"
18
19#if APU_HAVE_MYSQL
20
21#include "apu_version.h"
22#include "apu_config.h"
23
24#include <ctype.h>
25#include <stdlib.h>
26
27#if defined(HAVE_MYSQL_MYSQL_H)
28#if defined(HAVE_MYSQL_MY_GLOBAL_H)
29#include <mysql/my_global.h>
30#if defined(HAVE_MYSQL_MY_SYS_H)
31#include <mysql/my_sys.h>
32#endif
33#endif
34#include <mysql/mysql.h>
35#include <mysql/errmsg.h>
36#else /* !defined(HAVE_MYSQL_MYSQL_H) */
37#if defined(HAVE_MY_GLOBAL_H)
38#include <my_global.h>
39#if defined(HAVE_MY_SYS_H)
40#include <my_sys.h>
41#endif
42#endif
43#include <mysql.h>
44#include <errmsg.h>
45#endif
46
47#include "apr_strings.h"
48#include "apr_lib.h"
49#include "apr_buckets.h"
50
51#include "apr_dbd_internal.h"
52
53/* default maximum field size 1 MB */
54#define FIELDSIZE 1048575
55
56struct apr_dbd_prepared_t {
57 MYSQL_STMT* stmt;
58 int nargs;
59 int nvals;
60 apr_dbd_type_e *types;
61};
62
63struct apr_dbd_transaction_t {
64 int mode;
65 int errnum;
66 apr_dbd_t *handle;
67};
68
69struct apr_dbd_t {
70 MYSQL* conn ;
71 apr_dbd_transaction_t* trans ;
72 unsigned long fldsz;
73};
74
75struct apr_dbd_results_t {
76 int random;
77 MYSQL_RES *res;
78 MYSQL_STMT *statement;
79 MYSQL_BIND *bind;
80 apr_pool_t *pool;
81};
82struct apr_dbd_row_t {
83 MYSQL_ROW row;
84 apr_dbd_results_t *res;
85 unsigned long *len;
86};
87
88/* MySQL specific bucket for BLOB types */
89typedef struct apr_bucket_lob apr_bucket_lob;
90/**
91 * A bucket referring to a MySQL BLOB
92 */
93struct apr_bucket_lob {
94 /** Number of buckets using this memory */
95 apr_bucket_refcount refcount;
96 /** The row this bucket refers to */
97 const apr_dbd_row_t *row;
98 /** The column this bucket refers to */
99 int col;
100 /** The pool into which any needed structures should
101 * be created while reading from this bucket */
102 apr_pool_t *readpool;
103};
104
105static void lob_bucket_destroy(void *data);
106static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
107 apr_size_t *len, apr_read_type_e block);
108static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
109 const apr_dbd_row_t *row, int col,
110 apr_off_t offset, apr_size_t len,
111 apr_pool_t *p);
112static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
113 apr_off_t offset,
114 apr_size_t len, apr_pool_t *p,
115 apr_bucket_alloc_t *list);
116static int dbd_mysql_num_cols(apr_dbd_results_t *res);
117
118static const apr_bucket_type_t apr_bucket_type_lob = {
119 "LOB", 5, APR_BUCKET_DATA,
120 lob_bucket_destroy,
121 lob_bucket_read,
122 apr_bucket_setaside_notimpl,
123 apr_bucket_shared_split,
124 apr_bucket_shared_copy
125};
126
127static void lob_bucket_destroy(void *data)
128{
129 apr_bucket_lob *f = data;
130
131 if (apr_bucket_shared_destroy(f)) {
132 /* no need to destroy database objects here; it will get
133 * done automatically when the pool gets cleaned up */
134 apr_bucket_free(f);
135 }
136}
137
138static apr_status_t lob_bucket_read(apr_bucket *e, const char **str,
139 apr_size_t *len, apr_read_type_e block)
140{
141 apr_bucket_lob *a = e->data;
142 const apr_dbd_row_t *row = a->row;
143 apr_dbd_results_t *res = row->res;
144 int col = a->col;
145 apr_bucket *b = NULL;
146 int rv;
147 apr_size_t blength = e->length; /* bytes remaining in file past offset */
148 apr_off_t boffset = e->start;
149 MYSQL_BIND *bind = &res->bind[col];
150
151 *str = NULL; /* in case we die prematurely */
152
153 /* fetch from offset if not at the beginning */
154 if (boffset > 0) {
155 rv = mysql_stmt_fetch_column(res->statement, bind, col,
156 (unsigned long) boffset);
157 if (rv != 0) {
158 return APR_EGENERAL;
159 }
160 }
161 blength -= blength > bind->buffer_length ? bind->buffer_length : blength;
162 *len = e->length - blength;
163 *str = bind->buffer;
164
165 /* allocate new buffer, since we used this one for the bucket */
166 bind->buffer = apr_palloc(res->pool, bind->buffer_length);
167
168 /*
169 * Change the current bucket to refer to what we read,
170 * even if we read nothing because we hit EOF.
171 */
172 apr_bucket_pool_make(e, *str, *len, res->pool);
173
174 /* If we have more to read from the field, then create another bucket */
175 if (blength > 0) {
176 /* for efficiency, we can just build a new apr_bucket struct
177 * to wrap around the existing LOB bucket */
178 b = apr_bucket_alloc(sizeof(*b), e->list);
179 b->start = boffset + *len;
180 b->length = blength;
181 b->data = a;
182 b->type = &apr_bucket_type_lob;
183 b->free = apr_bucket_free;
184 b->list = e->list;
185 APR_BUCKET_INSERT_AFTER(e, b);
186 }
187 else {
188 lob_bucket_destroy(a);
189 }
190
191 return APR_SUCCESS;
192}
193
194static apr_bucket *apr_bucket_lob_make(apr_bucket *b,
195 const apr_dbd_row_t *row, int col,
196 apr_off_t offset, apr_size_t len,
197 apr_pool_t *p)
198{
199 apr_bucket_lob *f;
200
201 f = apr_bucket_alloc(sizeof(*f), b->list);
202 f->row = row;
203 f->col = col;
204 f->readpool = p;
205
206 b = apr_bucket_shared_make(b, f, offset, len);
207 b->type = &apr_bucket_type_lob;
208
209 return b;
210}
211
212static apr_bucket *apr_bucket_lob_create(const apr_dbd_row_t *row, int col,
213 apr_off_t offset,
214 apr_size_t len, apr_pool_t *p,
215 apr_bucket_alloc_t *list)
216{
217 apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
218
219 APR_BUCKET_INIT(b);
220 b->free = apr_bucket_free;
221 b->list = list;
222 return apr_bucket_lob_make(b, row, col, offset, len, p);
223}
224
225static apr_status_t free_result(void *data)
226{
227 mysql_free_result(data);
228 return APR_SUCCESS;
229}
230
231static int dbd_mysql_select(apr_pool_t *pool, apr_dbd_t *sql,
232 apr_dbd_results_t **results,
233 const char *query, int seek)
234{
235 int sz;
236 int ret;
237 if (sql->trans && sql->trans->errnum) {
238 return sql->trans->errnum;
239 }
240 ret = mysql_query(sql->conn, query);
241 if (!ret) {
242 if (sz = mysql_field_count(sql->conn), sz > 0) {
243 if (!*results) {
244 *results = apr_palloc(pool, sizeof(apr_dbd_results_t));
245 }
246 (*results)->random = seek;
247 (*results)->statement = NULL;
248 (*results)->pool = pool;
249 if (seek) {
250 (*results)->res = mysql_store_result(sql->conn);
251 }
252 else {
253 (*results)->res = mysql_use_result(sql->conn);
254 }
255 apr_pool_cleanup_register(pool, (*results)->res,
256 free_result,apr_pool_cleanup_null);
257 }
258 } else {
259 ret = mysql_errno(sql->conn);
260 }
261
262 if (TXN_NOTICE_ERRORS(sql->trans)) {
263 sql->trans->errnum = ret;
264 }
265 return ret;
266}
267
268static const char *dbd_mysql_get_name(const apr_dbd_results_t *res, int n)
269{
270 if ((n < 0) || (n >= (int) mysql_num_fields(res->res))) {
271 return NULL;
272 }
273
274 return mysql_fetch_fields(res->res)[n].name;
275}
276
277static int dbd_mysql_get_row(apr_pool_t *pool, apr_dbd_results_t *res,
278 apr_dbd_row_t **row, int rownum)
279{
280 MYSQL_ROW r = NULL;
281 int ret = 0;
282
283 if (res->statement) {
284 if (res->random) {
285 if (rownum > 0) {
286 mysql_stmt_data_seek(res->statement, (my_ulonglong) --rownum);
287 }
288 else {
289 return -1; /* invalid row */
290 }
291 }
292 ret = mysql_stmt_fetch(res->statement);
293 switch (ret) {
294 case 1:
295 ret = mysql_stmt_errno(res->statement);
296 break;
297 case MYSQL_NO_DATA:
298 ret = -1;
299 break;
300 default:
301 ret = 0; /* bad luck - get_entry will deal with this */
302 break;
303 }
304 }
305 else {
306 if (res->random) {
307 if (rownum > 0) {
308 mysql_data_seek(res->res, (my_ulonglong) --rownum);
309 }
310 else {
311 return -1; /* invalid row */
312 }
313 }
314 r = mysql_fetch_row(res->res);
315 if (r == NULL) {
316 ret = -1;
317 }
318 }
319 if (ret == 0) {
320 if (!*row) {
321 *row = apr_palloc(pool, sizeof(apr_dbd_row_t));
322 }
323 (*row)->row = r;
324 (*row)->res = res;
325 (*row)->len = mysql_fetch_lengths(res->res);
326 }
327 else {
328 apr_pool_cleanup_run(res->pool, res->res, free_result);
329 }
330 return ret;
331}
332#if 0
333/* An improved API that was proposed but not followed up */
334static int dbd_mysql_get_entry(const apr_dbd_row_t *row, int n,
335 apr_dbd_datum_t *val)
336{
337 MYSQL_BIND *bind;
338 if (dbd_mysql_num_cols(row->res) <= n) {
339 return NULL;
340 }
341 if (row->res->statement) {
342 bind = &row->res->bind[n];
343 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
344 val->type = APR_DBD_VALUE_NULL;
345 return -1;
346 }
347 if (*bind->is_null) {
348 val->type = APR_DBD_VALUE_NULL;
349 return -1;
350 }
351 else {
352 val->type = APR_DBD_VALUE_STRING;
353 val->value.stringval = bind->buffer;
354 }
355 }
356 else {
357 val->type = APR_DBD_VALUE_STRING;
358 val->value.stringval = row->row[n];
359 }
360 return 0;
361}
362#else
363
364static const char *dbd_mysql_get_entry(const apr_dbd_row_t *row, int n)
365{
366 MYSQL_BIND *bind;
367 if (dbd_mysql_num_cols(row->res) <= n) {
368 return NULL;
369 }
370 if (row->res->statement) {
371 bind = &row->res->bind[n];
372 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
373 return NULL;
374 }
375 if (*bind->is_null) {
376 return NULL;
377 }
378 else {
379 return bind->buffer;
380 }
381 }
382 else {
383 return row->row[n];
384 }
385 return NULL;
386}
387#endif
388
389static apr_status_t dbd_mysql_datum_get(const apr_dbd_row_t *row, int n,
390 apr_dbd_type_e type, void *data)
391{
392 if (row->res->statement) {
393 MYSQL_BIND *bind = &row->res->bind[n];
394 unsigned long len = *bind->length;
395
396 if (mysql_stmt_fetch_column(row->res->statement, bind, n, 0) != 0) {
397 return APR_EGENERAL;
398 }
399
400 if (*bind->is_null) {
401 return APR_ENOENT;
402 }
403
404 switch (type) {
405 case APR_DBD_TYPE_TINY:
406 *(char*)data = atoi(bind->buffer);
407 break;
408 case APR_DBD_TYPE_UTINY:
409 *(unsigned char*)data = atoi(bind->buffer);
410 break;
411 case APR_DBD_TYPE_SHORT:
412 *(short*)data = atoi(bind->buffer);
413 break;
414 case APR_DBD_TYPE_USHORT:
415 *(unsigned short*)data = atoi(bind->buffer);
416 break;
417 case APR_DBD_TYPE_INT:
418 *(int*)data = atoi(bind->buffer);
419 break;
420 case APR_DBD_TYPE_UINT:
421 *(unsigned int*)data = atoi(bind->buffer);
422 break;
423 case APR_DBD_TYPE_LONG:
424 *(long*)data = atol(bind->buffer);
425 break;
426 case APR_DBD_TYPE_ULONG:
427 *(unsigned long*)data = atol(bind->buffer);
428 break;
429 case APR_DBD_TYPE_LONGLONG:
430 *(apr_int64_t*)data = apr_atoi64(bind->buffer);
431 break;
432 case APR_DBD_TYPE_ULONGLONG:
433 *(apr_uint64_t*)data = apr_atoi64(bind->buffer);
434 break;
435 case APR_DBD_TYPE_FLOAT:
436 *(float*)data = (float) atof(bind->buffer);
437 break;
438 case APR_DBD_TYPE_DOUBLE:
439 *(double*)data = atof(bind->buffer);
440 break;
441 case APR_DBD_TYPE_STRING:
442 case APR_DBD_TYPE_TEXT:
443 case APR_DBD_TYPE_TIME:
444 case APR_DBD_TYPE_DATE:
445 case APR_DBD_TYPE_DATETIME:
446 case APR_DBD_TYPE_TIMESTAMP:
447 case APR_DBD_TYPE_ZTIMESTAMP:
448 *((char*)bind->buffer+bind->buffer_length-1) = '\0';
449 *(char**)data = bind->buffer;
450 break;
451 case APR_DBD_TYPE_BLOB:
452 case APR_DBD_TYPE_CLOB:
453 {
454 apr_bucket *e;
455 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
456
457 e = apr_bucket_lob_create(row, n, 0, len,
458 row->res->pool, b->bucket_alloc);
459 APR_BRIGADE_INSERT_TAIL(b, e);
460 }
461 break;
462 case APR_DBD_TYPE_NULL:
463 *(void**)data = NULL;
464 break;
465 default:
466 return APR_EGENERAL;
467 }
468 }
469 else {
470 if (row->row[n] == NULL) {
471 return APR_ENOENT;
472 }
473
474 switch (type) {
475 case APR_DBD_TYPE_TINY:
476 *(char*)data = atoi(row->row[n]);
477 break;
478 case APR_DBD_TYPE_UTINY:
479 *(unsigned char*)data = atoi(row->row[n]);
480 break;
481 case APR_DBD_TYPE_SHORT:
482 *(short*)data = atoi(row->row[n]);
483 break;
484 case APR_DBD_TYPE_USHORT:
485 *(unsigned short*)data = atoi(row->row[n]);
486 break;
487 case APR_DBD_TYPE_INT:
488 *(int*)data = atoi(row->row[n]);
489 break;
490 case APR_DBD_TYPE_UINT:
491 *(unsigned int*)data = atoi(row->row[n]);
492 break;
493 case APR_DBD_TYPE_LONG:
494 *(long*)data = atol(row->row[n]);
495 break;
496 case APR_DBD_TYPE_ULONG:
497 *(unsigned long*)data = atol(row->row[n]);
498 break;
499 case APR_DBD_TYPE_LONGLONG:
500 *(apr_int64_t*)data = apr_atoi64(row->row[n]);
501 break;
502 case APR_DBD_TYPE_ULONGLONG:
503 *(apr_uint64_t*)data = apr_atoi64(row->row[n]);
504 break;
505 case APR_DBD_TYPE_FLOAT:
506 *(float*)data = (float) atof(row->row[n]);
507 break;
508 case APR_DBD_TYPE_DOUBLE:
509 *(double*)data = atof(row->row[n]);
510 break;
511 case APR_DBD_TYPE_STRING:
512 case APR_DBD_TYPE_TEXT:
513 case APR_DBD_TYPE_TIME:
514 case APR_DBD_TYPE_DATE:
515 case APR_DBD_TYPE_DATETIME:
516 case APR_DBD_TYPE_TIMESTAMP:
517 case APR_DBD_TYPE_ZTIMESTAMP:
518 *(char**)data = row->row[n];
519 break;
520 case APR_DBD_TYPE_BLOB:
521 case APR_DBD_TYPE_CLOB:
522 {
523 apr_bucket *e;
524 apr_bucket_brigade *b = (apr_bucket_brigade*)data;
525
526 e = apr_bucket_pool_create(row->row[n], row->len[n],
527 row->res->pool, b->bucket_alloc);
528 APR_BRIGADE_INSERT_TAIL(b, e);
529 }
530 break;
531 case APR_DBD_TYPE_NULL:
532 *(void**)data = NULL;
533 break;
534 default:
535 return APR_EGENERAL;
536 }
537 }
538 return 0;
539}
540
541static const char *dbd_mysql_error(apr_dbd_t *sql, int n)
542{
543 return mysql_error(sql->conn);
544}
545
546static int dbd_mysql_query(apr_dbd_t *sql, int *nrows, const char *query)
547{
548 int ret;
549 if (sql->trans && sql->trans->errnum) {
550 return sql->trans->errnum;
551 }
552 ret = mysql_query(sql->conn, query);
553 if (ret != 0) {
554 ret = mysql_errno(sql->conn);
555 }
556 *nrows = (int) mysql_affected_rows(sql->conn);
557 if (TXN_NOTICE_ERRORS(sql->trans)) {
558 sql->trans->errnum = ret;
559 }
560 return ret;
561}
562
563static const char *dbd_mysql_escape(apr_pool_t *pool, const char *arg,
564 apr_dbd_t *sql)
565{
566 unsigned long len = strlen(arg);
567 char *ret = apr_palloc(pool, 2*len + 1);
568 mysql_real_escape_string(sql->conn, ret, arg, len);
569 return ret;
570}
571
572static apr_status_t stmt_close(void *data)
573{
574 mysql_stmt_close(data);
575 return APR_SUCCESS;
576}
577
578static int dbd_mysql_prepare(apr_pool_t *pool, apr_dbd_t *sql,
579 const char *query, const char *label,
580 int nargs, int nvals, apr_dbd_type_e *types,
581 apr_dbd_prepared_t **statement)
582{
583 /* Translate from apr_dbd to native query format */
584 int ret;
585
586 if (!*statement) {
587 *statement = apr_palloc(pool, sizeof(apr_dbd_prepared_t));
588 }
589 (*statement)->stmt = mysql_stmt_init(sql->conn);
590
591 if ((*statement)->stmt) {
592 apr_pool_cleanup_register(pool, (*statement)->stmt,
593 stmt_close, apr_pool_cleanup_null);
594 ret = mysql_stmt_prepare((*statement)->stmt, query, strlen(query));
595
596 if (ret != 0) {
597 ret = mysql_stmt_errno((*statement)->stmt);
598 }
599
600 (*statement)->nargs = nargs;
601 (*statement)->nvals = nvals;
602 (*statement)->types = types;
603
604 return ret;
605 }
606
607 return CR_OUT_OF_MEMORY;
608}
609
610static void dbd_mysql_bind(apr_dbd_prepared_t *statement,
611 const char **values, MYSQL_BIND *bind)
612{
613 int i, j;
614
615 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
616 bind[i].length = &bind[i].buffer_length;
617 bind[i].is_unsigned = 0;
618 bind[i].is_null = NULL;
619
620 if (values[j] == NULL) {
621 bind[i].buffer_type = MYSQL_TYPE_NULL;
622 }
623 else {
624 switch (statement->types[i]) {
625 case APR_DBD_TYPE_BLOB:
626 case APR_DBD_TYPE_CLOB:
627 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
628 bind[i].buffer = (void*)values[j];
629 bind[i].buffer_length = atol(values[++j]);
630
631 /* skip table and column */
632 j += 2;
633 break;
634 default:
635 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
636 bind[i].buffer = (void*)values[j];
637 bind[i].buffer_length = strlen(values[j]);
638 break;
639 }
640 }
641 }
642
643 return;
644}
645
646static int dbd_mysql_pquery_internal(apr_pool_t *pool, apr_dbd_t *sql,
647 int *nrows, apr_dbd_prepared_t *statement,
648 MYSQL_BIND *bind)
649{
650 int ret;
651
652 ret = mysql_stmt_bind_param(statement->stmt, bind);
653 if (ret != 0) {
654 *nrows = 0;
655 ret = mysql_stmt_errno(statement->stmt);
656 }
657 else {
658 ret = mysql_stmt_execute(statement->stmt);
659 if (ret != 0) {
660 ret = mysql_stmt_errno(statement->stmt);
661 }
662 *nrows = (int) mysql_stmt_affected_rows(statement->stmt);
663 }
664
665 return ret;
666}
667
668static int dbd_mysql_pquery(apr_pool_t *pool, apr_dbd_t *sql,
669 int *nrows, apr_dbd_prepared_t *statement,
670 const char **values)
671{
672 MYSQL_BIND *bind;
673 int ret;
674
675 if (sql->trans && sql->trans->errnum) {
676 return sql->trans->errnum;
677 }
678
679 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
680
681 dbd_mysql_bind(statement, values, bind);
682
683 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
684
685 if (TXN_NOTICE_ERRORS(sql->trans)) {
686 sql->trans->errnum = ret;
687 }
688 return ret;
689}
690
691static int dbd_mysql_pvquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
692 apr_dbd_prepared_t *statement, va_list args)
693{
694 const char **values;
695 int i;
696
697 if (sql->trans && sql->trans->errnum) {
698 return sql->trans->errnum;
699 }
700
701 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
702
703 for (i = 0; i < statement->nvals; i++) {
704 values[i] = va_arg(args, const char*);
705 }
706
707 return dbd_mysql_pquery(pool, sql, nrows, statement, values);
708}
709
710static int dbd_mysql_pselect_internal(apr_pool_t *pool, apr_dbd_t *sql,
711 apr_dbd_results_t **res,
712 apr_dbd_prepared_t *statement,
713 int random, MYSQL_BIND *bind)
714{
715 int nfields, i;
716 my_bool *is_nullr;
717#if MYSQL_VERSION_ID >= 50000
718 my_bool *error;
719#endif
720 int ret;
721 unsigned long *length, maxlen;
722
723 ret = mysql_stmt_bind_param(statement->stmt, bind);
724 if (ret == 0) {
725 ret = mysql_stmt_execute(statement->stmt);
726 if (!ret) {
727 if (!*res) {
728 *res = apr_pcalloc(pool, sizeof(apr_dbd_results_t));
729 }
730 (*res)->random = random;
731 (*res)->statement = statement->stmt;
732 (*res)->res = mysql_stmt_result_metadata(statement->stmt);
733 (*res)->pool = pool;
734 apr_pool_cleanup_register(pool, (*res)->res,
735 free_result, apr_pool_cleanup_null);
736 nfields = mysql_num_fields((*res)->res);
737 if (!(*res)->bind) {
738 (*res)->bind = apr_palloc(pool, nfields*sizeof(MYSQL_BIND));
739 length = apr_pcalloc(pool, nfields*sizeof(unsigned long));
740#if MYSQL_VERSION_ID >= 50000
741 error = apr_palloc(pool, nfields*sizeof(my_bool));
742#endif
743 is_nullr = apr_pcalloc(pool, nfields*sizeof(my_bool));
744 for ( i = 0; i < nfields; ++i ) {
745 maxlen = ((*res)->res->fields[i].length < sql->fldsz ?
746 (*res)->res->fields[i].length : sql->fldsz) + 1;
747 if ((*res)->res->fields[i].type == MYSQL_TYPE_BLOB) {
748 (*res)->bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
749 }
750 else {
751 (*res)->bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
752 }
753 (*res)->bind[i].buffer_length = maxlen;
754 (*res)->bind[i].length = &length[i];
755 (*res)->bind[i].buffer = apr_palloc(pool, maxlen);
756 (*res)->bind[i].is_null = is_nullr+i;
757#if MYSQL_VERSION_ID >= 50000
758 (*res)->bind[i].error = error+i;
759#endif
760 }
761 }
762 ret = mysql_stmt_bind_result(statement->stmt, (*res)->bind);
763 if (!ret) {
764 ret = mysql_stmt_store_result(statement->stmt);
765 }
766 }
767 }
768 if (ret != 0) {
769 ret = mysql_stmt_errno(statement->stmt);
770 }
771
772 return ret;
773}
774
775static int dbd_mysql_pselect(apr_pool_t *pool, apr_dbd_t *sql,
776 apr_dbd_results_t **res,
777 apr_dbd_prepared_t *statement, int random,
778 const char **args)
779{
780 int ret;
781 MYSQL_BIND *bind;
782
783 if (sql->trans && sql->trans->errnum) {
784 return sql->trans->errnum;
785 }
786
787 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
788
789 dbd_mysql_bind(statement, args, bind);
790
791 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
792
793 if (TXN_NOTICE_ERRORS(sql->trans)) {
794 sql->trans->errnum = ret;
795 }
796 return ret;
797}
798
799static int dbd_mysql_pvselect(apr_pool_t *pool, apr_dbd_t *sql,
800 apr_dbd_results_t **res,
801 apr_dbd_prepared_t *statement, int random,
802 va_list args)
803{
804 const char **values;
805 int i;
806
807 if (sql->trans && sql->trans->errnum) {
808 return sql->trans->errnum;
809 }
810
811 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
812
813 for (i = 0; i < statement->nvals; i++) {
814 values[i] = va_arg(args, const char*);
815 }
816
817 return dbd_mysql_pselect(pool, sql, res, statement, random, values);
818}
819
820static void dbd_mysql_bbind(apr_pool_t *pool, apr_dbd_prepared_t *statement,
821 const void **values, MYSQL_BIND *bind)
822{
823 void *arg;
824 int i, j;
825 apr_dbd_type_e type;
826
827 for (i = 0, j = 0; i < statement->nargs; i++, j++) {
828 arg = (void *)values[j];
829
830 bind[i].length = &bind[i].buffer_length;
831 bind[i].is_null = NULL;
832
833 type = (arg == NULL ? APR_DBD_TYPE_NULL : statement->types[i]);
834 switch (type) {
835 case APR_DBD_TYPE_TINY:
836 bind[i].buffer = arg;
837 bind[i].buffer_type = MYSQL_TYPE_TINY;
838 bind[i].is_unsigned = 0;
839 break;
840 case APR_DBD_TYPE_UTINY:
841 bind[i].buffer = arg;
842 bind[i].buffer_type = MYSQL_TYPE_TINY;
843 bind[i].is_unsigned = 1;
844 break;
845 case APR_DBD_TYPE_SHORT:
846 bind[i].buffer = arg;
847 bind[i].buffer_type = MYSQL_TYPE_SHORT;
848 bind[i].is_unsigned = 0;
849 break;
850 case APR_DBD_TYPE_USHORT:
851 bind[i].buffer = arg;
852 bind[i].buffer_type = MYSQL_TYPE_SHORT;
853 bind[i].is_unsigned = 1;
854 break;
855 case APR_DBD_TYPE_INT:
856 bind[i].buffer = arg;
857 bind[i].buffer_type = MYSQL_TYPE_LONG;
858 bind[i].is_unsigned = 0;
859 break;
860 case APR_DBD_TYPE_UINT:
861 bind[i].buffer = arg;
862 bind[i].buffer_type = MYSQL_TYPE_LONG;
863 bind[i].is_unsigned = 1;
864 break;
865 case APR_DBD_TYPE_LONG:
866 if (sizeof(int) == sizeof(long)) {
867 bind[i].buffer = arg;
868 }
869 else {
870 bind[i].buffer = apr_palloc(pool, sizeof(int));
871 *(int*)bind[i].buffer = *(long*)arg;
872 }
873 bind[i].buffer_type = MYSQL_TYPE_LONG;
874 bind[i].is_unsigned = 0;
875 break;
876 case APR_DBD_TYPE_ULONG:
877 if (sizeof(unsigned int) == sizeof(unsigned long)) {
878 bind[i].buffer = arg;
879 }
880 else {
881 bind[i].buffer = apr_palloc(pool, sizeof(unsigned int));
882 *(unsigned int*)bind[i].buffer = *(unsigned long*)arg;
883 }
884 bind[i].buffer_type = MYSQL_TYPE_LONG;
885 bind[i].is_unsigned = 1;
886 break;
887 case APR_DBD_TYPE_LONGLONG:
888 if (sizeof(my_ulonglong) == sizeof(apr_int64_t)) {
889 bind[i].buffer = arg;
890 bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
891 }
892 else { /* have to downsize, long long is not portable */
893 bind[i].buffer = apr_palloc(pool, sizeof(long));
894 *(long*)bind[i].buffer = (long) *(apr_int64_t*)arg;
895 bind[i].buffer_type = MYSQL_TYPE_LONG;
896 }
897 bind[i].is_unsigned = 0;
898 break;
899 case APR_DBD_TYPE_ULONGLONG:
900 if (sizeof(my_ulonglong) == sizeof(apr_uint64_t)) {
901 bind[i].buffer = arg;
902 bind[i].buffer_type = MYSQL_TYPE_LONGLONG;
903 }
904 else { /* have to downsize, long long is not portable */
905 bind[i].buffer = apr_palloc(pool, sizeof(long));
906 *(unsigned long*)bind[i].buffer =
907 (unsigned long) *(apr_uint64_t*)arg;
908 bind[i].buffer_type = MYSQL_TYPE_LONG;
909 }
910 bind[i].is_unsigned = 1;
911 break;
912 case APR_DBD_TYPE_FLOAT:
913 bind[i].buffer = arg;
914 bind[i].buffer_type = MYSQL_TYPE_FLOAT;
915 bind[i].is_unsigned = 0;
916 break;
917 case APR_DBD_TYPE_DOUBLE:
918 bind[i].buffer = arg;
919 bind[i].buffer_type = MYSQL_TYPE_DOUBLE;
920 bind[i].is_unsigned = 0;
921 break;
922 case APR_DBD_TYPE_STRING:
923 case APR_DBD_TYPE_TEXT:
924 case APR_DBD_TYPE_TIME:
925 case APR_DBD_TYPE_DATE:
926 case APR_DBD_TYPE_DATETIME:
927 case APR_DBD_TYPE_TIMESTAMP:
928 case APR_DBD_TYPE_ZTIMESTAMP:
929 bind[i].buffer = arg;
930 bind[i].buffer_type = MYSQL_TYPE_VAR_STRING;
931 bind[i].is_unsigned = 0;
932 bind[i].buffer_length = strlen((const char *)arg);
933 break;
934 case APR_DBD_TYPE_BLOB:
935 case APR_DBD_TYPE_CLOB:
936 bind[i].buffer = (void *)arg;
937 bind[i].buffer_type = MYSQL_TYPE_LONG_BLOB;
938 bind[i].is_unsigned = 0;
939 bind[i].buffer_length = *(apr_size_t*)values[++j];
940
941 /* skip table and column */
942 j += 2;
943 break;
944 case APR_DBD_TYPE_NULL:
945 default:
946 bind[i].buffer_type = MYSQL_TYPE_NULL;
947 break;
948 }
949 }
950
951 return;
952}
953
954static int dbd_mysql_pbquery(apr_pool_t *pool, apr_dbd_t *sql,
955 int *nrows, apr_dbd_prepared_t *statement,
956 const void **values)
957{
958 MYSQL_BIND *bind;
959 int ret;
960
961 if (sql->trans && sql->trans->errnum) {
962 return sql->trans->errnum;
963 }
964
965 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
966
967 dbd_mysql_bbind(pool, statement, values, bind);
968
969 ret = dbd_mysql_pquery_internal(pool, sql, nrows, statement, bind);
970
971 if (TXN_NOTICE_ERRORS(sql->trans)) {
972 sql->trans->errnum = ret;
973 }
974 return ret;
975}
976
977static int dbd_mysql_pvbquery(apr_pool_t *pool, apr_dbd_t *sql, int *nrows,
978 apr_dbd_prepared_t *statement, va_list args)
979{
980 const void **values;
981 int i;
982
983 if (sql->trans && sql->trans->errnum) {
984 return sql->trans->errnum;
985 }
986
987 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
988
989 for (i = 0; i < statement->nvals; i++) {
990 values[i] = va_arg(args, const void*);
991 }
992
993 return dbd_mysql_pbquery(pool, sql, nrows, statement, values);
994}
995
996static int dbd_mysql_pbselect(apr_pool_t *pool, apr_dbd_t *sql,
997 apr_dbd_results_t **res,
998 apr_dbd_prepared_t *statement, int random,
999 const void **args)
1000{
1001 int ret;
1002 MYSQL_BIND *bind;
1003
1004 if (sql->trans && sql->trans->errnum) {
1005 return sql->trans->errnum;
1006 }
1007
1008 bind = apr_palloc(pool, statement->nargs * sizeof(MYSQL_BIND));
1009
1010 dbd_mysql_bbind(pool, statement, args, bind);
1011
1012 ret = dbd_mysql_pselect_internal(pool, sql, res, statement, random, bind);
1013
1014 if (TXN_NOTICE_ERRORS(sql->trans)) {
1015 sql->trans->errnum = ret;
1016 }
1017 return ret;
1018}
1019
1020static int dbd_mysql_pvbselect(apr_pool_t *pool, apr_dbd_t *sql,
1021 apr_dbd_results_t **res,
1022 apr_dbd_prepared_t *statement, int random,
1023 va_list args)
1024{
1025 const void **values;
1026 int i;
1027
1028 if (sql->trans && sql->trans->errnum) {
1029 return sql->trans->errnum;
1030 }
1031
1032 values = apr_palloc(pool, sizeof(*values) * statement->nvals);
1033
1034 for (i = 0; i < statement->nvals; i++) {
1035 values[i] = va_arg(args, const void*);
1036 }
1037
1038 return dbd_mysql_pbselect(pool, sql, res, statement, random, values);
1039}
1040
1041static int dbd_mysql_end_transaction(apr_dbd_transaction_t *trans)
1042{
1043 int ret = -1;
1044 if (trans) {
1045 /* rollback on error or explicit rollback request */
1046 if (trans->errnum || TXN_DO_ROLLBACK(trans)) {
1047 trans->errnum = 0;
1048 ret = mysql_rollback(trans->handle->conn);
1049 }
1050 else {
1051 ret = mysql_commit(trans->handle->conn);
1052 }
1053 ret |= mysql_autocommit(trans->handle->conn, 1);
1054 trans->handle->trans = NULL;
1053 }
1055 }
1054 ret |= mysql_autocommit(trans->handle->conn, 1);
1055 trans->handle->trans = NULL;
1056 return ret;
1057}
1058/* Whether or not transactions work depends on whether the
1059 * underlying DB supports them within MySQL. Unfortunately
1060 * it fails silently with the default InnoDB.
1061 */
1062
1063static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1064 apr_dbd_transaction_t **trans)
1065{
1066 /* Don't try recursive transactions here */
1067 if (handle->trans) {
1068 dbd_mysql_end_transaction(handle->trans) ;
1069 }
1070 if (!*trans) {
1071 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1072 }
1073 (*trans)->errnum = mysql_autocommit(handle->conn, 0);
1074 (*trans)->handle = handle;
1075 handle->trans = *trans;
1076 return (*trans)->errnum;
1077}
1078
1079static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
1080{
1081 if (!trans)
1082 return APR_DBD_TRANSACTION_COMMIT;
1083
1084 return trans->mode;
1085}
1086
1087static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans,
1088 int mode)
1089{
1090 if (!trans)
1091 return APR_DBD_TRANSACTION_COMMIT;
1092
1093 return trans->mode = (mode & TXN_MODE_BITS);
1094}
1095
1096static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params,
1097 const char **error)
1098{
1099 static const char *const delims = " \r\n\t;|,";
1100 const char *ptr;
1101 int i;
1102 const char *key;
1103 size_t klen;
1104 const char *value;
1105 size_t vlen;
1106#if MYSQL_VERSION_ID >= 50013
1107 my_bool do_reconnect = 1;
1108#endif
1109 MYSQL *real_conn;
1110 unsigned long flags = 0;
1111
1112 struct {
1113 const char *field;
1114 const char *value;
1115 } fields[] = {
1116 {"host", NULL},
1117 {"user", NULL},
1118 {"pass", NULL},
1119 {"dbname", NULL},
1120 {"port", NULL},
1121 {"sock", NULL},
1122 {"flags", NULL},
1123 {"fldsz", NULL},
1124 {"group", NULL},
1125 {"reconnect", NULL},
1126 {NULL, NULL}
1127 };
1128 unsigned int port = 0;
1129 apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t));
1130 sql->fldsz = FIELDSIZE;
1131 sql->conn = mysql_init(sql->conn);
1132 if ( sql->conn == NULL ) {
1133 return NULL;
1134 }
1135 for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
1136 /* don't dereference memory that may not belong to us */
1137 if (ptr == params) {
1138 ++ptr;
1139 continue;
1140 }
1141 for (key = ptr-1; apr_isspace(*key); --key);
1142 klen = 0;
1143 while (apr_isalpha(*key)) {
1144 /* don't parse backwards off the start of the string */
1145 if (key == params) {
1146 --key;
1147 ++klen;
1148 break;
1149 }
1150 --key;
1151 ++klen;
1152 }
1153 ++key;
1154 for (value = ptr+1; apr_isspace(*value); ++value);
1155 vlen = strcspn(value, delims);
1156 for (i = 0; fields[i].field != NULL; i++) {
1157 if (!strncasecmp(fields[i].field, key, klen)) {
1158 fields[i].value = apr_pstrndup(pool, value, vlen);
1159 break;
1160 }
1161 }
1162 ptr = value+vlen;
1163 }
1164 if (fields[4].value != NULL) {
1165 port = atoi(fields[4].value);
1166 }
1167 if (fields[6].value != NULL &&
1168 !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) {
1169 flags |= CLIENT_FOUND_ROWS; /* only option we know */
1170 }
1171 if (fields[7].value != NULL) {
1172 sql->fldsz = atol(fields[7].value);
1173 }
1174 if (fields[8].value != NULL) {
1175 mysql_options(sql->conn, MYSQL_READ_DEFAULT_GROUP, fields[8].value);
1176 }
1177#if MYSQL_VERSION_ID >= 50013
1178 if (fields[9].value != NULL) {
1179 do_reconnect = atoi(fields[9].value) ? 1 : 0;
1180 }
1181#endif
1182
1183#if MYSQL_VERSION_ID >= 50013
1184 /* the MySQL manual says this should be BEFORE mysql_real_connect */
1185 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
1186#endif
1187
1188 real_conn = mysql_real_connect(sql->conn, fields[0].value,
1189 fields[1].value, fields[2].value,
1190 fields[3].value, port,
1191 fields[5].value, flags);
1192
1193 if(real_conn == NULL) {
1194 if (error) {
1195 *error = apr_pstrdup(pool, mysql_error(sql->conn));
1196 }
1197 mysql_close(sql->conn);
1198 return NULL;
1199 }
1200
1201#if MYSQL_VERSION_ID >= 50013
1202 /* Some say this should be AFTER mysql_real_connect */
1203 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
1204#endif
1205
1206 return sql;
1207}
1208
1209static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
1210{
1211 mysql_close(handle->conn);
1212 return APR_SUCCESS;
1213}
1214
1215static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
1216 apr_dbd_t *handle)
1217{
1218 return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
1219}
1220
1221static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
1222 const char* name)
1223{
1224 return mysql_select_db(handle->conn, name);
1225}
1226
1227static void *dbd_mysql_native(apr_dbd_t *handle)
1228{
1229 return handle->conn;
1230}
1231
1232static int dbd_mysql_num_cols(apr_dbd_results_t *res)
1233{
1234 if (res->statement) {
1235 return mysql_stmt_field_count(res->statement);
1236 }
1237 else {
1238 return mysql_num_fields(res->res);
1239 }
1240}
1241
1242static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
1243{
1244 if (res->random) {
1245 if (res->statement) {
1246 return (int) mysql_stmt_num_rows(res->statement);
1247 }
1248 else {
1249 return (int) mysql_num_rows(res->res);
1250 }
1251 }
1252 else {
1253 return -1;
1254 }
1255}
1256
1257static apr_status_t thread_end(void *data)
1258{
1259 mysql_thread_end();
1260 return APR_SUCCESS;
1261}
1262
1263static void dbd_mysql_init(apr_pool_t *pool)
1264{
1265 my_init();
1266 mysql_thread_init();
1267
1268 /* FIXME: this is a guess; find out what it really does */
1269 apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null);
1270}
1271APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = {
1272 "mysql",
1273 dbd_mysql_init,
1274 dbd_mysql_native,
1275 dbd_mysql_open,
1276 dbd_mysql_check_conn,
1277 dbd_mysql_close,
1278 dbd_mysql_select_db,
1279 dbd_mysql_transaction,
1280 dbd_mysql_end_transaction,
1281 dbd_mysql_query,
1282 dbd_mysql_select,
1283 dbd_mysql_num_cols,
1284 dbd_mysql_num_tuples,
1285 dbd_mysql_get_row,
1286 dbd_mysql_get_entry,
1287 dbd_mysql_error,
1288 dbd_mysql_escape,
1289 dbd_mysql_prepare,
1290 dbd_mysql_pvquery,
1291 dbd_mysql_pvselect,
1292 dbd_mysql_pquery,
1293 dbd_mysql_pselect,
1294 dbd_mysql_get_name,
1295 dbd_mysql_transaction_mode_get,
1296 dbd_mysql_transaction_mode_set,
1297 "?",
1298 dbd_mysql_pvbquery,
1299 dbd_mysql_pvbselect,
1300 dbd_mysql_pbquery,
1301 dbd_mysql_pbselect,
1302 dbd_mysql_datum_get
1303};
1304
1305#endif
1056 return ret;
1057}
1058/* Whether or not transactions work depends on whether the
1059 * underlying DB supports them within MySQL. Unfortunately
1060 * it fails silently with the default InnoDB.
1061 */
1062
1063static int dbd_mysql_transaction(apr_pool_t *pool, apr_dbd_t *handle,
1064 apr_dbd_transaction_t **trans)
1065{
1066 /* Don't try recursive transactions here */
1067 if (handle->trans) {
1068 dbd_mysql_end_transaction(handle->trans) ;
1069 }
1070 if (!*trans) {
1071 *trans = apr_pcalloc(pool, sizeof(apr_dbd_transaction_t));
1072 }
1073 (*trans)->errnum = mysql_autocommit(handle->conn, 0);
1074 (*trans)->handle = handle;
1075 handle->trans = *trans;
1076 return (*trans)->errnum;
1077}
1078
1079static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t *trans)
1080{
1081 if (!trans)
1082 return APR_DBD_TRANSACTION_COMMIT;
1083
1084 return trans->mode;
1085}
1086
1087static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t *trans,
1088 int mode)
1089{
1090 if (!trans)
1091 return APR_DBD_TRANSACTION_COMMIT;
1092
1093 return trans->mode = (mode & TXN_MODE_BITS);
1094}
1095
1096static apr_dbd_t *dbd_mysql_open(apr_pool_t *pool, const char *params,
1097 const char **error)
1098{
1099 static const char *const delims = " \r\n\t;|,";
1100 const char *ptr;
1101 int i;
1102 const char *key;
1103 size_t klen;
1104 const char *value;
1105 size_t vlen;
1106#if MYSQL_VERSION_ID >= 50013
1107 my_bool do_reconnect = 1;
1108#endif
1109 MYSQL *real_conn;
1110 unsigned long flags = 0;
1111
1112 struct {
1113 const char *field;
1114 const char *value;
1115 } fields[] = {
1116 {"host", NULL},
1117 {"user", NULL},
1118 {"pass", NULL},
1119 {"dbname", NULL},
1120 {"port", NULL},
1121 {"sock", NULL},
1122 {"flags", NULL},
1123 {"fldsz", NULL},
1124 {"group", NULL},
1125 {"reconnect", NULL},
1126 {NULL, NULL}
1127 };
1128 unsigned int port = 0;
1129 apr_dbd_t *sql = apr_pcalloc(pool, sizeof(apr_dbd_t));
1130 sql->fldsz = FIELDSIZE;
1131 sql->conn = mysql_init(sql->conn);
1132 if ( sql->conn == NULL ) {
1133 return NULL;
1134 }
1135 for (ptr = strchr(params, '='); ptr; ptr = strchr(ptr, '=')) {
1136 /* don't dereference memory that may not belong to us */
1137 if (ptr == params) {
1138 ++ptr;
1139 continue;
1140 }
1141 for (key = ptr-1; apr_isspace(*key); --key);
1142 klen = 0;
1143 while (apr_isalpha(*key)) {
1144 /* don't parse backwards off the start of the string */
1145 if (key == params) {
1146 --key;
1147 ++klen;
1148 break;
1149 }
1150 --key;
1151 ++klen;
1152 }
1153 ++key;
1154 for (value = ptr+1; apr_isspace(*value); ++value);
1155 vlen = strcspn(value, delims);
1156 for (i = 0; fields[i].field != NULL; i++) {
1157 if (!strncasecmp(fields[i].field, key, klen)) {
1158 fields[i].value = apr_pstrndup(pool, value, vlen);
1159 break;
1160 }
1161 }
1162 ptr = value+vlen;
1163 }
1164 if (fields[4].value != NULL) {
1165 port = atoi(fields[4].value);
1166 }
1167 if (fields[6].value != NULL &&
1168 !strcmp(fields[6].value, "CLIENT_FOUND_ROWS")) {
1169 flags |= CLIENT_FOUND_ROWS; /* only option we know */
1170 }
1171 if (fields[7].value != NULL) {
1172 sql->fldsz = atol(fields[7].value);
1173 }
1174 if (fields[8].value != NULL) {
1175 mysql_options(sql->conn, MYSQL_READ_DEFAULT_GROUP, fields[8].value);
1176 }
1177#if MYSQL_VERSION_ID >= 50013
1178 if (fields[9].value != NULL) {
1179 do_reconnect = atoi(fields[9].value) ? 1 : 0;
1180 }
1181#endif
1182
1183#if MYSQL_VERSION_ID >= 50013
1184 /* the MySQL manual says this should be BEFORE mysql_real_connect */
1185 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
1186#endif
1187
1188 real_conn = mysql_real_connect(sql->conn, fields[0].value,
1189 fields[1].value, fields[2].value,
1190 fields[3].value, port,
1191 fields[5].value, flags);
1192
1193 if(real_conn == NULL) {
1194 if (error) {
1195 *error = apr_pstrdup(pool, mysql_error(sql->conn));
1196 }
1197 mysql_close(sql->conn);
1198 return NULL;
1199 }
1200
1201#if MYSQL_VERSION_ID >= 50013
1202 /* Some say this should be AFTER mysql_real_connect */
1203 mysql_options(sql->conn, MYSQL_OPT_RECONNECT, &do_reconnect);
1204#endif
1205
1206 return sql;
1207}
1208
1209static apr_status_t dbd_mysql_close(apr_dbd_t *handle)
1210{
1211 mysql_close(handle->conn);
1212 return APR_SUCCESS;
1213}
1214
1215static apr_status_t dbd_mysql_check_conn(apr_pool_t *pool,
1216 apr_dbd_t *handle)
1217{
1218 return mysql_ping(handle->conn) ? APR_EGENERAL : APR_SUCCESS;
1219}
1220
1221static int dbd_mysql_select_db(apr_pool_t *pool, apr_dbd_t* handle,
1222 const char* name)
1223{
1224 return mysql_select_db(handle->conn, name);
1225}
1226
1227static void *dbd_mysql_native(apr_dbd_t *handle)
1228{
1229 return handle->conn;
1230}
1231
1232static int dbd_mysql_num_cols(apr_dbd_results_t *res)
1233{
1234 if (res->statement) {
1235 return mysql_stmt_field_count(res->statement);
1236 }
1237 else {
1238 return mysql_num_fields(res->res);
1239 }
1240}
1241
1242static int dbd_mysql_num_tuples(apr_dbd_results_t *res)
1243{
1244 if (res->random) {
1245 if (res->statement) {
1246 return (int) mysql_stmt_num_rows(res->statement);
1247 }
1248 else {
1249 return (int) mysql_num_rows(res->res);
1250 }
1251 }
1252 else {
1253 return -1;
1254 }
1255}
1256
1257static apr_status_t thread_end(void *data)
1258{
1259 mysql_thread_end();
1260 return APR_SUCCESS;
1261}
1262
1263static void dbd_mysql_init(apr_pool_t *pool)
1264{
1265 my_init();
1266 mysql_thread_init();
1267
1268 /* FIXME: this is a guess; find out what it really does */
1269 apr_pool_cleanup_register(pool, NULL, thread_end, apr_pool_cleanup_null);
1270}
1271APU_MODULE_DECLARE_DATA const apr_dbd_driver_t apr_dbd_mysql_driver = {
1272 "mysql",
1273 dbd_mysql_init,
1274 dbd_mysql_native,
1275 dbd_mysql_open,
1276 dbd_mysql_check_conn,
1277 dbd_mysql_close,
1278 dbd_mysql_select_db,
1279 dbd_mysql_transaction,
1280 dbd_mysql_end_transaction,
1281 dbd_mysql_query,
1282 dbd_mysql_select,
1283 dbd_mysql_num_cols,
1284 dbd_mysql_num_tuples,
1285 dbd_mysql_get_row,
1286 dbd_mysql_get_entry,
1287 dbd_mysql_error,
1288 dbd_mysql_escape,
1289 dbd_mysql_prepare,
1290 dbd_mysql_pvquery,
1291 dbd_mysql_pvselect,
1292 dbd_mysql_pquery,
1293 dbd_mysql_pselect,
1294 dbd_mysql_get_name,
1295 dbd_mysql_transaction_mode_get,
1296 dbd_mysql_transaction_mode_set,
1297 "?",
1298 dbd_mysql_pvbquery,
1299 dbd_mysql_pvbselect,
1300 dbd_mysql_pbquery,
1301 dbd_mysql_pbselect,
1302 dbd_mysql_datum_get
1303};
1304
1305#endif