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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #define HAVE_MYSQL_MYSQL_H
22 #include "apu_version.h"
23 #include "apu_config.h"
31 #elif defined(HAVE_MYSQL_MYSQL_H)
32 #include <mysql/mysql.h>
33 #include <mysql/errmsg.h>
36 #include "apr_strings.h"
37 #include "apr_buckets.h"
39 #include "apr_dbd_internal.h"
41 /* default maximum field size 1 MB */
42 #define FIELDSIZE 1048575
44 struct apr_dbd_prepared_t
{
48 apr_dbd_type_e
*types
;
51 struct apr_dbd_transaction_t
{
59 apr_dbd_transaction_t
* trans
;
63 struct apr_dbd_results_t
{
66 MYSQL_STMT
*statement
;
70 struct apr_dbd_row_t
{
72 apr_dbd_results_t
*res
;
76 /* MySQL specific bucket for BLOB types */
77 typedef struct apr_bucket_lob apr_bucket_lob
;
79 * A bucket referring to a MySQL BLOB
81 struct apr_bucket_lob
{
82 /** Number of buckets using this memory */
83 apr_bucket_refcount refcount
;
84 /** The row this bucket refers to */
85 const apr_dbd_row_t
*row
;
86 /** The column this bucket refers to */
88 /** The pool into which any needed structures should
89 * be created while reading from this bucket */
93 static void lob_bucket_destroy(void *data
);
94 static apr_status_t
lob_bucket_read(apr_bucket
*e
, const char **str
,
95 apr_size_t
*len
, apr_read_type_e block
);
96 static apr_bucket
*apr_bucket_lob_make(apr_bucket
*b
,
97 const apr_dbd_row_t
*row
, int col
,
98 apr_off_t offset
, apr_size_t len
,
100 static apr_bucket
*apr_bucket_lob_create(const apr_dbd_row_t
*row
, int col
,
102 apr_size_t len
, apr_pool_t
*p
,
103 apr_bucket_alloc_t
*list
);
105 static const apr_bucket_type_t apr_bucket_type_lob
= {
106 "LOB", 5, APR_BUCKET_DATA
,
109 apr_bucket_setaside_notimpl
,
110 apr_bucket_shared_split
,
111 apr_bucket_shared_copy
114 static void lob_bucket_destroy(void *data
)
116 apr_bucket_lob
*f
= data
;
118 if (apr_bucket_shared_destroy(f
)) {
119 /* no need to destroy database objects here; it will get
120 * done automatically when the pool gets cleaned up */
125 static apr_status_t
lob_bucket_read(apr_bucket
*e
, const char **str
,
126 apr_size_t
*len
, apr_read_type_e block
)
128 apr_bucket_lob
*a
= e
->data
;
129 const apr_dbd_row_t
*row
= a
->row
;
130 apr_dbd_results_t
*res
= row
->res
;
132 apr_bucket
*b
= NULL
;
134 apr_size_t blength
= e
->length
; /* bytes remaining in file past offset */
135 apr_off_t boffset
= e
->start
;
136 MYSQL_BIND
*bind
= &res
->bind
[col
];
138 *str
= NULL
; /* in case we die prematurely */
140 /* fetch from offset if not at the beginning */
142 rv
= mysql_stmt_fetch_column(res
->statement
, bind
, col
, boffset
);
147 blength
-= blength
> bind
->buffer_length
? bind
->buffer_length
: blength
;
148 *len
= e
->length
- blength
;
151 /* allocate new buffer, since we used this one for the bucket */
152 bind
->buffer
= apr_palloc(res
->pool
, bind
->buffer_length
);
155 * Change the current bucket to refer to what we read,
156 * even if we read nothing because we hit EOF.
158 apr_bucket_pool_make(e
, *str
, *len
, res
->pool
);
160 /* If we have more to read from the field, then create another bucket */
162 /* for efficiency, we can just build a new apr_bucket struct
163 * to wrap around the existing LOB bucket */
164 b
= apr_bucket_alloc(sizeof(*b
), e
->list
);
165 b
->start
= boffset
+ *len
;
168 b
->type
= &apr_bucket_type_lob
;
169 b
->free
= apr_bucket_free
;
171 APR_BUCKET_INSERT_AFTER(e
, b
);
174 lob_bucket_destroy(a
);
180 static apr_bucket
*apr_bucket_lob_make(apr_bucket
*b
,
181 const apr_dbd_row_t
*row
, int col
,
182 apr_off_t offset
, apr_size_t len
,
187 f
= apr_bucket_alloc(sizeof(*f
), b
->list
);
192 b
= apr_bucket_shared_make(b
, f
, offset
, len
);
193 b
->type
= &apr_bucket_type_lob
;
198 static apr_bucket
*apr_bucket_lob_create(const apr_dbd_row_t
*row
, int col
,
200 apr_size_t len
, apr_pool_t
*p
,
201 apr_bucket_alloc_t
*list
)
203 apr_bucket
*b
= apr_bucket_alloc(sizeof(*b
), list
);
206 b
->free
= apr_bucket_free
;
208 return apr_bucket_lob_make(b
, row
, col
, offset
, len
, p
);
211 static apr_status_t
free_result(void *data
)
213 mysql_free_result(data
);
217 static int dbd_mysql_select(apr_pool_t
*pool
, apr_dbd_t
*sql
,
218 apr_dbd_results_t
**results
,
219 const char *query
, int seek
)
223 if (sql
->trans
&& sql
->trans
->errnum
) {
224 return sql
->trans
->errnum
;
226 ret
= mysql_query(sql
->conn
, query
);
228 if (sz
= mysql_field_count(sql
->conn
), sz
> 0) {
230 *results
= apr_palloc(pool
, sizeof(apr_dbd_results_t
));
232 (*results
)->random
= seek
;
233 (*results
)->statement
= NULL
;
234 (*results
)->pool
= pool
;
236 (*results
)->res
= mysql_store_result(sql
->conn
);
239 (*results
)->res
= mysql_use_result(sql
->conn
);
241 apr_pool_cleanup_register(pool
, (*results
)->res
,
242 free_result
,apr_pool_cleanup_null
);
245 ret
= mysql_errno(sql
->conn
);
248 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
249 sql
->trans
->errnum
= ret
;
254 static const char *dbd_mysql_get_name(const apr_dbd_results_t
*res
, int n
)
256 if ((n
< 0) || (n
>= mysql_num_fields(res
->res
))) {
260 return mysql_fetch_fields(res
->res
)[n
].name
;
263 static int dbd_mysql_get_row(apr_pool_t
*pool
, apr_dbd_results_t
*res
,
264 apr_dbd_row_t
**row
, int rownum
)
269 if (res
->statement
) {
272 mysql_stmt_data_seek(res
->statement
, (my_ulonglong
)rownum
);
275 ret
= mysql_stmt_fetch(res
->statement
);
278 ret
= mysql_stmt_errno(res
->statement
);
284 ret
= 0; /* bad luck - get_entry will deal with this */
291 mysql_data_seek(res
->res
, (my_ulonglong
) rownum
);
294 r
= mysql_fetch_row(res
->res
);
301 *row
= apr_palloc(pool
, sizeof(apr_dbd_row_t
));
305 (*row
)->len
= mysql_fetch_lengths(res
->res
);
308 apr_pool_cleanup_run(pool
, res
->res
, free_result
);
313 /* An improved API that was proposed but not followed up */
314 static int dbd_mysql_get_entry(const apr_dbd_row_t
*row
, int n
,
315 apr_dbd_datum_t
*val
)
318 if (row
->res
->statement
) {
319 bind
= &row
->res
->bind
[n
];
320 if (mysql_stmt_fetch_column(row
->res
->statement
, bind
, n
, 0) != 0) {
321 val
->type
= APR_DBD_VALUE_NULL
;
324 if (*bind
->is_null
) {
325 val
->type
= APR_DBD_VALUE_NULL
;
329 val
->type
= APR_DBD_VALUE_STRING
;
330 val
->value
.stringval
= bind
->buffer
;
334 val
->type
= APR_DBD_VALUE_STRING
;
335 val
->value
.stringval
= row
->row
[n
];
341 static const char *dbd_mysql_get_entry(const apr_dbd_row_t
*row
, int n
)
344 if (row
->res
->statement
) {
345 bind
= &row
->res
->bind
[n
];
346 if (mysql_stmt_fetch_column(row
->res
->statement
, bind
, n
, 0) != 0) {
349 if (*bind
->is_null
) {
363 static apr_status_t
dbd_mysql_datum_get(const apr_dbd_row_t
*row
, int n
,
364 apr_dbd_type_e type
, void *data
)
366 if (row
->res
->statement
) {
367 MYSQL_BIND
*bind
= &row
->res
->bind
[n
];
368 unsigned long len
= *bind
->length
;
370 if (mysql_stmt_fetch_column(row
->res
->statement
, bind
, n
, 0) != 0) {
374 if (*bind
->is_null
) {
379 case APR_DBD_TYPE_TINY
:
380 *(char*)data
= atoi(bind
->buffer
);
382 case APR_DBD_TYPE_UTINY
:
383 *(unsigned char*)data
= atoi(bind
->buffer
);
385 case APR_DBD_TYPE_SHORT
:
386 *(short*)data
= atoi(bind
->buffer
);
388 case APR_DBD_TYPE_USHORT
:
389 *(unsigned short*)data
= atoi(bind
->buffer
);
391 case APR_DBD_TYPE_INT
:
392 *(int*)data
= atoi(bind
->buffer
);
394 case APR_DBD_TYPE_UINT
:
395 *(unsigned int*)data
= atoi(bind
->buffer
);
397 case APR_DBD_TYPE_LONG
:
398 *(long*)data
= atol(bind
->buffer
);
400 case APR_DBD_TYPE_ULONG
:
401 *(unsigned long*)data
= atol(bind
->buffer
);
403 case APR_DBD_TYPE_LONGLONG
:
404 *(apr_int64_t
*)data
= apr_atoi64(bind
->buffer
);
406 case APR_DBD_TYPE_ULONGLONG
:
407 *(apr_uint64_t
*)data
= apr_atoi64(bind
->buffer
);
409 case APR_DBD_TYPE_FLOAT
:
410 *(float*)data
= atof(bind
->buffer
);
412 case APR_DBD_TYPE_DOUBLE
:
413 *(double*)data
= atof(bind
->buffer
);
415 case APR_DBD_TYPE_STRING
:
416 case APR_DBD_TYPE_TEXT
:
417 case APR_DBD_TYPE_TIME
:
418 case APR_DBD_TYPE_DATE
:
419 case APR_DBD_TYPE_DATETIME
:
420 case APR_DBD_TYPE_TIMESTAMP
:
421 case APR_DBD_TYPE_ZTIMESTAMP
:
422 *((char*)bind
->buffer
+bind
->buffer_length
-1) = '\0';
423 *(char**)data
= bind
->buffer
;
425 case APR_DBD_TYPE_BLOB
:
426 case APR_DBD_TYPE_CLOB
:
429 apr_bucket_brigade
*b
= (apr_bucket_brigade
*)data
;
431 e
= apr_bucket_lob_create(row
, n
, 0, len
,
432 row
->res
->pool
, b
->bucket_alloc
);
433 APR_BRIGADE_INSERT_TAIL(b
, e
);
436 case APR_DBD_TYPE_NULL
:
437 *(void**)data
= NULL
;
444 if (row
->row
[n
] == NULL
) {
449 case APR_DBD_TYPE_TINY
:
450 *(char*)data
= atoi(row
->row
[n
]);
452 case APR_DBD_TYPE_UTINY
:
453 *(unsigned char*)data
= atoi(row
->row
[n
]);
455 case APR_DBD_TYPE_SHORT
:
456 *(short*)data
= atoi(row
->row
[n
]);
458 case APR_DBD_TYPE_USHORT
:
459 *(unsigned short*)data
= atoi(row
->row
[n
]);
461 case APR_DBD_TYPE_INT
:
462 *(int*)data
= atoi(row
->row
[n
]);
464 case APR_DBD_TYPE_UINT
:
465 *(unsigned int*)data
= atoi(row
->row
[n
]);
467 case APR_DBD_TYPE_LONG
:
468 *(long*)data
= atol(row
->row
[n
]);
470 case APR_DBD_TYPE_ULONG
:
471 *(unsigned long*)data
= atol(row
->row
[n
]);
473 case APR_DBD_TYPE_LONGLONG
:
474 *(apr_int64_t
*)data
= apr_atoi64(row
->row
[n
]);
476 case APR_DBD_TYPE_ULONGLONG
:
477 *(apr_uint64_t
*)data
= apr_atoi64(row
->row
[n
]);
479 case APR_DBD_TYPE_FLOAT
:
480 *(float*)data
= atof(row
->row
[n
]);
482 case APR_DBD_TYPE_DOUBLE
:
483 *(double*)data
= atof(row
->row
[n
]);
485 case APR_DBD_TYPE_STRING
:
486 case APR_DBD_TYPE_TEXT
:
487 case APR_DBD_TYPE_TIME
:
488 case APR_DBD_TYPE_DATE
:
489 case APR_DBD_TYPE_DATETIME
:
490 case APR_DBD_TYPE_TIMESTAMP
:
491 case APR_DBD_TYPE_ZTIMESTAMP
:
492 *(char**)data
= row
->row
[n
];
494 case APR_DBD_TYPE_BLOB
:
495 case APR_DBD_TYPE_CLOB
:
498 apr_bucket_brigade
*b
= (apr_bucket_brigade
*)data
;
500 e
= apr_bucket_pool_create(row
->row
[n
], row
->len
[n
],
501 row
->res
->pool
, b
->bucket_alloc
);
502 APR_BRIGADE_INSERT_TAIL(b
, e
);
505 case APR_DBD_TYPE_NULL
:
506 *(void**)data
= NULL
;
515 static const char *dbd_mysql_error(apr_dbd_t
*sql
, int n
)
517 return mysql_error(sql
->conn
);
520 static int dbd_mysql_query(apr_dbd_t
*sql
, int *nrows
, const char *query
)
523 if (sql
->trans
&& sql
->trans
->errnum
) {
524 return sql
->trans
->errnum
;
526 ret
= mysql_query(sql
->conn
, query
);
528 ret
= mysql_errno(sql
->conn
);
530 *nrows
= mysql_affected_rows(sql
->conn
);
531 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
532 sql
->trans
->errnum
= ret
;
537 static const char *dbd_mysql_escape(apr_pool_t
*pool
, const char *arg
,
540 unsigned long len
= strlen(arg
);
541 char *ret
= apr_palloc(pool
, 2*len
+ 1);
542 mysql_real_escape_string(sql
->conn
, ret
, arg
, len
);
546 static apr_status_t
stmt_close(void *data
)
548 mysql_stmt_close(data
);
552 static int dbd_mysql_prepare(apr_pool_t
*pool
, apr_dbd_t
*sql
,
553 const char *query
, const char *label
,
554 int nargs
, int nvals
, apr_dbd_type_e
*types
,
555 apr_dbd_prepared_t
**statement
)
557 /* Translate from apr_dbd to native query format */
561 *statement
= apr_palloc(pool
, sizeof(apr_dbd_prepared_t
));
563 (*statement
)->stmt
= mysql_stmt_init(sql
->conn
);
565 if ((*statement
)->stmt
) {
566 apr_pool_cleanup_register(pool
, (*statement
)->stmt
,
567 stmt_close
, apr_pool_cleanup_null
);
568 ret
= mysql_stmt_prepare((*statement
)->stmt
, query
, strlen(query
));
571 ret
= mysql_stmt_errno((*statement
)->stmt
);
574 (*statement
)->nargs
= nargs
;
575 (*statement
)->nvals
= nvals
;
576 (*statement
)->types
= types
;
581 return CR_OUT_OF_MEMORY
;
584 static void dbd_mysql_bind(apr_dbd_prepared_t
*statement
,
585 const char **values
, MYSQL_BIND
*bind
)
589 for (i
= 0, j
= 0; i
< statement
->nargs
; i
++, j
++) {
590 bind
[i
].length
= &bind
[i
].buffer_length
;
591 bind
[i
].is_unsigned
= 0;
592 bind
[i
].is_null
= NULL
;
594 if (values
[j
] == NULL
) {
595 bind
[i
].buffer_type
= MYSQL_TYPE_NULL
;
598 switch (statement
->types
[i
]) {
599 case APR_DBD_TYPE_BLOB
:
600 case APR_DBD_TYPE_CLOB
:
601 bind
[i
].buffer_type
= MYSQL_TYPE_LONG_BLOB
;
602 bind
[i
].buffer
= (void*)values
[j
];
603 bind
[i
].buffer_length
= atol(values
[++j
]);
605 /* skip table and column */
609 bind
[i
].buffer_type
= MYSQL_TYPE_VAR_STRING
;
610 bind
[i
].buffer
= (void*)values
[j
];
611 bind
[i
].buffer_length
= strlen(values
[j
]);
620 static int dbd_mysql_pquery_internal(apr_pool_t
*pool
, apr_dbd_t
*sql
,
621 int *nrows
, apr_dbd_prepared_t
*statement
,
626 ret
= mysql_stmt_bind_param(statement
->stmt
, bind
);
629 ret
= mysql_stmt_errno(statement
->stmt
);
632 ret
= mysql_stmt_execute(statement
->stmt
);
634 ret
= mysql_stmt_errno(statement
->stmt
);
636 *nrows
= mysql_stmt_affected_rows(statement
->stmt
);
642 static int dbd_mysql_pquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
643 int *nrows
, apr_dbd_prepared_t
*statement
,
649 if (sql
->trans
&& sql
->trans
->errnum
) {
650 return sql
->trans
->errnum
;
653 bind
= apr_palloc(pool
, statement
->nargs
* sizeof(MYSQL_BIND
));
655 dbd_mysql_bind(statement
, values
, bind
);
657 ret
= dbd_mysql_pquery_internal(pool
, sql
, nrows
, statement
, bind
);
659 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
660 sql
->trans
->errnum
= ret
;
665 static int dbd_mysql_pvquery(apr_pool_t
*pool
, apr_dbd_t
*sql
, int *nrows
,
666 apr_dbd_prepared_t
*statement
, va_list args
)
671 if (sql
->trans
&& sql
->trans
->errnum
) {
672 return sql
->trans
->errnum
;
675 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nvals
);
677 for (i
= 0; i
< statement
->nvals
; i
++) {
678 values
[i
] = va_arg(args
, const char*);
681 return dbd_mysql_pquery(pool
, sql
, nrows
, statement
, values
);
684 static int dbd_mysql_pselect_internal(apr_pool_t
*pool
, apr_dbd_t
*sql
,
685 apr_dbd_results_t
**res
,
686 apr_dbd_prepared_t
*statement
,
687 int random
, MYSQL_BIND
*bind
)
691 #if MYSQL_VERSION_ID >= 50000
695 unsigned long *length
, maxlen
;
697 ret
= mysql_stmt_bind_param(statement
->stmt
, bind
);
699 ret
= mysql_stmt_execute(statement
->stmt
);
702 *res
= apr_pcalloc(pool
, sizeof(apr_dbd_results_t
));
704 (*res
)->random
= random
;
705 (*res
)->statement
= statement
->stmt
;
706 (*res
)->res
= mysql_stmt_result_metadata(statement
->stmt
);
708 apr_pool_cleanup_register(pool
, (*res
)->res
,
709 free_result
, apr_pool_cleanup_null
);
710 nfields
= mysql_num_fields((*res
)->res
);
712 (*res
)->bind
= apr_palloc(pool
, nfields
*sizeof(MYSQL_BIND
));
713 length
= apr_pcalloc(pool
, nfields
*sizeof(unsigned long));
714 #if MYSQL_VERSION_ID >= 50000
715 error
= apr_palloc(pool
, nfields
*sizeof(my_bool
));
717 is_nullr
= apr_pcalloc(pool
, nfields
*sizeof(my_bool
));
718 for ( i
= 0; i
< nfields
; ++i
) {
719 maxlen
= ((*res
)->res
->fields
[i
].length
< sql
->fldsz
?
720 (*res
)->res
->fields
[i
].length
: sql
->fldsz
) + 1;
721 if ((*res
)->res
->fields
[i
].type
== MYSQL_TYPE_BLOB
) {
722 (*res
)->bind
[i
].buffer_type
= MYSQL_TYPE_LONG_BLOB
;
725 (*res
)->bind
[i
].buffer_type
= MYSQL_TYPE_VAR_STRING
;
727 (*res
)->bind
[i
].buffer_length
= maxlen
;
728 (*res
)->bind
[i
].length
= &length
[i
];
729 (*res
)->bind
[i
].buffer
= apr_palloc(pool
, maxlen
);
730 (*res
)->bind
[i
].is_null
= is_nullr
+i
;
731 #if MYSQL_VERSION_ID >= 50000
732 (*res
)->bind
[i
].error
= error
+i
;
736 ret
= mysql_stmt_bind_result(statement
->stmt
, (*res
)->bind
);
738 ret
= mysql_stmt_store_result(statement
->stmt
);
743 ret
= mysql_stmt_errno(statement
->stmt
);
749 static int dbd_mysql_pselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
750 apr_dbd_results_t
**res
,
751 apr_dbd_prepared_t
*statement
, int random
,
757 if (sql
->trans
&& sql
->trans
->errnum
) {
758 return sql
->trans
->errnum
;
761 bind
= apr_palloc(pool
, statement
->nargs
* sizeof(MYSQL_BIND
));
763 dbd_mysql_bind(statement
, args
, bind
);
765 ret
= dbd_mysql_pselect_internal(pool
, sql
, res
, statement
, random
, bind
);
767 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
768 sql
->trans
->errnum
= ret
;
773 static int dbd_mysql_pvselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
774 apr_dbd_results_t
**res
,
775 apr_dbd_prepared_t
*statement
, int random
,
781 if (sql
->trans
&& sql
->trans
->errnum
) {
782 return sql
->trans
->errnum
;
785 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nvals
);
787 for (i
= 0; i
< statement
->nvals
; i
++) {
788 values
[i
] = va_arg(args
, const char*);
791 return dbd_mysql_pselect(pool
, sql
, res
, statement
, random
, values
);
794 static void dbd_mysql_bbind(apr_pool_t
*pool
, apr_dbd_prepared_t
*statement
,
795 const void **values
, MYSQL_BIND
*bind
)
801 for (i
= 0, j
= 0; i
< statement
->nargs
; i
++, j
++) {
802 arg
= (void *)values
[j
];
804 bind
[i
].length
= &bind
[i
].buffer_length
;
805 bind
[i
].is_null
= NULL
;
807 type
= (arg
== NULL
? APR_DBD_TYPE_NULL
: statement
->types
[i
]);
809 case APR_DBD_TYPE_TINY
:
810 bind
[i
].buffer
= arg
;
811 bind
[i
].buffer_type
= MYSQL_TYPE_TINY
;
812 bind
[i
].is_unsigned
= 0;
814 case APR_DBD_TYPE_UTINY
:
815 bind
[i
].buffer
= arg
;
816 bind
[i
].buffer_type
= MYSQL_TYPE_TINY
;
817 bind
[i
].is_unsigned
= 1;
819 case APR_DBD_TYPE_SHORT
:
820 bind
[i
].buffer
= arg
;
821 bind
[i
].buffer_type
= MYSQL_TYPE_SHORT
;
822 bind
[i
].is_unsigned
= 0;
824 case APR_DBD_TYPE_USHORT
:
825 bind
[i
].buffer
= arg
;
826 bind
[i
].buffer_type
= MYSQL_TYPE_SHORT
;
827 bind
[i
].is_unsigned
= 1;
829 case APR_DBD_TYPE_INT
:
830 bind
[i
].buffer
= arg
;
831 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
832 bind
[i
].is_unsigned
= 0;
834 case APR_DBD_TYPE_UINT
:
835 bind
[i
].buffer
= arg
;
836 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
837 bind
[i
].is_unsigned
= 1;
839 case APR_DBD_TYPE_LONG
:
840 if (sizeof(int) == sizeof(long)) {
841 bind
[i
].buffer
= arg
;
844 bind
[i
].buffer
= apr_palloc(pool
, sizeof(int));
845 *(int*)bind
[i
].buffer
= *(long*)arg
;
847 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
848 bind
[i
].is_unsigned
= 0;
850 case APR_DBD_TYPE_ULONG
:
851 if (sizeof(unsigned int) == sizeof(unsigned long)) {
852 bind
[i
].buffer
= arg
;
855 bind
[i
].buffer
= apr_palloc(pool
, sizeof(unsigned int));
856 *(unsigned int*)bind
[i
].buffer
= *(unsigned long*)arg
;
858 bind
[i
].buffer_type
= MYSQL_TYPE_LONG
;
859 bind
[i
].is_unsigned
= 1;
861 case APR_DBD_TYPE_LONGLONG
:
862 if (sizeof(long long) == sizeof(apr_int64_t
)) {
863 bind
[i
].buffer
= arg
;
866 bind
[i
].buffer
= apr_palloc(pool
, sizeof(long long));
867 *(long long*)bind
[i
].buffer
= *(apr_int64_t
*)arg
;
869 bind
[i
].buffer_type
= MYSQL_TYPE_LONGLONG
;
870 bind
[i
].is_unsigned
= 0;
872 case APR_DBD_TYPE_ULONGLONG
:
873 if (sizeof(unsigned long long) == sizeof(apr_uint64_t
)) {
874 bind
[i
].buffer
= arg
;
877 bind
[i
].buffer
= apr_palloc(pool
, sizeof(unsigned long long));
878 *(unsigned long long*)bind
[i
].buffer
= *(apr_uint64_t
*)arg
;
880 bind
[i
].buffer_type
= MYSQL_TYPE_LONGLONG
;
881 bind
[i
].is_unsigned
= 1;
883 case APR_DBD_TYPE_FLOAT
:
884 bind
[i
].buffer
= arg
;
885 bind
[i
].buffer_type
= MYSQL_TYPE_FLOAT
;
886 bind
[i
].is_unsigned
= 0;
888 case APR_DBD_TYPE_DOUBLE
:
889 bind
[i
].buffer
= arg
;
890 bind
[i
].buffer_type
= MYSQL_TYPE_DOUBLE
;
891 bind
[i
].is_unsigned
= 0;
893 case APR_DBD_TYPE_STRING
:
894 case APR_DBD_TYPE_TEXT
:
895 case APR_DBD_TYPE_TIME
:
896 case APR_DBD_TYPE_DATE
:
897 case APR_DBD_TYPE_DATETIME
:
898 case APR_DBD_TYPE_TIMESTAMP
:
899 case APR_DBD_TYPE_ZTIMESTAMP
:
900 bind
[i
].buffer
= arg
;
901 bind
[i
].buffer_type
= MYSQL_TYPE_VAR_STRING
;
902 bind
[i
].is_unsigned
= 0;
903 bind
[i
].buffer_length
= strlen((const char *)arg
);
905 case APR_DBD_TYPE_BLOB
:
906 case APR_DBD_TYPE_CLOB
:
907 bind
[i
].buffer
= (void *)arg
;
908 bind
[i
].buffer_type
= MYSQL_TYPE_LONG_BLOB
;
909 bind
[i
].is_unsigned
= 0;
910 bind
[i
].buffer_length
= *(apr_size_t
*)values
[++j
];
912 /* skip table and column */
915 case APR_DBD_TYPE_NULL
:
917 bind
[i
].buffer_type
= MYSQL_TYPE_NULL
;
925 static int dbd_mysql_pbquery(apr_pool_t
*pool
, apr_dbd_t
*sql
,
926 int *nrows
, apr_dbd_prepared_t
*statement
,
932 if (sql
->trans
&& sql
->trans
->errnum
) {
933 return sql
->trans
->errnum
;
936 bind
= apr_palloc(pool
, statement
->nargs
* sizeof(MYSQL_BIND
));
938 dbd_mysql_bbind(pool
, statement
, values
, bind
);
940 ret
= dbd_mysql_pquery_internal(pool
, sql
, nrows
, statement
, bind
);
942 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
943 sql
->trans
->errnum
= ret
;
948 static int dbd_mysql_pvbquery(apr_pool_t
*pool
, apr_dbd_t
*sql
, int *nrows
,
949 apr_dbd_prepared_t
*statement
, va_list args
)
954 if (sql
->trans
&& sql
->trans
->errnum
) {
955 return sql
->trans
->errnum
;
958 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nvals
);
960 for (i
= 0; i
< statement
->nvals
; i
++) {
961 values
[i
] = va_arg(args
, const void*);
964 return dbd_mysql_pbquery(pool
, sql
, nrows
, statement
, values
);
967 static int dbd_mysql_pbselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
968 apr_dbd_results_t
**res
,
969 apr_dbd_prepared_t
*statement
, int random
,
975 if (sql
->trans
&& sql
->trans
->errnum
) {
976 return sql
->trans
->errnum
;
979 bind
= apr_palloc(pool
, statement
->nargs
* sizeof(MYSQL_BIND
));
981 dbd_mysql_bbind(pool
, statement
, args
, bind
);
983 ret
= dbd_mysql_pselect_internal(pool
, sql
, res
, statement
, random
, bind
);
985 if (TXN_NOTICE_ERRORS(sql
->trans
)) {
986 sql
->trans
->errnum
= ret
;
991 static int dbd_mysql_pvbselect(apr_pool_t
*pool
, apr_dbd_t
*sql
,
992 apr_dbd_results_t
**res
,
993 apr_dbd_prepared_t
*statement
, int random
,
999 if (sql
->trans
&& sql
->trans
->errnum
) {
1000 return sql
->trans
->errnum
;
1003 values
= apr_palloc(pool
, sizeof(*values
) * statement
->nvals
);
1005 for (i
= 0; i
< statement
->nvals
; i
++) {
1006 values
[i
] = va_arg(args
, const void*);
1009 return dbd_mysql_pbselect(pool
, sql
, res
, statement
, random
, values
);
1012 static int dbd_mysql_end_transaction(apr_dbd_transaction_t
*trans
)
1016 /* rollback on error or explicit rollback request */
1017 if (trans
->errnum
|| TXN_DO_ROLLBACK(trans
)) {
1019 ret
= mysql_rollback(trans
->handle
->conn
);
1022 ret
= mysql_commit(trans
->handle
->conn
);
1025 ret
|= mysql_autocommit(trans
->handle
->conn
, 1);
1026 trans
->handle
->trans
= NULL
;
1029 /* Whether or not transactions work depends on whether the
1030 * underlying DB supports them within MySQL. Unfortunately
1031 * it fails silently with the default InnoDB.
1034 static int dbd_mysql_transaction(apr_pool_t
*pool
, apr_dbd_t
*handle
,
1035 apr_dbd_transaction_t
**trans
)
1037 /* Don't try recursive transactions here */
1038 if (handle
->trans
) {
1039 dbd_mysql_end_transaction(handle
->trans
) ;
1042 *trans
= apr_pcalloc(pool
, sizeof(apr_dbd_transaction_t
));
1044 (*trans
)->errnum
= mysql_autocommit(handle
->conn
, 0);
1045 (*trans
)->handle
= handle
;
1046 handle
->trans
= *trans
;
1047 return (*trans
)->errnum
;
1050 static int dbd_mysql_transaction_mode_get(apr_dbd_transaction_t
*trans
)
1053 return APR_DBD_TRANSACTION_COMMIT
;
1058 static int dbd_mysql_transaction_mode_set(apr_dbd_transaction_t
*trans
,
1062 return APR_DBD_TRANSACTION_COMMIT
;
1064 return trans
->mode
= (mode
& TXN_MODE_BITS
);
1067 static apr_dbd_t
*dbd_mysql_open(apr_pool_t
*pool
, const char *params
,
1070 static const char *const delims
= " \r\n\t;|,";
1077 #if MYSQL_VERSION_ID >= 50013
1078 my_bool do_reconnect
= 1;
1081 unsigned long flags
= 0;
1098 unsigned int port
= 0;
1099 apr_dbd_t
*sql
= apr_pcalloc(pool
, sizeof(apr_dbd_t
));
1100 sql
->fldsz
= FIELDSIZE
;
1101 sql
->conn
= mysql_init(sql
->conn
);
1102 if ( sql
->conn
== NULL
) {
1105 for (ptr
= strchr(params
, '='); ptr
; ptr
= strchr(ptr
, '=')) {
1106 /* don't dereference memory that may not belong to us */
1107 if (ptr
== params
) {
1111 for (key
= ptr
-1; isspace(*key
); --key
);
1113 while (isalpha(*key
)) {
1114 /* don't parse backwards off the start of the string */
1115 if (key
== params
) {
1124 for (value
= ptr
+1; isspace(*value
); ++value
);
1125 vlen
= strcspn(value
, delims
);
1126 for (i
= 0; fields
[i
].field
!= NULL
; i
++) {
1127 if (!strncasecmp(fields
[i
].field
, key
, klen
)) {
1128 fields
[i
].value
= apr_pstrndup(pool
, value
, vlen
);
1134 if (fields
[4].value
!= NULL
) {
1135 port
= atoi(fields
[4].value
);
1137 if (fields
[6].value
!= NULL
&&
1138 !strcmp(fields
[6].value
, "CLIENT_FOUND_ROWS")) {
1139 flags
|= CLIENT_FOUND_ROWS
; /* only option we know */
1141 if (fields
[7].value
!= NULL
) {
1142 sql
->fldsz
= atol(fields
[7].value
);
1144 if (fields
[8].value
!= NULL
) {
1145 mysql_options(sql
->conn
, MYSQL_READ_DEFAULT_GROUP
, fields
[8].value
);
1148 #if MYSQL_VERSION_ID >= 50013
1149 /* the MySQL manual says this should be BEFORE mysql_real_connect */
1150 mysql_options(sql
->conn
, MYSQL_OPT_RECONNECT
, &do_reconnect
);
1153 real_conn
= mysql_real_connect(sql
->conn
, fields
[0].value
,
1154 fields
[1].value
, fields
[2].value
,
1155 fields
[3].value
, port
,
1156 fields
[5].value
, flags
);
1158 if(real_conn
== NULL
) {
1160 *error
= apr_pstrdup(pool
, mysql_error(sql
->conn
));
1162 mysql_close(sql
->conn
);
1166 #if MYSQL_VERSION_ID >= 50013
1167 /* Some say this should be AFTER mysql_real_connect */
1168 mysql_options(sql
->conn
, MYSQL_OPT_RECONNECT
, &do_reconnect
);
1174 static apr_status_t
dbd_mysql_close(apr_dbd_t
*handle
)
1176 mysql_close(handle
->conn
);
1180 static apr_status_t
dbd_mysql_check_conn(apr_pool_t
*pool
,
1183 return mysql_ping(handle
->conn
) ? APR_EGENERAL
: APR_SUCCESS
;
1186 static int dbd_mysql_select_db(apr_pool_t
*pool
, apr_dbd_t
* handle
,
1189 return mysql_select_db(handle
->conn
, name
);
1192 static void *dbd_mysql_native(apr_dbd_t
*handle
)
1194 return handle
->conn
;
1197 static int dbd_mysql_num_cols(apr_dbd_results_t
*res
)
1199 if (res
->statement
) {
1200 return mysql_stmt_field_count(res
->statement
);
1203 return mysql_num_fields(res
->res
);
1207 static int dbd_mysql_num_tuples(apr_dbd_results_t
*res
)
1210 if (res
->statement
) {
1211 return (int) mysql_stmt_num_rows(res
->statement
);
1214 return (int) mysql_num_rows(res
->res
);
1222 static apr_status_t
thread_end(void *data
)
1228 static void dbd_mysql_init(apr_pool_t
*pool
)
1231 mysql_thread_init();
1233 /* FIXME: this is a guess; find out what it really does */
1234 apr_pool_cleanup_register(pool
, NULL
, thread_end
, apr_pool_cleanup_null
);
1236 APU_DECLARE_DATA
const apr_dbd_driver_t apr_dbd_mysql_driver
= {
1241 dbd_mysql_check_conn
,
1243 dbd_mysql_select_db
,
1244 dbd_mysql_transaction
,
1245 dbd_mysql_end_transaction
,
1249 dbd_mysql_num_tuples
,
1251 dbd_mysql_get_entry
,
1260 dbd_mysql_transaction_mode_get
,
1261 dbd_mysql_transaction_mode_set
,
1264 dbd_mysql_pvbselect
,